feat: Allow null at comptime - #2877
Conversation
|
@pullfrog review |
|
pkg.pr.new packages benchmark commit |
Bundle size comparison (
|
| 🟢 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.30 kB ( |
| tgpu_initFromDevice.ts | 262.76 kB ( |
| tgpu_resolve.ts | 161.96 kB ( |
| tgpu_resolveWithContext.ts | 161.90 kB ( |
| tgpu_bindGroupLayout.ts | 62.32 kB ( |
| tgpu_mutableAccessor.ts | 57.04 kB ( |
| tgpu_accessor.ts | 57.04 kB ( |
| tgpu_privateVar.ts | 55.73 kB ( |
| tgpu_workgroupVar.ts | 55.73 kB ( |
| tgpu_const.ts | 55.15 kB ( |
| tgpu_lazy.ts | 54.95 kB ( |
| tgpu_fragmentFn.ts | 39.68 kB ( |
| tgpu_fn.ts | 39.62 kB ( |
| tgpu_vertexFn.ts | 39.50 kB ( |
| tgpu_computeFn.ts | 39.20 kB ( |
| tgpu_vertexLayout.ts | 28.33 kB ( |
| tgpu_comptime.ts | 15.93 kB ( |
| tgpu_unroll.ts | 1.75 kB ( |
| tgpu_slot.ts | 1.70 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.71, 1.46, 3.05, 4.77, 5.02, 8.23, 17.83, 17.51]
line [0.74, 1.51, 2.79, 4.51, 5.98, 8.49, 17.25, 19.22]
line [0.65, 1.36, 3.01, 4.55, 5.25, 9.49, 18.05, 17.17]
---
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.23, 0.36, 0.48, 0.58, 0.78, 0.81, 1.01, 1.10]
line [0.22, 0.35, 0.49, 0.59, 0.84, 0.84, 1.01, 1.14]
line [0.23, 0.38, 0.49, 0.60, 0.80, 0.82, 1.00, 1.11]
---
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.62, 3.11, 4.97, 8.77, 18.71, 41.24, 81.77]
line [0.70, 1.58, 2.36, 4.88, 8.48, 19.83, 40.63, 83.14]
line [0.66, 1.51, 2.77, 5.36, 9.04, 18.60, 40.98, 82.01]
|
4715dd2 to
44abdfa
Compare
44abdfa to
60810de
Compare
60810de to
5eb127c
Compare
There was a problem hiding this comment.
ℹ️ 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.
nullLiteraltinyest node — node code106inNodeTypeCatalog, aNulltuple type, and inclusion in theLiteralunion (nodes.ts).- Parsing both backends — the babel
NullLiteral()transpiler and theLiteral.raw === 'null'path both emit[nullLiteral]; thedualTestinparsers.test.tsexercises acorn and babel. - WGSL emission —
WgslGenerator._expressionrendersnullLiteralassnip(null, UnknownData, 'constant', false); since the value is not a string,isKnownAtComptimeis true, so===/!==fold and prune branches/ternaries. - Runtime guard —
resolutionCtx.resolve(null)throws a comptime-specific'null' is only allowed in comptime checkserror instead of silently lowering to0(the priorNumber(null)behavior). - Consumers in lockstep —
tseynitstringifiesnull,isExpressionaccepts 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,typegpu113, 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.
DeepSeek Flash (free via Pullfrog for OSS) | 𝕏
| if (node.bigint) { | ||
| console.warn('BigInt literals are represented as numbers - loss of precision may occur.'); | ||
| } | ||
| if (node.raw === 'null') { |
There was a problem hiding this comment.
I would put it above the bigint
baf601b to
17cd254
Compare
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — the branch was force-pushed/rebuilt onto current main, and the feature code is byte-identical to the previously fully-reviewed 4cc75c7. The only PR-content change since the prior pullfrog review is a docs update that closes the one open nitpick.
- Docs: null-comparison pruning example —
utils.mdxbranch-pruning section now demonstratespreprocessSlot.$ !== null ? preprocessSlot.$(-5) : -5on aslot<null | ((a: number) => number)>, making null-comparison folding discoverable. This directly addresses the prior review's "branch-pruning list omitsnull" nitpick. The twoslash snippet typechecks and matches the feature's tested behavior.
No code changed in this delta and no new issues. The feature itself remains approved and green.
DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

No description provided.