From 95e4432a2f0a4ae037a24bb141ac32138a71c67f Mon Sep 17 00:00:00 2001 From: dchaudhari7177 <111210939+dchaudhari7177@users.noreply.github.com> Date: Sat, 3 Oct 2026 15:23:00 +0530 Subject: [PATCH 1/2] fix: Keep generated identifiers off the template-parameter enumerants A module-scope or local declaration named like an address space, access mode or texel format shadows that enumerant, so a later template such as var would refer to the declaration. Reserve them in the namespace so such names get a suffix. They stay valid as struct member names, which shadow nothing. Closes #3065 --- .../typegpu/src/core/resolve/namespace.ts | 4 +- packages/typegpu/src/nameUtils.ts | 60 +++++++++++++++++++ packages/typegpu/tests/namespace.test.ts | 33 ++++++++++ 3 files changed, 95 insertions(+), 2 deletions(-) diff --git a/packages/typegpu/src/core/resolve/namespace.ts b/packages/typegpu/src/core/resolve/namespace.ts index 90fcba08f7..a20f15b6ab 100644 --- a/packages/typegpu/src/core/resolve/namespace.ts +++ b/packages/typegpu/src/core/resolve/namespace.ts @@ -1,5 +1,5 @@ import type { ResolvedSnippet } from '../../data/snippet.ts'; -import { bannedTokens, builtins } from '../../nameUtils.ts'; +import { bannedTokens, builtins, templateEnumerants } from '../../nameUtils.ts'; import { $internal } from '../../shared/symbols.ts'; import { ShelllessRepository } from '../../tgsl/shellless.ts'; import type { TgpuLazy, TgpuSlot } from '../slot/slotTypes.ts'; @@ -36,7 +36,7 @@ class NamespaceImpl implements Namespace { constructor(strategy: 'random' | 'strict') { this[$internal] = { strategy, - takenGlobalIdentifiers: new Set([...bannedTokens, ...builtins]), + takenGlobalIdentifiers: new Set([...bannedTokens, ...builtins, ...templateEnumerants]), shelllessRepo: new ShelllessRepository(), memoizedResolves: new WeakMap(), memoizedLazy: new WeakMap(), diff --git a/packages/typegpu/src/nameUtils.ts b/packages/typegpu/src/nameUtils.ts index cd2c82846b..24e2ba640c 100644 --- a/packages/typegpu/src/nameUtils.ts +++ b/packages/typegpu/src/nameUtils.ts @@ -179,6 +179,66 @@ export const bannedTokens = new Set([ 'storage', ]); +/** + * Predeclared enumerants that appear as template parameters, e.g. `var` or + * `texture_storage_2d`. A module-scope or local declaration with one of these names + * would shadow the enumerant, so a template using it would refer to the declaration instead. + * Unlike `bannedTokens`, these remain valid as struct member names, which never shadow anything. + */ +export const templateEnumerants = new Set([ + // address spaces + 'function', + 'private', + 'workgroup', + 'uniform', + 'storage', + // access modes + 'read', + 'write', + 'read_write', + // texel formats + 'rgba8unorm', + 'rgba8snorm', + 'rgba8uint', + 'rgba8sint', + 'rgba16unorm', + 'rgba16snorm', + 'rgba16uint', + 'rgba16sint', + 'rgba16float', + 'rg8unorm', + 'rg8snorm', + 'rg8uint', + 'rg8sint', + 'rg16unorm', + 'rg16snorm', + 'rg16uint', + 'rg16sint', + 'rg16float', + 'r8unorm', + 'r8snorm', + 'r8uint', + 'r8sint', + 'r16unorm', + 'r16snorm', + 'r16uint', + 'r16sint', + 'r16float', + 'r32uint', + 'r32sint', + 'r32float', + 'rg32uint', + 'rg32sint', + 'rg32float', + 'rgba32uint', + 'rgba32sint', + 'rgba32float', + 'bgra8unorm', + 'rgb10a2unorm', + 'rgb10a2uint', + 'rg11b10ufloat', +]); + export const builtins = new Set([ // constructors 'array', diff --git a/packages/typegpu/tests/namespace.test.ts b/packages/typegpu/tests/namespace.test.ts index 262f4a5908..7bfb9de55b 100644 --- a/packages/typegpu/tests/namespace.test.ts +++ b/packages/typegpu/tests/namespace.test.ts @@ -3,6 +3,39 @@ import { tgpu, d } from 'typegpu'; import { it } from 'typegpu-testing-utility'; describe('tgpu.namespace', () => { + it('does not name a declaration after a template enumerant', ({ root }) => { + const counter = root.createMutable(d.u32).$name('read_write'); + const scale = tgpu.privateVar(d.f32).$name('rgba8unorm'); + + const fn = () => { + 'use gpu'; + const write = counter.$ + 1; + scale.$ = d.f32(write); + }; + + expect(tgpu.resolve([fn])).toMatchInlineSnapshot(` + "@group(0) @binding(0) var read_write_1: u32; + + var rgba8unorm_1: f32; + + fn fn_1() { + let write_1 = (read_write_1 + 1u); + rgba8unorm_1 = f32(write_1); + }" + `); + }); + + it('still accepts a template enumerant as a struct member name', () => { + const Access = d.struct({ read: d.u32, write: d.u32 }); + + expect(tgpu.resolve([Access])).toMatchInlineSnapshot(` + "struct Access { + read: u32, + write: u32, + }" + `); + }); + it('defines direct dependencies only once', () => { const Boid = d.struct({ pos: d.vec3f, From ff31e37b2876cdd2e6d89a76b15b88c3b991e1c0 Mon Sep 17 00:00:00 2001 From: dchaudhari7177 <111210939+dchaudhari7177@users.noreply.github.com> Date: Sun, 4 Oct 2026 10:22:21 +0530 Subject: [PATCH 2/2] fix: Reserve the immediate address space too --- packages/typegpu/src/nameUtils.ts | 1 + packages/typegpu/tests/namespace.test.ts | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/packages/typegpu/src/nameUtils.ts b/packages/typegpu/src/nameUtils.ts index 24e2ba640c..38f58f1d7e 100644 --- a/packages/typegpu/src/nameUtils.ts +++ b/packages/typegpu/src/nameUtils.ts @@ -192,6 +192,7 @@ export const templateEnumerants = new Set([ 'workgroup', 'uniform', 'storage', + 'immediate', // access modes 'read', 'write', diff --git a/packages/typegpu/tests/namespace.test.ts b/packages/typegpu/tests/namespace.test.ts index 7bfb9de55b..43958bc22d 100644 --- a/packages/typegpu/tests/namespace.test.ts +++ b/packages/typegpu/tests/namespace.test.ts @@ -25,6 +25,19 @@ describe('tgpu.namespace', () => { `); }); + it('does not name an immediate after its own address space', () => { + const level = tgpu['~unstable'].immediateVar(d.f32).$name('immediate'); + const fn = tgpu.fn([], d.f32)(() => level.$); + + expect(tgpu.resolve([fn])).toMatchInlineSnapshot(` + "var immediate_1: f32; + + fn fn_1() -> f32 { + return immediate_1; + }" + `); + }); + it('still accepts a template enumerant as a struct member name', () => { const Access = d.struct({ read: d.u32, write: d.u32 });