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..38f58f1d7e 100644 --- a/packages/typegpu/src/nameUtils.ts +++ b/packages/typegpu/src/nameUtils.ts @@ -179,6 +179,67 @@ 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', + 'immediate', + // 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..43958bc22d 100644 --- a/packages/typegpu/tests/namespace.test.ts +++ b/packages/typegpu/tests/namespace.test.ts @@ -3,6 +3,52 @@ 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('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 }); + + expect(tgpu.resolve([Access])).toMatchInlineSnapshot(` + "struct Access { + read: u32, + write: u32, + }" + `); + }); + it('defines direct dependencies only once', () => { const Boid = d.struct({ pos: d.vec3f,