Skip to content

feat: Allow null at comptime - #2877

Open
aleksanderkatan wants to merge 9 commits into
mainfrom
feat/null-legal-at-comptime
Open

feat: Allow null at comptime#2877
aleksanderkatan wants to merge 9 commits into
mainfrom
feat/null-legal-at-comptime

Conversation

@aleksanderkatan

Copy link
Copy Markdown
Member

No description provided.

@aleksanderkatan

Copy link
Copy Markdown
Member Author

@pullfrog review

@github-actions

github-actions Bot commented Aug 19, 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/tinyest@46403f02a58b3dcf4a9fb31dd016781e8eb79743
https://pkg.pr.new/software-mansion/TypeGPU/tinyest-for-wgsl@46403f02a58b3dcf4a9fb31dd016781e8eb79743
https://pkg.pr.new/software-mansion/TypeGPU/typegpu@46403f02a58b3dcf4a9fb31dd016781e8eb79743
https://pkg.pr.new/software-mansion/TypeGPU/unplugin-typegpu@46403f02a58b3dcf4a9fb31dd016781e8eb79743

benchmark
view benchmark

commit
view commit

@github-actions

github-actions Bot commented Aug 19, 2026

Copy link
Copy Markdown

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

🟢 Decreased ➖ Unchanged 🔴 Increased (max 0.08%) ❔ Unknown
0 304 21 0

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

Test tsdown
tgpu_init.ts 263.31 kB ($${\color{green}-3.5\%}$$)
tgpu_initFromDevice.ts 262.77 kB ($${\color{green}-3.7\%}$$)
tgpu_resolve.ts 161.96 kB ($${\color{green}-40.6\%}$$)
tgpu_resolveWithContext.ts 161.90 kB ($${\color{green}-40.7\%}$$)
tgpu_bindGroupLayout.ts 62.32 kB ($${\color{green}-77.2\%}$$)
tgpu_mutableAccessor.ts 57.04 kB ($${\color{green}-79.1\%}$$)
tgpu_accessor.ts 57.04 kB ($${\color{green}-79.1\%}$$)
tgpu_privateVar.ts 55.73 kB ($${\color{green}-79.6\%}$$)
tgpu_workgroupVar.ts 55.73 kB ($${\color{green}-79.6\%}$$)
tgpu_const.ts 55.15 kB ($${\color{green}-79.8\%}$$)
tgpu_lazy.ts 54.95 kB ($${\color{green}-79.9\%}$$)
tgpu_fragmentFn.ts 39.68 kB ($${\color{green}-85.5\%}$$)
tgpu_fn.ts 39.62 kB ($${\color{green}-85.5\%}$$)
tgpu_vertexFn.ts 39.50 kB ($${\color{green}-85.5\%}$$)
tgpu_computeFn.ts 39.20 kB ($${\color{green}-85.6\%}$$)
tgpu_vertexLayout.ts 28.33 kB ($${\color{green}-89.6\%}$$)
tgpu_comptime.ts 15.93 kB ($${\color{green}-94.2\%}$$)
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 19, 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.67, 1.35, 2.96, 4.10, 5.14, 6.80, 15.37, 17.08]
  line [0.56, 1.16, 2.46, 4.13, 4.79, 8.23, 15.54, 16.83]
  line [0.70, 1.35, 2.81, 4.36, 5.16, 7.87, 16.25, 17.41]
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.19, 0.35, 0.47, 0.56, 0.95, 0.81, 0.98, 1.10]
  line [0.19, 0.35, 0.50, 0.60, 0.80, 0.83, 0.99, 1.00]
  line [0.19, 0.32, 0.51, 0.60, 0.80, 0.83, 0.98, 1.04]
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.60, 1.62, 2.44, 4.83, 7.83, 17.11, 37.43, 74.80]
  line [0.64, 1.66, 2.26, 4.31, 7.76, 16.63, 36.38, 74.14]
  line [0.62, 1.63, 2.58, 4.51, 8.12, 16.80, 38.57, 76.02]
Loading

pullfrog[bot]

This comment was marked as outdated.

@aleksanderkatan
aleksanderkatan marked this pull request as ready for review August 19, 2026 15:47
Copilot AI lite review requested due to automatic review settings August 19, 2026 15:47

This comment was marked as outdated.

pullfrog[bot]

This comment was marked as outdated.

@aleksanderkatan
aleksanderkatan force-pushed the feat/null-legal-at-comptime branch from 4715dd2 to 44abdfa Compare August 20, 2026 11:34
@aleksanderkatan
aleksanderkatan changed the base branch from main to chore/drop-acorn August 20, 2026 11:35
@aleksanderkatan
aleksanderkatan force-pushed the feat/null-legal-at-comptime branch from 44abdfa to 60810de Compare August 20, 2026 11:42
pullfrog[bot]

This comment was marked as outdated.

@aleksanderkatan
aleksanderkatan marked this pull request as draft August 24, 2026 11:39
@aleksanderkatan
aleksanderkatan force-pushed the feat/null-legal-at-comptime branch from 60810de to 5eb127c Compare August 24, 2026 12:03
@aleksanderkatan
aleksanderkatan changed the base branch from chore/drop-acorn to main August 24, 2026 13:10
@aleksanderkatan
aleksanderkatan marked this pull request as ready for review August 25, 2026 08:05

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

ℹ️ Minor suggestions — the null-at-comptime feature is sound, well-scoped, and verified green on the rebased head; one still-open docs nitpick below.

Reviewed changes — this re-review covers the branch after a rebase onto main. The vectorOps portmanteau and the chore/drop-acorn base are gone, so the diff is now feature-only (12 files): a clean null-at-comptime change.

  • nullLiteral tinyest node — node code 106 in NodeTypeCatalog, a Null tuple type, and inclusion in the Literal union (nodes.ts).
  • Parsing both backends — the babel NullLiteral() transpiler and the Literal.raw === 'null' path both emit [nullLiteral]; the dualTest in parsers.test.ts exercises acorn and babel.
  • WGSL emissionWgslGenerator._expression renders nullLiteral as snip(null, UnknownData, 'constant', false); since the value is not a string, isKnownAtComptime is true, so ===/!== fold and prune branches/ternaries.
  • Runtime guardresolutionCtx.resolve(null) throws a comptime-specific 'null' is only allowed in comptime checks error instead of silently lowering to 0 (the prior Number(null) behavior).
  • Consumers in locksteptseynit stringifies null, isExpression accepts the node, and the obfuscator passes it through untouched.
  • Tests — parser, tseynit, slot, comptime, tgslFn (incl. runtime negatives), and obfuscation cases. All affected suites pass on the rebased head (parsers 18, typegpu 113, unplugin 25).

The design holds together: a null literal becomes a comptime-known snippet that folds away during branch/ternary pruning, while any path that would emit null into runtime WGSL throws. The previously-raised runtime-path concern (x !== null with a non-foldable operand surfacing a generic "right-hand side of unknown type") was discussed and resolved by the author and reviewer as acceptable. A maintainer has approved.

ℹ️ Docs: branch-pruning list still omits null

packages/typegpu docs apps/typegpu-docs/src/content/docs/apis/utils.mdx (§ conds comptime-known at resolution) enumerates which values enable branch pruning: referenced JS values/operations, slot values, and comptime-returned values. Now that x === null / x !== null folds during pruning (this PR's core capability), a one-line mention of null-safety comparisons would make the feature discoverable to users hitting number | null slots or externals. Worth adding since the whole point of this PR is that null is legal only at comptime.

Pullfrog  | Fix it ➔View workflow run | Using DeepSeek Flash (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.

feat: Make null legal to be used in 'use gpu' functions at comptime

4 participants