Skip to content

Commit f1899ea

Browse files
aleksanderkatanAleksander Kataniwoplazareczkok
authored
feat: Expected type stack in WgslGenerator (#1532)
* Add struct default constructor * Update docs * Update packages/typegpu/tests/struct.test.ts Co-authored-by: Iwo Plaza <[email protected]> * Update packages/typegpu/src/tgsl/wgslGenerator.ts Co-authored-by: Konrad Reczko <[email protected]> * Change [1] to [0] * Make array schemas callable (but they do nothing) * Move the struct resolve test to wgslGenerator.test.ts * Move tests back to struct.test.ts * Add array tests * Implement JS side of array calls * Implement WGSL side * Change u32 to i32 * Add tgsl parsing tests * Remove unnecessary (I hope so) parentheses * Docs * Lint * Fix import * Lint * Nits * Remove generic from struct constructor, update docs * Apply suggestions from code review Co-authored-by: Iwo Plaza <[email protected]> * Update array toString, override dualImpl toString * Add some tests * Add expectedTypeStack * Use expectedTypeStack in struct call * Use expectedTypeStack for returns and nested structs * Remove callStack from return value generation, comment out some weird tests * Remove the arbitrary object cast to output type * CallStack is kil * Remove legacy calls to stack from tgpuFn * Remove legacy calls from fragmentFn and tgpuFn * Add array support * Add type coercion, apply type coercion to return value * Merge fixes * Move struct/array handling * Cleanup struct/array call * Refactor * Add function support * Add bool to the stack for if/for/while conditions * Bring back commented test 1 * Bring back commented tests 2 & 3 * Refactor array generation, add better comments * Rename `argTypes` to `argConversionHint` * Remove UnknownData from stack type * Docs for expectedTypeStack * Lint * Update error messages * Nits * Better convert to null message * Update tests to still create Output structs before return statement * Unify type errors * Simpler tests, review fixes * Holding the expected type stack... on the stack * Tweaks * Review fixes * Shuffle things around --------- Co-authored-by: Aleksander Katan <[email protected]> Co-authored-by: Iwo Plaza <[email protected]> Co-authored-by: Konrad Reczko <[email protected]>
1 parent 3423a07 commit f1899ea

28 files changed

+674
-421
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import type { TgpuDualFn } from '../../data/dataTypes.ts';
2+
import type { MapValueToSnippet, Snippet } from '../../data/snippet.ts';
3+
import { inCodegenMode } from '../../execMode.ts';
4+
import type { FnArgsConversionHint } from '../../types.ts';
5+
import { setName } from '../../shared/meta.ts';
6+
import { $internal } from '../../shared/symbols.ts';
7+
8+
export function createDualImpl<T extends (...args: never[]) => unknown>(
9+
jsImpl: T,
10+
gpuImpl: (...args: MapValueToSnippet<Parameters<T>>) => Snippet,
11+
name: string,
12+
argConversionHint: FnArgsConversionHint = 'keep',
13+
): TgpuDualFn<T> {
14+
const impl = ((...args: Parameters<T>) => {
15+
if (inCodegenMode()) {
16+
return gpuImpl(...(args as MapValueToSnippet<Parameters<T>>)) as Snippet;
17+
}
18+
return jsImpl(...args);
19+
}) as T;
20+
21+
setName(impl, name);
22+
impl.toString = () => name;
23+
(impl as TgpuDualFn<T>)[$internal] = { jsImpl, gpuImpl, argConversionHint };
24+
25+
return impl as TgpuDualFn<T>;
26+
}

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

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { snip } from '../../data/snippet.ts';
44
import { Void } from '../../data/wgslTypes.ts';
55
import { ExecutionError } from '../../errors.ts';
66
import { provideInsideTgpuFn } from '../../execMode.ts';
7-
import { createDualImpl } from '../../shared/generators.ts';
87
import type { TgpuNamable } from '../../shared/meta.ts';
98
import { getName, setName } from '../../shared/meta.ts';
109
import type { Infer } from '../../shared/repr.ts';
@@ -14,7 +13,6 @@ import {
1413
$providing,
1514
} from '../../shared/symbols.ts';
1615
import type { Prettify } from '../../shared/utilityTypes.ts';
17-
import type { GenerationCtx } from '../../tgsl/generationHelpers.ts';
1816
import type {
1917
FnArgsConversionHint,
2018
ResolutionCtx,
@@ -43,6 +41,7 @@ import type {
4341
InheritArgNames,
4442
} from './fnTypes.ts';
4543
import { stripTemplate } from './templateUtils.ts';
44+
import { createDualImpl } from './dualImpl.ts';
4645

4746
// ----------
4847
// Public API
@@ -85,7 +84,7 @@ export type TgpuFnShell<
8584
interface TgpuFnBase<ImplSchema extends AnyFn> extends TgpuNamable {
8685
readonly [$internal]: {
8786
implementation: Implementation<ImplSchema>;
88-
argTypes: FnArgsConversionHint;
87+
argConversionHint: FnArgsConversionHint;
8988
};
9089
readonly resourceType: 'function';
9190
readonly shell: TgpuFnShellHeader<
@@ -170,7 +169,7 @@ function createFn<ImplSchema extends AnyFn>(
170169
const fnBase: This = {
171170
[$internal]: {
172171
implementation,
173-
argTypes: shell.argTypes,
172+
argConversionHint: shell.argTypes,
174173
},
175174
shell,
176175
resourceType: 'function' as const,
@@ -207,23 +206,9 @@ function createFn<ImplSchema extends AnyFn>(
207206
shell.returnType,
208207
core.applyExternals,
209208
);
210-
211-
return core.resolve(ctx, shell.argTypes, shell.returnType);
212209
}
213210

214-
const generationCtx = ctx as GenerationCtx;
215-
if (generationCtx.callStack === undefined) {
216-
throw new Error(
217-
'Cannot resolve a TGSL function outside of a generation context',
218-
);
219-
}
220-
221-
try {
222-
generationCtx.callStack.push(shell.returnType);
223-
return core.resolve(ctx, shell.argTypes, shell.returnType);
224-
} finally {
225-
generationCtx.callStack.pop();
226-
}
211+
return core.resolve(ctx, shell.argTypes, shell.returnType);
227212
},
228213
};
229214

@@ -284,7 +269,7 @@ function createBoundFunction<ImplSchema extends AnyFn>(
284269
const fnBase: This = {
285270
[$internal]: {
286271
implementation: innerFn[$internal].implementation,
287-
argTypes: innerFn[$internal].argTypes,
272+
argConversionHint: innerFn[$internal].argConversionHint,
288273
},
289274
resourceType: 'function',
290275
shell: innerFn.shell,

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

Lines changed: 5 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import {
1717
type TgpuNamable,
1818
} from '../../shared/meta.ts';
1919
import { $getNameForward, $internal } from '../../shared/symbols.ts';
20-
import type { GenerationCtx } from '../../tgsl/generationHelpers.ts';
2120
import type { ResolutionCtx, SelfResolvable } from '../../types.ts';
2221
import { addReturnTypeToExternals } from '../resolve/externals.ts';
2322
import { createFnCore, type FnCore } from './fnCore.ts';
@@ -227,31 +226,11 @@ function createFragmentFn(
227226
}
228227
core.applyExternals({ Out: outputType });
229228

230-
if (typeof implementation === 'string') {
231-
return core.resolve(
232-
ctx,
233-
inputWithLocation ? [inputWithLocation] : [],
234-
shell.returnType,
235-
);
236-
}
237-
238-
const generationCtx = ctx as GenerationCtx;
239-
if (generationCtx.callStack === undefined) {
240-
throw new Error(
241-
'Cannot resolve a TGSL function outside of a generation context',
242-
);
243-
}
244-
245-
try {
246-
generationCtx.callStack.push(outputType);
247-
return core.resolve(
248-
ctx,
249-
inputWithLocation ? [inputWithLocation] : [],
250-
shell.returnType,
251-
);
252-
} finally {
253-
generationCtx.callStack.pop();
254-
}
229+
return core.resolve(
230+
ctx,
231+
inputWithLocation ? [inputWithLocation] : [],
232+
shell.returnType,
233+
);
255234
},
256235

257236
toString() {

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

Lines changed: 5 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import {
1616
type TgpuNamable,
1717
} from '../../shared/meta.ts';
1818
import { $getNameForward, $internal } from '../../shared/symbols.ts';
19-
import type { GenerationCtx } from '../../tgsl/generationHelpers.ts';
2019
import type { ResolutionCtx, SelfResolvable } from '../../types.ts';
2120
import { createFnCore, type FnCore } from './fnCore.ts';
2221
import type {
@@ -207,31 +206,13 @@ function createVertexFn(
207206
core.applyExternals({ In: inputType });
208207
}
209208
core.applyExternals({ Out: outputWithLocation });
210-
211-
return core.resolve(
212-
ctx,
213-
shell.argTypes,
214-
outputWithLocation,
215-
);
216-
}
217-
218-
const generationCtx = ctx as GenerationCtx;
219-
if (generationCtx.callStack === undefined) {
220-
throw new Error(
221-
'Cannot resolve a TGSL function outside of a generation context',
222-
);
223209
}
224210

225-
try {
226-
generationCtx.callStack.push(outputWithLocation);
227-
return core.resolve(
228-
ctx,
229-
shell.argTypes,
230-
outputWithLocation,
231-
);
232-
} finally {
233-
generationCtx.callStack.pop();
234-
}
211+
return core.resolve(
212+
ctx,
213+
shell.argTypes,
214+
outputWithLocation,
215+
);
235216
},
236217

237218
toString() {

packages/typegpu/src/data/dataTypes.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export type TgpuDualFn<TImpl extends (...args: never[]) => unknown> =
2828
[$internal]: {
2929
jsImpl: TImpl | string;
3030
gpuImpl: (...args: MapValueToSnippet<Parameters<TImpl>>) => Snippet;
31-
argTypes: FnArgsConversionHint;
31+
argConversionHint: FnArgsConversionHint;
3232
};
3333
};
3434

packages/typegpu/src/data/matrix.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { createDualImpl } from '../shared/generators.ts';
1+
import { createDualImpl } from '../core/function/dualImpl.ts';
22
import type { $repr } from '../shared/symbols.ts';
33
import { $internal } from '../shared/symbols.ts';
44
import type { SelfResolvable } from '../types.ts';

packages/typegpu/src/data/numeric.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { createDualImpl } from '../shared/generators.ts';
1+
import { createDualImpl } from '../core/function/dualImpl.ts';
22
import { $internal } from '../shared/symbols.ts';
33
import { snip } from './snippet.ts';
44
import type {

packages/typegpu/src/data/vector.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { createDualImpl } from '../shared/generators.ts';
1+
import { createDualImpl } from '../core/function/dualImpl.ts';
22
import { $repr } from '../shared/symbols.ts';
33
import { snip } from './snippet.ts';
44
import { bool, f16, f32, i32, u32 } from './numeric.ts';

packages/typegpu/src/errors.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,3 +197,12 @@ export class IllegalBufferAccessError extends Error {
197197
Object.setPrototypeOf(this, IllegalBufferAccessError.prototype);
198198
}
199199
}
200+
201+
export class WgslTypeError extends Error {
202+
constructor(msg: string) {
203+
super(msg);
204+
205+
// Set the prototype explicitly.
206+
Object.setPrototypeOf(this, WgslTypeError.prototype);
207+
}
208+
}

packages/typegpu/src/resolutionCtx.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,14 @@ class ItemStateStackImpl implements ItemStateStack {
101101
return state;
102102
}
103103

104+
get topFunctionReturnType(): AnyData {
105+
const scope = this._stack.findLast((e) => e.type === 'functionScope');
106+
if (!scope) {
107+
throw new Error('Internal error, expected function scope to be present.');
108+
}
109+
return scope.returnType;
110+
}
111+
104112
pushItem() {
105113
this._itemDepth++;
106114
this._stack.push({
@@ -343,8 +351,8 @@ export class ResolutionCtxImpl implements ResolutionCtx {
343351
public readonly fixedBindings: FixedBindingConfig[] = [];
344352
// --
345353

346-
public readonly callStack: unknown[] = [];
347354
public readonly names: NameRegistry;
355+
public expectedType: AnyData | undefined;
348356

349357
constructor(opts: ResolutionCtxImplOptions) {
350358
this.names = opts.names;
@@ -354,6 +362,10 @@ export class ResolutionCtxImpl implements ResolutionCtx {
354362
return this._indentController.pre;
355363
}
356364

365+
get topFunctionReturnType() {
366+
return this._itemStateStack.topFunctionReturnType;
367+
}
368+
357369
indent(): string {
358370
return this._indentController.indent();
359371
}

0 commit comments

Comments
 (0)