Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 17 additions & 10 deletions packages/typegpu/src/tgsl/wgslGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,17 @@ const usageToVarTemplateMap: Record<VariableScope | BindableBufferUsage, string>
*/
const functionInitialBlockDepth = 2;

function schemaWrappingSuggestion(declaration: string, rhs: string, value: unknown): string {
if (value === null || value === undefined || typeof value === 'string') {
return '';
}

return `
-----
- Try using or defining a schema that matches your desired value the most, and wrap the value with it: '${declaration} = Schema(${rhs})'
-----`;
}

export class WgslGenerator implements ShaderGenerator {
#ctx: ResolutionCtx | undefined = undefined;
// used to detect `continue` and `break` nodes in loop body, as well as label
Expand Down Expand Up @@ -1334,13 +1345,11 @@ Try 'return ${typeStr}(${str});' instead.

const definitionDataType = eq.dataType;

if (definitionDataType === UnknownData) {
if (definitionDataType === UnknownData || wgsl.isVoid(definitionDataType)) {
const rhsStr = stringifyNode(eqNode);
const declaration = `let ${rawId}`;
throw new WgslTypeError(
`'let ${rawId} = ${rhsStr}' is invalid, cannot determine WGSL type of '${rhsStr}'
-----
- Try using or defining a schema that matches your desired value the most, and wrap the value with it: 'let ${rawId} = Schema(${rhsStr})'
-----`,
`'${declaration} = ${rhsStr}' is invalid, cannot determine WGSL type of '${rhsStr}'${schemaWrappingSuggestion(declaration, rhsStr, eq.value)}`,
);
}

Expand Down Expand Up @@ -1423,13 +1432,11 @@ Try 'return ${typeStr}(${str});' instead.
let varType: 'var' | 'let' | 'const' | '<deferred>' = '<deferred>';
let definitionDataType = eq.dataType;

if (definitionDataType === UnknownData) {
if (definitionDataType === UnknownData || wgsl.isVoid(definitionDataType)) {
const rhsStr = stringifyNode(eqNode);
const declaration = `const ${rawId}`;
throw new WgslTypeError(
`'const ${rawId} = ${rhsStr}' is invalid, cannot determine WGSL type of '${rhsStr}'
-----
- Try using or defining a schema that matches your desired value the most, and wrap the value with it: 'const ${rawId} = Schema(${rhsStr})'
-----`,
`'${declaration} = ${rhsStr}' is invalid, cannot determine WGSL type of '${rhsStr}'${schemaWrappingSuggestion(declaration, rhsStr, eq.value)}`,
);
}

Expand Down
49 changes: 45 additions & 4 deletions packages/typegpu/tests/tgsl/letDeclaration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,20 @@ import { tgpu, d } from 'typegpu';
import { expectSnippetOf } from '../utils/parseResolved.ts';

describe('let declarations', () => {
it('rejects let assigning the result of a void function', () => {
const noop = tgpu.fn([])(() => {});

const f = tgpu.fn([])(() => {
let a = noop();
});

expect(() => tgpu.resolve([f])).toThrowErrorMatchingInlineSnapshot(`
[Error: Resolution of the following tree failed:
- <root>
- fn:f: 'let a = noop()' is invalid, cannot determine WGSL type of 'noop()']
`);
});

it('initializes a local definition with a scalar value', () => {
function foo() {
'use gpu';
Expand Down Expand Up @@ -49,10 +63,37 @@ describe('let declarations', () => {
[Error: Resolution of the following tree failed:
- <root>
- fn*:foo
- fn*:foo(): 'let a = "12"' is invalid, cannot determine WGSL type of '"12"'
-----
- Try using or defining a schema that matches your desired value the most, and wrap the value with it: 'let a = Schema("12")'
-----]
- fn*:foo(): 'let a = "12"' is invalid, cannot determine WGSL type of '"12"']
`);
});

it('throws when initializing with null without a schema hint', () => {
function foo() {
'use gpu';
let a = null;
return a;
}

expect(() => tgpu.resolve([foo])).toThrowErrorMatchingInlineSnapshot(`
[Error: Resolution of the following tree failed:
- <root>
- fn*:foo
- fn*:foo(): 'let a = null' is invalid, cannot determine WGSL type of 'null']
`);
});

it('rejects bare undefined because it has no WGSL type', () => {
function foo() {
'use gpu';
let a = undefined;
return a;
}

expect(() => tgpu.resolve([foo])).toThrowErrorMatchingInlineSnapshot(`
[Error: Resolution of the following tree failed:
- <root>
- fn*:foo
- fn*:foo(): 'let a = undefined' is invalid, cannot determine WGSL type of 'undefined']
`);
});
});
56 changes: 56 additions & 0 deletions packages/typegpu/tests/tgsl/typeInference.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,62 @@ describe('wgsl generator type inference', () => {
`);
});

it('does not suggest wrapping null with a schema', () => {
const myFn = () => {
'use gpu';
const a = null;
};

expect(() => tgpu.resolve([myFn])).toThrowErrorMatchingInlineSnapshot(`
[Error: Resolution of the following tree failed:
- <root>
- fn*:myFn
- fn*:myFn(): 'const a = null' is invalid, cannot determine WGSL type of 'null']
`);
});

it('rejects bare undefined because it has no WGSL type', () => {
const myFn = () => {
'use gpu';
const a = undefined;
};

expect(() => tgpu.resolve([myFn])).toThrowErrorMatchingInlineSnapshot(`
[Error: Resolution of the following tree failed:
- <root>
- fn*:myFn
- fn*:myFn(): 'const a = undefined' is invalid, cannot determine WGSL type of 'undefined']
`);
});

it('does not suggest wrapping a string with a schema', () => {
const myFn = () => {
'use gpu';
const a = 'hello';
};

expect(() => tgpu.resolve([myFn])).toThrowErrorMatchingInlineSnapshot(`
[Error: Resolution of the following tree failed:
- <root>
- fn*:myFn
- fn*:myFn(): 'const a = "hello"' is invalid, cannot determine WGSL type of '"hello"']
`);
});

it('rejects assigning the result of a void function', () => {
const noop = tgpu.fn([])(() => {});

const f = tgpu.fn([])(() => {
const a = noop();
});

expect(() => tgpu.resolve([f])).toThrowErrorMatchingInlineSnapshot(`
[Error: Resolution of the following tree failed:
- <root>
- fn:f: 'const a = noop()' is invalid, cannot determine WGSL type of 'noop()']
`);
});

it('throws when creating an empty untyped array', () => {
const myFn = tgpu.fn([])(() => {
const myArr = [];
Expand Down
17 changes: 0 additions & 17 deletions packages/typegpu/tests/tgslFn.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1312,23 +1312,6 @@ describe('string injection', () => {
});

describe('nulls in TGSL', () => {
it('throws when assigning to a variable', () => {
const myFn = () => {
'use gpu';
const a = null;
};

expect(() => tgpu.resolve([myFn])).toThrowErrorMatchingInlineSnapshot(`
[Error: Resolution of the following tree failed:
- <root>
- fn*:myFn
- fn*:myFn(): 'const a = null' is invalid, cannot determine WGSL type of 'null'
-----
- Try using or defining a schema that matches your desired value the most, and wrap the value with it: 'const a = Schema(null)'
-----]
`);
});

it('allows comptime usage', () => {
let externalNum: number | null;
const myFn = () => {
Expand Down