Skip to content

Commit 588b090

Browse files
authored
fix: Giving local declarations unique names if they clash with global declarations, and vice-versa + 'strict' the default naming scheme in pipelines (#2000)
1 parent a7a7dc9 commit 588b090

52 files changed

Lines changed: 3369 additions & 3156 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/typegpu/src/core/function/fnCore.ts

Lines changed: 4 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,7 @@
1-
import { FuncParameterType } from 'tinyest';
21
import { getAttributesString } from '../../data/attributes.ts';
32
import { type AnyData, undecorate } from '../../data/dataTypes.ts';
4-
import {
5-
type ResolvedSnippet,
6-
snip,
7-
type Snippet,
8-
} from '../../data/snippet.ts';
9-
import {
10-
isPtr,
11-
isWgslData,
12-
isWgslStruct,
13-
Void,
14-
type WgslStruct,
15-
} from '../../data/wgslTypes.ts';
3+
import { type ResolvedSnippet, snip } from '../../data/snippet.ts';
4+
import { isWgslData, isWgslStruct, Void } from '../../data/wgslTypes.ts';
165
import { MissingLinksError } from '../../errors.ts';
176
import { getMetaData, getName, setName } from '../../shared/meta.ts';
187
import type { ResolutionCtx } from '../../types.ts';
@@ -190,54 +179,6 @@ export function createFnCore(
190179

191180
// generate wgsl string
192181

193-
const args: Snippet[] = [];
194-
const argAliases: [string, Snippet][] = [];
195-
196-
for (const [i, argType] of argTypes.entries()) {
197-
const astParam = ast.params[i];
198-
// We know if arguments are passed by reference or by value, because we
199-
// enforce that based on the whether the argument is a pointer or not.
200-
//
201-
// It still applies for shell-less functions, since we determine the type
202-
// of the argument based on the argument's referentiality.
203-
// In other words, if we pass a reference to a function, it's typed as a pointer,
204-
// otherwise it's typed as a value.
205-
const origin = isPtr(argType)
206-
? argType.addressSpace === 'storage'
207-
? argType.access === 'read' ? 'readonly' : 'mutable'
208-
: argType.addressSpace
209-
: 'argument';
210-
211-
switch (astParam?.type) {
212-
case FuncParameterType.identifier: {
213-
const rawName = astParam.name;
214-
const snippet = snip(ctx.makeNameValid(rawName), argType, origin);
215-
args.push(snippet);
216-
if (snippet.value !== rawName) {
217-
argAliases.push([rawName, snippet]);
218-
}
219-
break;
220-
}
221-
case FuncParameterType.destructuredObject: {
222-
args.push(snip(`_arg_${i}`, argType, origin));
223-
argAliases.push(...astParam.props.map(({ name, alias }) => {
224-
// Undecorating, as the struct type can contain builtins
225-
const destrType = undecorate(
226-
(argTypes[i] as WgslStruct).propTypes[name],
227-
);
228-
229-
return [
230-
alias,
231-
snip(`_arg_${i}.${name}`, destrType, 'argument'),
232-
] as [string, Snippet];
233-
}));
234-
break;
235-
}
236-
case undefined:
237-
args.push(snip(`_arg_${i}`, argType, origin));
238-
}
239-
}
240-
241182
const { head, body, returnType: actualReturnType } = ctx.fnToWgsl({
242183
functionType: fnAttribute.includes('@compute')
243184
? 'compute'
@@ -246,8 +187,8 @@ export function createFnCore(
246187
: fnAttribute.includes('@fragment')
247188
? 'fragment'
248189
: 'normal',
249-
args,
250-
argAliases: Object.fromEntries(argAliases),
190+
argTypes,
191+
params: ast.params,
251192
returnType,
252193
body: ast.body,
253194
externalMap,

packages/typegpu/src/core/resolve/namespace.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ export function getUniqueName(
8787
namespace: NamespaceInternal,
8888
resource: object,
8989
): string {
90-
const name = namespace.nameRegistry.makeUnique(getName(resource));
90+
const name = namespace.nameRegistry.makeUnique(getName(resource), true);
9191
for (const listener of namespace.listeners.name) {
9292
listener({ target: resource, name });
9393
}

packages/typegpu/src/core/root/init.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -848,7 +848,7 @@ export async function init(options?: InitOptions): Promise<TgpuRoot> {
848848
const {
849849
adapter: adapterOpt,
850850
device: deviceOpt,
851-
unstable_names: names = 'random',
851+
unstable_names: names = 'strict',
852852
unstable_logOptions,
853853
} = options ?? {};
854854

@@ -907,7 +907,7 @@ export async function init(options?: InitOptions): Promise<TgpuRoot> {
907907
export function initFromDevice(options: InitFromDeviceOptions): TgpuRoot {
908908
const {
909909
device,
910-
unstable_names: names = 'random',
910+
unstable_names: names = 'strict',
911911
unstable_logOptions,
912912
} = options ?? {};
913913

packages/typegpu/src/nameRegistry.ts

Lines changed: 64 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,8 @@ const bannedTokens = new Set([
173173
'with',
174174
'writeonly',
175175
'yield',
176+
// Keywords that should be reserved
177+
'sampler',
176178
]);
177179

178180
export interface NameRegistry {
@@ -181,8 +183,9 @@ export interface NameRegistry {
181183
* in the lifetime of a single resolution process.
182184
* Should append "_" to primer, followed by some id.
183185
* @param primer Used in the generation process, makes the identifier more recognizable.
186+
* @param global Whether the name should be registered in the global scope (true), or in the current function scope (false)
184187
*/
185-
makeUnique(primer?: string): string;
188+
makeUnique(primer: string | undefined, global: boolean): string;
186189

187190
/**
188191
* Creates a valid WGSL identifier.
@@ -196,6 +199,9 @@ export interface NameRegistry {
196199
* makeValid("_"); // ERROR (too difficult to make valid to care)
197200
*/
198201
makeValid(primer: string): string;
202+
203+
pushFunctionScope(): void;
204+
popFunctionScope(): void;
199205
}
200206

201207
function sanitizePrimer(primer: string | undefined) {
@@ -229,45 +235,78 @@ export function isValidIdentifier(ident: string): boolean {
229235
}
230236

231237
abstract class NameRegistryImpl implements NameRegistry {
232-
abstract makeUnique(primer?: string): string;
238+
abstract getUniqueVariant(base: string): string;
239+
240+
readonly #usedNames: Set<string>;
241+
readonly #usedFunctionScopeNamesStack: Set<string>[];
242+
243+
constructor() {
244+
this.#usedNames = new Set<string>(bannedTokens);
245+
this.#usedFunctionScopeNamesStack = [];
246+
}
247+
248+
get usedFunctionScopeNames(): Set<string> | undefined {
249+
return this
250+
.#usedFunctionScopeNamesStack[
251+
this.#usedFunctionScopeNamesStack.length - 1
252+
];
253+
}
254+
255+
makeUnique(primer: string | undefined, global: boolean): string {
256+
const sanitizedPrimer = sanitizePrimer(primer);
257+
const name = this.getUniqueVariant(sanitizedPrimer);
258+
259+
if (global) {
260+
this.#usedNames.add(name);
261+
} else {
262+
this.usedFunctionScopeNames?.add(name);
263+
}
264+
265+
return name;
266+
}
233267

234268
makeValid(primer: string): string {
235-
if (isValidIdentifier(primer)) {
269+
if (isValidIdentifier(primer) && !this.#usedNames.has(primer)) {
270+
this.usedFunctionScopeNames?.add(primer);
236271
return primer;
237272
}
238-
return this.makeUnique(primer);
273+
return this.makeUnique(primer, false);
239274
}
240-
}
241275

242-
export class RandomNameRegistry extends NameRegistryImpl {
243-
private lastUniqueId = 0;
276+
isUsed(name: string): boolean {
277+
return this.#usedNames.has(name) ||
278+
!!this.usedFunctionScopeNames?.has(name);
279+
}
244280

245-
makeUnique(primer?: string | undefined): string {
246-
const sanitizedPrimer = sanitizePrimer(primer);
281+
pushFunctionScope(): void {
282+
this.#usedFunctionScopeNamesStack.push(new Set<string>());
283+
}
247284

248-
return `${sanitizedPrimer}_${this.lastUniqueId++}`;
285+
popFunctionScope(): void {
286+
this.#usedFunctionScopeNamesStack.pop();
249287
}
250288
}
251289

252-
export class StrictNameRegistry extends NameRegistryImpl {
253-
/**
254-
* Allows to provide a good fallback for instances of the
255-
* same function that are bound to different slot values.
256-
*/
257-
private readonly _usedNames = new Set<string>(bannedTokens);
290+
export class RandomNameRegistry extends NameRegistryImpl {
291+
#lastUniqueId = 0;
258292

259-
// TODO: optimize this with a map
260-
makeUnique(primer?: string | undefined): string {
261-
const sanitizedPrimer = sanitizePrimer(primer);
293+
getUniqueVariant(base: string): string {
294+
let name = `${base}_${this.#lastUniqueId++}`;
295+
while (this.isUsed(name)) {
296+
name = `${base}_${this.#lastUniqueId++}`;
297+
}
298+
return name;
299+
}
300+
}
262301

302+
export class StrictNameRegistry extends NameRegistryImpl {
303+
getUniqueVariant(base: string): string {
263304
let index = 0;
264-
let label = sanitizedPrimer;
265-
while (this._usedNames.has(label)) {
305+
let name = base;
306+
while (this.isUsed(name)) {
266307
index++;
267-
label = `${sanitizedPrimer}_${index}`;
308+
name = `${base}_${index}`;
268309
}
269-
270-
this._usedNames.add(label);
271-
return label;
310+
return name;
272311
}
273312
}

0 commit comments

Comments
 (0)