impr: Strict type checking on logical and relational binary expressions operands#2647
impr: Strict type checking on logical and relational binary expressions operands#2647cieplypolar wants to merge 41 commits into
Conversation
- Mark function arguments as side-effect-free - Mark tgpu.const reads as side-effect-free - Propagate operand side-effects in pure binary operators - Add side-effect tracking to dualImpl (matching callableSchema) - Mark buffer reads (uniform/readonly/mutable) as side-effect-free - Add comprehensive ternary runtime tests from issue #2587 - Update existing ternary test for runtime select() generation
- Mark function arguments as side-effect-free - Mark tgpu.const reads as side-effect-free - Propagate operand side-effects in pure binary operators - Add side-effect tracking to dualImpl (matching callableSchema) - Mark buffer reads (uniform/readonly/mutable) as side-effect-free - Add comprehensive ternary runtime tests from issue #2587 - Update existing ternary test for runtime select() generation
… discard sideEffects
|
pkg.pr.new packages benchmark commit |
Bundle size comparison (
|
| 🟢 Decreased | ➖ Unchanged | 🔴 Increased (max 0.37%) | ❔ Unknown |
|---|---|---|---|
| 0 | 300 | 21 | 0 |
import { ... } in PR vs import * as ... in PR (is the library tree-Shakeable?):
| Test | tsdown |
|---|---|
| tgpu_init.ts | 257.56 kB ( |
| tgpu_initFromDevice.ts | 257.05 kB ( |
| tgpu_resolve.ts | 163.03 kB ( |
| tgpu_resolveWithContext.ts | 162.97 kB ( |
| tgpu_bindGroupLayout.ts | 68.96 kB ( |
| tgpu_mutableAccessor.ts | 65.97 kB ( |
| tgpu_accessor.ts | 65.96 kB ( |
| tgpu_privateVar.ts | 65.31 kB ( |
| tgpu_workgroupVar.ts | 65.30 kB ( |
| tgpu_const.ts | 64.55 kB ( |
| tgpu_fn.ts | 38.14 kB ( |
| tgpu_fragmentFn.ts | 38.13 kB ( |
| tgpu_vertexFn.ts | 37.95 kB ( |
| tgpu_computeFn.ts | 37.66 kB ( |
| tgpu_vertexLayout.ts | 26.79 kB ( |
| tgpu_comptime.ts | 14.48 kB ( |
| tgpu_unroll.ts | 1.66 kB ( |
| tgpu_slot.ts | 1.54 kB ( |
| tgpu_lazy.ts | 1.19 kB ( |
If you wish to run a comparison for other, slower bundlers, run the 'Tree-shake test' from the GitHub Actions menu.
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.69, 1.32, 2.43, 3.60, 3.72, 6.23, 13.47, 14.31]
line [0.52, 1.02, 2.26, 3.30, 4.06, 6.20, 11.92, 13.70]
line [0.57, 1.15, 2.37, 3.68, 4.24, 6.72, 12.51, 14.62]
---
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.18, 0.31, 0.44, 0.54, 0.71, 0.74, 0.89, 1.13]
line [0.18, 0.30, 0.37, 0.42, 0.60, 0.61, 0.75, 0.83]
line [0.18, 0.34, 0.45, 0.51, 0.69, 0.68, 0.81, 0.90]
---
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.61, 1.41, 2.17, 4.19, 8.34, 16.23, 30.44, 62.16]
line [0.65, 1.32, 2.35, 3.91, 7.44, 15.52, 30.32, 62.62]
line [0.57, 1.33, 2.65, 3.85, 7.05, 15.75, 32.14, 64.38]
|
There was a problem hiding this comment.
Reviewed changes — strict operand type checking for relational and logical binary expressions, plus forced boolean conversion for ternary conditions.
- Added
binaryRelationalOpToStdMapand runtime type checks inwgslGenerator.tsfor<,<=,>,>=,===,!==,&&, and||. - Refactored comptime relational comparisons to use the same std-function suggestion map.
- Forced ternary conditions to
boolusingtryConvertSnippet. - Added
binaryLogicalOps.test.tsand moved short-circuit evaluation tests fromwgslGenerator.test.ts. - Added ternary non-bool condition failure test.
Important
Two concrete issues to address before merging: an unreachable error branch in the new ternary handling, and a typo in the comptime vector-comparison suggestion message.
⚠️ Unreachable ternary error branch
tryConvertSnippet either returns a Snippet or throws a WgslTypeError; it never returns a falsy value. The added if (!convertedTest) branch is dead code, so the custom "Cannot convert condition to bool" message is never shown.
Technical details
# Unreachable ternary error branch
## Affected sites
- `packages/typegpu/src/tgsl/wgslGenerator.ts:920` — `if (!convertedTest)` is always false.
## Required outcome
- Remove the unreachable branch, or replace `tryConvertSnippet` with a conversion helper that returns `null`/`undefined` on failure so the custom error message can be surfaced.
## Suggested approach
Drop the `if (!convertedTest)` guard and rely on `tryConvertSnippet`'s own error. If the custom message is required, wrap the conversion in a `try/catch` and rethrow with the desired text.⚠️ Typo in comptime vector suggestion message
The comptime path appends an extra single quote to the suggested std-function name. The output reads use 'std.ge''. instead of use 'std.ge'..
Technical details
# Typo in comptime vector suggestion message
## Affected sites
- `packages/typegpu/src/tgsl/wgslGenerator.ts:412` — template ends with `''.` instead of `'.`.
- `packages/typegpu/tests/tgsl/binaryLogicalOps.test.ts:271` — inline snapshot matches the typo.
## Required outcome
- Fix the trailing quote in the error template and update the matching inline snapshot.
## Suggested approach
Change `` ` For component-wise comparison, use 'std.${stdBinaryRelationalOp}''.` `` to `` ` For component-wise comparison, use 'std.${stdBinaryRelationalOp}'.` `` in `wgslGenerator.ts`, then update the snapshot in `binaryLogicalOps.test.ts`.Kimi K2 (free via Pullfrog for OSS) | 𝕏
7f8469e to
36dd367
Compare
There was a problem hiding this comment.
Important
The new runtime path for &&/|| implicitly casts non-boolean operands (e.g. numeric values) to bool, so non-boolean operands are accepted instead of rejected.
Reviewed changes — the latest commit refactors logical-expression handling, reorganizes short-circuit tests, and adds ref/outside-condition coverage.
- Split
NODE.logicalExprfrom binary/assignment expressions to give&&/||their own short-circuit and runtime paths. - Removed
&&/||fromparenthesizedOpssince logical expression parentheses are now emitted explicitly. - Added runtime type validation for relational operators and for
&&/||, though the&&/||check runs after converting operands tobool. - Added
refoperands and reorganized tests intoinside conditionandoutside conditiongroups, moving the original short-circuit suite out ofwgslGenerator.test.ts.
A detailed note on the strictness gap is inline on wgslGenerator.ts.
Kimi K2 (free via Pullfrog for OSS) | 𝕏
c7d643f to
728cacc
Compare
There was a problem hiding this comment.
Important
The runtime
&&/||strictness issue from the previous review is still present: non-boolean operands are implicitly converted toboolbefore the boolean check, so numeric operands are accepted rather than rejected. The existing inline thread onwgslGenerator.tsstill tracks this.New commits since the last review refactor
resolveSnippetto preservepossibleSideEffects, updateaccessor.tsforisGPUCallablesnippets, add a zero-argumentdualFntest, and add a for-loop variable shadowing test. All 215 affected tests pass.
Kimi K2 (free via Pullfrog for OSS) | 𝕏
There was a problem hiding this comment.
Important
The runtime
&&/||strictness issue from previous reviews is still present in this batch: non-boolean operands are converted toboolbefore the boolean check, so numeric operands silently compile. The existing inline thread onwgslGenerator.tscontinues to track this.New commits (
c7d643f..728cacc) — all 215 affected tests pass. The bulk of the change is thepossibleSideEffectspropagation:snip,rawCodeSnippet,RefOperator,derefSnippet, variable declarations, literals, unary/binary expressions, andaccessor.tsnow carry side-effect information.One follow-up below on a missing
possibleSideEffectspropagation in the runtime logical-expression path.
Kimi K2 (free via Pullfrog for OSS) | 𝕏
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — the two new commits refine the comptime &&/|| short-circuit path and wire possibleSideEffects through the runtime logical-operator snippet.
- Added
castToBoolhandling in comptime&&/||short-circuit — returns a boolean constant when the surrounding context expectsbool, otherwise preserves the original operand value. - Returned the raw right-hand snippet in comptime short-circuit paths — keeps the operand's type and side-effect metadata instead of forcing a
boolconversion. - Propagated
possibleSideEffectsfrom the original operands into the runtime&&/||snippet. - Added
&&/||side-effect tests for pure and impure runtime cases, plus minor oxlint-comment test polish.
Kimi K2 (free via Pullfrog for OSS) | 𝕏
There was a problem hiding this comment.
Important
The latest commit does not resolve the runtime &&/|| strictness gap — numeric operands are still accepted because the boolean check runs after implicit conversion to bool.
Reviewed changes — the new commit simplifies the logical-operator type check and updates the ref test snapshots.
- Simplified the runtime
&&/||validation by removing the redundantop === '&&' || op === '||'guard inside theNODE.logicalExprbranch. - Updated the
reflogical-operator tests to return!!(a.$ && b.$)and!!(a.$ || b.$).
Kimi K2 (free via Pullfrog for OSS) | 𝕏
8beca68 to
a6688d2
Compare
There was a problem hiding this comment.
Test body
Kimi K2 (free via Pullfrog for OSS) | 𝕏
|
Important The runtime Reviewed changes — the latest commit reorders the runtime logical-operator path and cleans up
|

Part of the #2645.
Addressed: <, >, >=, <=, ===, !==, || and &&.
Also, forced ternary operator to check if condition is boolean.
Closes #2702.