Skip to content

feat: Simple Object destructuring in GPU functions - #2913

Open
vende11s wants to merge 4 commits into
mainfrom
feat/destructuring-objects
Open

feat: Simple Object destructuring in GPU functions#2913
vende11s wants to merge 4 commits into
mainfrom
feat/destructuring-objects

Conversation

@vende11s

@vende11s vende11s commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator

Adds #2318

Allows simple object destructuring like:

 const { a, b: renamed } = createPair();

or

const pair = Pair({ a: 2, b: 3 });
const { a, b: c } = pair;

which generates:

let destructured_0 = createPair();
let a = destructured_0.a;
let renamed = destructured_0.b;

and

let pair = Pair(2i, 3i);
let a = pair.a;
let c = pair.b;

@github-actions

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

benchmark
view benchmark

commit
view commit

@github-actions

github-actions Bot commented Aug 24, 2026

Copy link
Copy Markdown

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

🟢 Decreased ➖ Unchanged 🔴 Increased (max 1.34%) ❔ Unknown
0 302 23 0

import * as ... in PR vs import * as ... in target (did bundle size increase?):

Test tsdown
common_fullScreenTriangle.ts 42.88 kB ($${\color{red}+1.3\%}$$)
STATIC_common.ts 59.64 kB ($${\color{red}+1.0\%}$$)

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

Test tsdown
tgpu_init.ts 264.40 kB ($${\color{green}-3.5\%}$$)
tgpu_initFromDevice.ts 263.86 kB ($${\color{green}-3.7\%}$$)
tgpu_resolve.ts 163.06 kB ($${\color{green}-40.5\%}$$)
tgpu_resolveWithContext.ts 163.00 kB ($${\color{green}-40.5\%}$$)
tgpu_bindGroupLayout.ts 62.32 kB ($${\color{green}-77.2\%}$$)
tgpu_mutableAccessor.ts 57.61 kB ($${\color{green}-79.0\%}$$)
tgpu_accessor.ts 57.61 kB ($${\color{green}-79.0\%}$$)
tgpu_privateVar.ts 55.73 kB ($${\color{green}-79.7\%}$$)
tgpu_workgroupVar.ts 55.73 kB ($${\color{green}-79.7\%}$$)
tgpu_const.ts 55.15 kB ($${\color{green}-79.9\%}$$)
tgpu_lazy.ts 54.95 kB ($${\color{green}-79.9\%}$$)
tgpu_fragmentFn.ts 40.25 kB ($${\color{green}-85.3\%}$$)
tgpu_fn.ts 40.19 kB ($${\color{green}-85.3\%}$$)
tgpu_vertexFn.ts 40.06 kB ($${\color{green}-85.4\%}$$)
tgpu_computeFn.ts 39.77 kB ($${\color{green}-85.5\%}$$)
tgpu_vertexLayout.ts 28.33 kB ($${\color{green}-89.7\%}$$)
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 24, 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.70, 1.46, 3.18, 4.30, 5.11, 8.22, 17.20, 17.44]
  line [0.65, 1.40, 3.09, 4.04, 4.74, 8.03, 15.37, 16.88]
  line [0.73, 1.36, 2.94, 4.10, 5.37, 8.42, 16.44, 15.99]
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.22, 0.40, 0.52, 0.57, 0.89, 0.85, 1.02, 1.12]
  line [0.26, 0.46, 0.59, 0.63, 0.90, 0.79, 1.03, 1.09]
  line [0.22, 0.36, 0.55, 0.60, 0.94, 0.81, 1.01, 1.10]
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.72, 1.78, 2.60, 5.12, 9.71, 18.43, 41.61, 81.47]
  line [0.82, 1.63, 2.57, 4.74, 8.20, 18.61, 38.67, 76.74]
  line [0.68, 1.70, 2.31, 4.77, 8.63, 18.57, 37.15, 77.91]
Loading

@vende11s
vende11s marked this pull request as ready for review August 26, 2026 16:04
Copilot AI lite review requested due to automatic review settings August 26, 2026 16:04

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 adds support for simple object destructuring inside 'use gpu' functions by extending the Tinyest AST to represent variable bindings as a BindingPattern (identifier vs. destructured object), then updating parsing, WGSL generation, linting, obfuscation, and tests accordingly.

Changes:

  • Extend Tinyest let/const declarations to use BindingPattern and introduce BindingPatternType (with compatibility aliases for existing FuncParameter* naming).
  • Parse simple object destructuring for variable declarations / parameters in tinyest-for-wgsl, and generate corresponding WGSL declarations in WgslGenerator.
  • Update ESLint rule coverage and test snapshots across packages to reflect the new AST shape and supported destructuring subset.

Reviewed changes

Copilot reviewed 18 out of 18 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/unplugin-typegpu/test/use-gpu-directive.test.ts Updates expected Tinyest AST snapshots for let/const bindings.
packages/unplugin-typegpu/test/tgsl-transpiling.test.ts Updates transpilation snapshots to the new binding pattern representation.
packages/unplugin-typegpu/test/parser-options.test.ts Adjusts parser option snapshot to the new BindingPattern form.
packages/unplugin-typegpu/test/obfuscation.test.ts Adds coverage for obfuscating destructured variable declarations + updates snapshots.
packages/unplugin-typegpu/test/aliasing.test.ts Updates alias-gathering snapshots to new binding representation.
packages/unplugin-typegpu/src/core/obfuscate.ts Refactors binding obfuscation to support identifier vs destructured object patterns.
packages/typegpu/tests/tgsl/wgslGenerator.test.ts Adds WGSL generator tests for object destructuring (const/let, single-eval, name conflicts).
packages/typegpu/tests/internal/tseynit.test.ts Adds stringify coverage for destructured binding declarations.
packages/typegpu/tests/internal/metadata.test.ts Updates metadata normalization tests to handle legacy vs new binding forms.
packages/typegpu/src/tgsl/wgslGenerator.ts Implements object destructuring statement lowering and updates let/const handling to BindingPattern.
packages/typegpu/src/shared/tseynit.ts Adds BindingPattern stringification for debugging/printing.
packages/typegpu/src/shared/normalizeMetadata.ts Normalizes legacy metadata by converting string bindings into identifier BindingPatterns.
packages/typegpu/src/resolutionCtx.ts Updates parameter pattern checks to use BindingPatternType.
packages/tinyest/src/nodes.ts Introduces BindingPatternType/BindingPattern, updates Let/Const to use them, keeps compatibility aliases.
packages/tinyest-for-wgsl/tests/parsers.test.ts Adds parser coverage for destructured declarations and unsupported patterns + snapshot updates.
packages/tinyest-for-wgsl/src/parsers.ts Adds parsing for BindingPattern (identifier/object destructuring), rejects unsupported destructuring contexts.
packages/eslint-plugin/tests/rules/noUnsupportedSyntax.test.ts Expands valid/invalid coverage for supported destructuring + unsupported binding patterns / loop headers.
packages/eslint-plugin/src/rules/noUnsupportedSyntax.ts Allows supported simple object binding patterns, adds checks for destructuring assignment and loop-header destructuring.

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

Comment on lines +1349 to +1352
return {
code: declarations.map((declaration) => declaration.code).join('\n'),
definesInNearestScope: true,
};
Comment on lines +1746 to +1749
const loopBinding = loopVar[1];
if (loopBinding.type !== tinyest.BindingPatternType.identifier) {
throw new WgslTypeError('Destructuring in for..of loops is not supported yet.');
}

@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 outstanding changes needed.

Reviewed changes

  • tinyest Let/Const binding overhaul — The 2nd tuple element changed from a string to a structured BindingPattern (identifier / destructuredObject); FuncParameter/FuncParameterType kept as aliases of the new names for backward source compatibility, and every consumer of the shared node shape was updated in lockstep.
  • TGSL parser (tinyest-for-wgsl) — New parseBindingPattern (shared by variable declarations and function parameters) replaces the previous per-parameter inline mapping, and now hard-rejects nested/computed/default/rest/array patterns, destructuring assignments, and destructuring in for-loop init / for...of headers with clear errors.
  • WGSL lowering (wgslGenerator)_objectDestructuringStatement lowers a destructured const/let into an internal temporary (guaranteeing single evaluation of the source expression) plus one const/let per alias; temporary names are uniquified via the existing makeUniqueIdentifier machinery, and mutable destructured let correctly lowers to var. The GLSL generator inherits the same lowering.
  • Metadata migration (normalizeMetadata)normalizeLegacyBindings recursively rewrites legacy v1/v2 metadata where a let/const binding was a plain string into the new identifier BindingPattern shape, and is idempotent on the new object form; the internal metadata tests exercise real end-to-end resolve on legacy-shaped metadata as well as the new format.
  • ESLint rule (noUnsupportedSyntax) — Now allows simple object-pattern variable declarations and destructured parameters while rejecting everything else; its support predicate matches what the parser accepts, so lint and transpile stay in agreement.
  • Obfuscation (obfuscate.ts)obfuscateBindingPattern renames identifier bindings and destructured aliases while leaving property names intact (they must survive into the generated member access).
  • Tests across the parser, generator, metadata migration, obfuscation, and eslint rule layers, including single-evaluation, name-collision, and mutation cases.

⚠️ Pre-existing silent-drop now fails loudly (improvement, but a behavior change)

On the old code path, destructured function-parameter sub-properties that weren't the simple key: alias shape were silently dropped (props.flatMap((prop) => condition ? [entry] : [])), which could route an argument into the wrong struct field — a correctness bug. The new parseBindingPattern throws for these. That hard error is strictly better and the eslint rule now reports it earlier with a helpful message, but it is a behavioral change for any code that previously transpiled-but-mishandled such params — worth a line in the changelog/release notes.

I verified the changed packages independently: all targeted unit tests pass (tinyest-for-wgsl parsers, unplugin obfuscation, typegpu wgslGenerator/metadata/tseynit, eslint noUnsupportedSyntax) and the core typegpu typecheck (pnpm --filter typegpu test:types) is clean.

ℹ️ Nitpicks

  • wgslGenerator.#destructuringIndex is an instance-global counter that is never reset, so temporary names are numbered monotonically across every function in a resolve rather than per function. Harmless (each name is further uniquified in its own scope) but slightly inconsistent with how other names are scoped.
  • The generated temporary for a non-identifier destructing source is emitted as a mutable let (struct values take the deferred var-type path even though the temp is never reassigned). Cosmetic; const could be used, but there's no behavioral impact.

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

2 participants