Skip to content

fix: Runtime ternaries should require non-alias arguments - #2834

Open
iwoplaza wants to merge 1 commit into
mainfrom
fix/runtime-ternaries-should-require-non-alias-args
Open

fix: Runtime ternaries should require non-alias arguments#2834
iwoplaza wants to merge 1 commit into
mainfrom
fix/runtime-ternaries-should-require-non-alias-args

Conversation

@iwoplaza

@iwoplaza iwoplaza commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

No description provided.

@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown

pkg.pr.new

packages
Ready to be installed by your favorite package manager ⬇️

https://pkg.pr.new/software-mansion/TypeGPU/eslint-plugin-typegpu@3e83c9f2dd0595d11797ec8de603d684812dc352
https://pkg.pr.new/software-mansion/TypeGPU/tgpu-gen@3e83c9f2dd0595d11797ec8de603d684812dc352
https://pkg.pr.new/software-mansion/TypeGPU/tinyest-for-wgsl@3e83c9f2dd0595d11797ec8de603d684812dc352
https://pkg.pr.new/software-mansion/TypeGPU/typegpu@3e83c9f2dd0595d11797ec8de603d684812dc352
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/cli@3e83c9f2dd0595d11797ec8de603d684812dc352
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/color@3e83c9f2dd0595d11797ec8de603d684812dc352
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/gl@3e83c9f2dd0595d11797ec8de603d684812dc352
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/noise@3e83c9f2dd0595d11797ec8de603d684812dc352
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/radiance-cascades@3e83c9f2dd0595d11797ec8de603d684812dc352
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/react@3e83c9f2dd0595d11797ec8de603d684812dc352
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/sdf@3e83c9f2dd0595d11797ec8de603d684812dc352
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/three@3e83c9f2dd0595d11797ec8de603d684812dc352
https://pkg.pr.new/software-mansion/TypeGPU/unplugin-typegpu@3e83c9f2dd0595d11797ec8de603d684812dc352

benchmark
view benchmark

commit
view commit

@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown

Bundle size comparison (import * as ... in PR vs import * as ... in target):

🟢 Decreased ➖ Unchanged 🔴 Increased (max 0.02%) ❔ Unknown
0 303 21 0

import { ... } in PR vs import * as ... in PR (is the library tree-Shakeable?):

Test tsdown
tgpu_init.ts 270.74 kB ($${\color{green}-3.3\%}$$)
tgpu_initFromDevice.ts 270.19 kB ($${\color{green}-3.5\%}$$)
tgpu_resolve.ts 170.98 kB ($${\color{green}-38.9\%}$$)
tgpu_resolveWithContext.ts 170.92 kB ($${\color{green}-39.0\%}$$)
tgpu_bindGroupLayout.ts 73.93 kB ($${\color{green}-73.6\%}$$)
tgpu_mutableAccessor.ts 68.66 kB ($${\color{green}-75.5\%}$$)
tgpu_accessor.ts 68.66 kB ($${\color{green}-75.5\%}$$)
tgpu_privateVar.ts 67.35 kB ($${\color{green}-75.9\%}$$)
tgpu_workgroupVar.ts 67.35 kB ($${\color{green}-75.9\%}$$)
tgpu_const.ts 66.77 kB ($${\color{green}-76.2\%}$$)
tgpu_lazy.ts 66.56 kB ($${\color{green}-76.2\%}$$)
tgpu_fragmentFn.ts 38.92 kB ($${\color{green}-86.1\%}$$)
tgpu_fn.ts 38.87 kB ($${\color{green}-86.1\%}$$)
tgpu_vertexFn.ts 38.74 kB ($${\color{green}-86.2\%}$$)
tgpu_computeFn.ts 38.44 kB ($${\color{green}-86.3\%}$$)
tgpu_vertexLayout.ts 27.57 kB ($${\color{green}-90.2\%}$$)
tgpu_comptime.ts 15.18 kB ($${\color{green}-94.6\%}$$)
tgpu_unroll.ts 1.75 kB ($${\color{green}-99.4\%}$$)
tgpu_slot.ts 1.70 kB ($${\color{green}-99.4\%}$$)

If you wish to run a comparison for other, slower bundlers, run the 'Tree-shake test' from the GitHub Actions menu.

@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown

Resolution Time Benchmark

---
config:
  themeVariables:
    xyChart:
      plotColorPalette: "#E63946, #3B82F6, #059669"
---
xychart
  title "Random Branching (🔴 PR | 🔵 main | 🟢 release)"
  x-axis "max depth" [1, 2, 3, 4, 5, 6, 7, 8]
  y-axis "time (ms)"
  line [0.80, 1.58, 3.70, 5.94, 6.57, 12.15, 20.62, 20.00]
  line [0.80, 1.68, 3.63, 6.10, 6.27, 10.70, 18.66, 21.16]
  line [0.82, 1.68, 3.71, 5.76, 6.36, 10.65, 18.49, 22.19]
Loading
---
config:
  themeVariables:
    xyChart:
      plotColorPalette: "#E63946, #3B82F6, #059669"
---
xychart
  title "Linear Recursion (🔴 PR | 🔵 main | 🟢 release)"
  x-axis "max depth" [1, 2, 3, 4, 5, 6, 7, 8]
  y-axis "time (ms)"
  line [0.27, 0.45, 0.59, 0.75, 0.93, 1.01, 1.18, 1.34]
  line [0.32, 0.50, 0.64, 0.75, 1.04, 1.03, 1.18, 1.32]
  line [0.33, 0.48, 0.64, 0.76, 1.02, 1.02, 1.26, 1.43]
Loading
---
config:
  themeVariables:
    xyChart:
      plotColorPalette: "#E63946, #3B82F6, #059669"
---
xychart
  title "Full Tree (🔴 PR | 🔵 main | 🟢 release)"
  x-axis "max depth" [1, 2, 3, 4, 5, 6, 7, 8]
  y-axis "time (ms)"
  line [0.70, 1.85, 3.39, 6.30, 11.02, 21.95, 48.41, 98.34]
  line [0.88, 1.90, 3.93, 5.59, 10.34, 21.70, 47.74, 94.65]
  line [0.79, 1.89, 3.96, 5.43, 10.95, 22.85, 49.16, 100.02]
Loading

@iwoplaza
iwoplaza force-pushed the fix/runtime-ternaries-should-require-non-alias-args branch from 7a07fe6 to 3e83c9f Compare August 11, 2026 20:14
@iwoplaza iwoplaza linked an issue Aug 11, 2026 that may be closed by this pull request
@iwoplaza
iwoplaza marked this pull request as ready for review August 11, 2026 20:35
Copilot AI lite review requested due to automatic review settings August 11, 2026 20:35

@cieplypolar cieplypolar left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shipit:

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR tightens TGSL runtime ternary handling to prevent JS-vs-WGSL semantic mismatches by rejecting runtime ternaries whose branches are non-ephemeral aliases (references), and adds a regression test to lock the behavior in.

Changes:

  • Add a new test asserting that runtime ternaries selecting between struct field aliases throw during resolution.
  • Extend the WGSL generator’s runtime-ternary validation to reject alias branches unless the type is “naturally ephemeral” (scalar-like).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
packages/typegpu/tests/tgsl/ternaryOperator.test.ts Adds coverage for rejecting runtime ternaries that would alias in JS but copy in WGSL.
packages/typegpu/src/tgsl/wgslGenerator.ts Adds alias-branch invalidation logic for runtime ternary lowering to select(...).

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +912 to 916
(isAlias(consequent) && !wgsl.isNaturallyEphemeral(consequent.dataType)) ||
(isAlias(alternative) && !wgsl.isNaturallyEphemeral(alternative.dataType))
) {
throw new Error(
`Ternary operator '${stringifyNode(expression)}' is invalid. For more complex branching, please use 'std.select' or if/else statements.`,

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ The fix is correct, well-scoped, and covered by a genuine regression test. One minor suggestion inline about the error guidance.

Reviewed changes

  • wgslGenerator.ts — runtime ternary guard extended to non-ephemeral aliases: a runtime ternary now throws when either branch is an alias whose data type isn't naturally ephemeral in JS (i.e. vectors/structs/arrays), preserving JS reference semantics that select() would silently downgrade to a copy. Scalar aliases remain allowed via the isNaturallyEphemeral carve-out.
  • ternaryOperator.test.ts — regression test: runtimeBool ? boid.pos : boid.vel (both branches struct-member aliases of vec3f) is asserted to throw. I verified this test genuinely pins the fix — it fails with the guard removed and passes with it.

The isAlias(x) && !isNaturallyEphemeral(x.dataType) idiom matches existing precedents (generationHelpers.ts:118, ref.ts:67, wgslGenerator.ts:522), and no new imports are required. I ran the full typegpu suite (2311 tests), the docs example snapshot tests, and the gl/three/sort/noise/react suites — all pass, so no existing code regressed.

Technical details
# Runtime ternaries with vector-alias branches

## Confirmed behavior
- Guard fires only when the condition is runtime (`isKnownAtComptime(test)` is false) and the branch stays an alias after `convertToCommonType`. Existing scalar-member/uniform ternaries (u32) bypass it via `isNaturallyEphemeral`.
- `validSelectBranchTypes` (`std/boolean.ts:406`) limits branches to scalars + vectors, so the non-ephemeral cases blocked here are exactly vector aliases.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

Comment on lines +912 to +913
(isAlias(consequent) && !wgsl.isNaturallyEphemeral(consequent.dataType)) ||
(isAlias(alternative) && !wgsl.isNaturallyEphemeral(alternative.dataType))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When this new guard fires for a vector alias, the shared error message advises std.select or if/else — but neither restores the JS reference semantics this check is protecting: std.select(boid.pos, boid.vel, cond) (whose codegenImpl in std/boolean.ts has no alias guard) reproduces the exact reference-vs-copy mismatch, and an if/else on the JS side still aliases. The test comments document the real remedy (d.vec3f(boid.pos)). Consider adding a copy hint to the message for this case (like the ArrayExpression error does), or applying the same guard to std.select.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: Runtime ternaries should require non-alias arguments

3 participants