Skip to content

impr: Strict type checking on logical and relational binary expressions operands#2647

Open
cieplypolar wants to merge 41 commits into
mainfrom
fix/unify-test-in-ternary
Open

impr: Strict type checking on logical and relational binary expressions operands#2647
cieplypolar wants to merge 41 commits into
mainfrom
fix/unify-test-in-ternary

Conversation

@cieplypolar

@cieplypolar cieplypolar commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator

Part of the #2645.

Addressed: <, >, >=, <=, ===, !==, || and &&.

Also, forced ternary operator to check if condition is boolean.

Closes #2702.

pullfrog Bot and others added 12 commits June 15, 2026 19:16
- 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
@github-actions

github-actions Bot commented Jun 23, 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/tgpu-gen@29819e694f4d5ec074dcb44049d362f70df897a5
https://pkg.pr.new/software-mansion/TypeGPU/tinyest-for-wgsl@29819e694f4d5ec074dcb44049d362f70df897a5
https://pkg.pr.new/software-mansion/TypeGPU/typegpu@29819e694f4d5ec074dcb44049d362f70df897a5
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/cli@29819e694f4d5ec074dcb44049d362f70df897a5
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/color@29819e694f4d5ec074dcb44049d362f70df897a5
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/gl@29819e694f4d5ec074dcb44049d362f70df897a5
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/noise@29819e694f4d5ec074dcb44049d362f70df897a5
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/radiance-cascades@29819e694f4d5ec074dcb44049d362f70df897a5
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/react@29819e694f4d5ec074dcb44049d362f70df897a5
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/sdf@29819e694f4d5ec074dcb44049d362f70df897a5
https://pkg.pr.new/software-mansion/TypeGPU/@typegpu/three@29819e694f4d5ec074dcb44049d362f70df897a5
https://pkg.pr.new/software-mansion/TypeGPU/unplugin-typegpu@29819e694f4d5ec074dcb44049d362f70df897a5

benchmark
view benchmark

commit
view commit

@github-actions

github-actions Bot commented Jun 23, 2026

Copy link
Copy Markdown

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

🟢 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 ($${\color{green}-3.2\%}$$)
tgpu_initFromDevice.ts 257.05 kB ($${\color{green}-3.4\%}$$)
tgpu_resolve.ts 163.03 kB ($${\color{green}-38.7\%}$$)
tgpu_resolveWithContext.ts 162.97 kB ($${\color{green}-38.8\%}$$)
tgpu_bindGroupLayout.ts 68.96 kB ($${\color{green}-74.1\%}$$)
tgpu_mutableAccessor.ts 65.97 kB ($${\color{green}-75.2\%}$$)
tgpu_accessor.ts 65.96 kB ($${\color{green}-75.2\%}$$)
tgpu_privateVar.ts 65.31 kB ($${\color{green}-75.5\%}$$)
tgpu_workgroupVar.ts 65.30 kB ($${\color{green}-75.5\%}$$)
tgpu_const.ts 64.55 kB ($${\color{green}-75.7\%}$$)
tgpu_fn.ts 38.14 kB ($${\color{green}-85.7\%}$$)
tgpu_fragmentFn.ts 38.13 kB ($${\color{green}-85.7\%}$$)
tgpu_vertexFn.ts 37.95 kB ($${\color{green}-85.7\%}$$)
tgpu_computeFn.ts 37.66 kB ($${\color{green}-85.8\%}$$)
tgpu_vertexLayout.ts 26.79 kB ($${\color{green}-89.9\%}$$)
tgpu_comptime.ts 14.48 kB ($${\color{green}-94.6\%}$$)
tgpu_unroll.ts 1.66 kB ($${\color{green}-99.4\%}$$)
tgpu_slot.ts 1.54 kB ($${\color{green}-99.4\%}$$)
tgpu_lazy.ts 1.19 kB ($${\color{green}-99.6\%}$$)

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 Jun 23, 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.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]
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.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]
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.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]
Loading

@cieplypolar cieplypolar changed the base branch from main to fix/possible-side-effects-flow July 7, 2026 12:14
@cieplypolar cieplypolar marked this pull request as ready for review July 7, 2026 12:14

@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.

Reviewed changes — strict operand type checking for relational and logical binary expressions, plus forced boolean conversion for ternary conditions.

  • Added binaryRelationalOpToStdMap and runtime type checks in wgslGenerator.ts for <, <=, >, >=, ===, !==, &&, and ||.
  • Refactored comptime relational comparisons to use the same std-function suggestion map.
  • Forced ternary conditions to bool using tryConvertSnippet.
  • Added binaryLogicalOps.test.ts and moved short-circuit evaluation tests from wgslGenerator.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`.

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

Comment thread packages/typegpu/src/tgsl/wgslGenerator.ts Outdated
Comment thread packages/typegpu/src/tgsl/wgslGenerator.ts Outdated
@cieplypolar cieplypolar force-pushed the fix/unify-test-in-ternary branch from 7f8469e to 36dd367 Compare July 9, 2026 11:54

@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.

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.logicalExpr from binary/assignment expressions to give &&/|| their own short-circuit and runtime paths.
  • Removed &&/|| from parenthesizedOps since logical expression parentheses are now emitted explicitly.
  • Added runtime type validation for relational operators and for &&/||, though the &&/|| check runs after converting operands to bool.
  • Added ref operands and reorganized tests into inside condition and outside condition groups, moving the original short-circuit suite out of wgslGenerator.test.ts.

A detailed note on the strictness gap is inline on wgslGenerator.ts.

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

Comment thread packages/typegpu/src/tgsl/wgslGenerator.ts
@cieplypolar cieplypolar changed the base branch from fix/possible-side-effects-flow to main July 14, 2026 18:13
@cieplypolar cieplypolar changed the base branch from main to fix/possible-side-effects-flow July 14, 2026 18:15
@cieplypolar cieplypolar force-pushed the fix/unify-test-in-ternary branch from c7d643f to 728cacc Compare July 14, 2026 18:18

@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.

Important

The runtime &&/|| strictness issue from the previous review is still present: non-boolean operands are implicitly converted to bool before the boolean check, so numeric operands are accepted rather than rejected. The existing inline thread on wgslGenerator.ts still tracks this.

New commits since the last review refactor resolveSnippet to preserve possibleSideEffects, update accessor.ts for isGPUCallable snippets, add a zero-argument dualFn test, and add a for-loop variable shadowing test. All 215 affected tests pass.

Pullfrog  | Fix it ➔View workflow run | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏

@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.

Important

The runtime &&/|| strictness issue from previous reviews is still present in this batch: non-boolean operands are converted to bool before the boolean check, so numeric operands silently compile. The existing inline thread on wgslGenerator.ts continues to track this.

New commits (c7d643f..728cacc) — all 215 affected tests pass. The bulk of the change is the possibleSideEffects propagation: snip, rawCodeSnippet, RefOperator, derefSnippet, variable declarations, literals, unary/binary expressions, and accessor.ts now carry side-effect information.

One follow-up below on a missing possibleSideEffects propagation in the runtime logical-expression path.

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

Comment thread packages/typegpu/src/tgsl/wgslGenerator.ts Outdated

@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.

✅ 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 castToBool handling in comptime &&/|| short-circuit — returns a boolean constant when the surrounding context expects bool, 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 bool conversion.
  • Propagated possibleSideEffects from the original operands into the runtime &&/|| snippet.
  • Added &&/|| side-effect tests for pure and impure runtime cases, plus minor oxlint-comment test polish.

Pullfrog  | View workflow run | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏

@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.

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 redundant op === '&&' || op === '||' guard inside the NODE.logicalExpr branch.
  • Updated the ref logical-operator tests to return !!(a.$ && b.$) and !!(a.$ || b.$).

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

Comment thread packages/typegpu/src/tgsl/wgslGenerator.ts
Base automatically changed from fix/possible-side-effects-flow to main July 15, 2026 11:03
@cieplypolar cieplypolar force-pushed the fix/unify-test-in-ternary branch from 8beca68 to a6688d2 Compare July 15, 2026 11:13
Copilot AI review requested due to automatic review settings July 15, 2026 11:14

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@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.

Test body

Pullfrog  | Fix it ➔View workflow run | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏

@pullfrog

pullfrog Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Important

The runtime &&/|| strictness gap from previous reviews is still present.

Reviewed changes — the latest commit reorders the runtime logical-operator path and cleans up wgslGenerator.test.ts.

  • Reordered lhsStr/rhsStr resolution to run after the boolean-operand validation in NODE.logicalExpr.
  • Removed the for-loop variable-shadowing test and the moved short-circuit suite from wgslGenerator.test.ts.

⚠️ Runtime &&/|| still accepts implicitly convertible non-boolean operands

The boolean check at packages/typegpu/src/tgsl/wgslGenerator.ts:390 still inspects convLhs.dataType and convRhs.dataType after convertToCommonType(this.ctx, [lhsExpr, rhsExpr], [bool]). Because convertToCommonType performs implicit primitive casts, operands that are convertible to bool (e.g., two u32 values) pass validation instead of being rejected.

I verified this locally by adding a targeted test: return !!(x && y) with d.u32 inputs resolves successfully and emits the "Implicit conversions from [x: u32, y: u32] to bool are supported, but not recommended" warning instead of throwing. The same applies to ||.

The existing test that expects a throw uses mixed operand types (u32 + Boid), where Boid cannot be converted to bool; this masks the gap.

Required outcome

  • Reject runtime &&/|| unless both operands are already boolean before any implicit bool conversion is attempted.
  • Add regression tests for pairs where both operands are non-boolean but implicitly convertible to bool (e.g., u32 && u32, f32 || i32).

Suggested approach

Validate the original operand types (lhsExpr.dataType/rhsExpr.dataType, dereferencing pointers if necessary) before calling convertToCommonType, or use an explicit-only conversion path for this branch.

Pullfrog  | View workflow run | via Pullfrog | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏

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.

[RFC] impr: && and || operators behavior

3 participants