From 2e80d3c87d4f4145c6591728f9ffbe0b5d531e10 Mon Sep 17 00:00:00 2001 From: huytdps13400 <68500260+huytdps13400@users.noreply.github.com> Date: Sun, 30 Aug 2026 09:24:50 +0700 Subject: [PATCH 1/6] fix: improve undefined variable diagnostics --- packages/typegpu/src/tgsl/wgslGenerator.ts | 10 ++++-- .../typegpu/tests/tgsl/letDeclaration.test.ts | 18 ++++++++++ .../typegpu/tests/tgsl/typeInference.test.ts | 34 +++++++++++++++++++ 3 files changed, 60 insertions(+), 2 deletions(-) diff --git a/packages/typegpu/src/tgsl/wgslGenerator.ts b/packages/typegpu/src/tgsl/wgslGenerator.ts index 33d64ec786..460d176a7f 100644 --- a/packages/typegpu/src/tgsl/wgslGenerator.ts +++ b/packages/typegpu/src/tgsl/wgslGenerator.ts @@ -1334,7 +1334,10 @@ Try 'return ${typeStr}(${str});' instead. const definitionDataType = eq.dataType; - if (definitionDataType === UnknownData) { + if ( + definitionDataType === UnknownData || + (eq.value === undefined && wgsl.isVoid(definitionDataType)) + ) { const rhsStr = stringifyNode(eqNode); throw new WgslTypeError( `'let ${rawId} = ${rhsStr}' is invalid, cannot determine WGSL type of '${rhsStr}' @@ -1423,7 +1426,10 @@ Try 'return ${typeStr}(${str});' instead. let varType: 'var' | 'let' | 'const' | '' = ''; let definitionDataType = eq.dataType; - if (definitionDataType === UnknownData) { + if ( + definitionDataType === UnknownData || + (eq.value === undefined && wgsl.isVoid(definitionDataType)) + ) { const rhsStr = stringifyNode(eqNode); throw new WgslTypeError( `'const ${rawId} = ${rhsStr}' is invalid, cannot determine WGSL type of '${rhsStr}' diff --git a/packages/typegpu/tests/tgsl/letDeclaration.test.ts b/packages/typegpu/tests/tgsl/letDeclaration.test.ts index ed88e09832..9290db5cb2 100644 --- a/packages/typegpu/tests/tgsl/letDeclaration.test.ts +++ b/packages/typegpu/tests/tgsl/letDeclaration.test.ts @@ -55,4 +55,22 @@ describe('let declarations', () => { -----] `); }); + + it('suggests wrapping an untyped undefined value with a schema', () => { + function foo() { + 'use gpu'; + let a = undefined; + return a; + } + + expect(() => tgpu.resolve([foo])).toThrowErrorMatchingInlineSnapshot(` + [Error: Resolution of the following tree failed: + - + - fn*:foo + - fn*:foo(): 'let a = undefined' is invalid, cannot determine WGSL type of 'undefined' + ----- + - Try using or defining a schema that matches your desired value the most, and wrap the value with it: 'let a = Schema(undefined)' + -----] + `); + }); }); diff --git a/packages/typegpu/tests/tgsl/typeInference.test.ts b/packages/typegpu/tests/tgsl/typeInference.test.ts index 89269b63a2..94a5a8ade6 100644 --- a/packages/typegpu/tests/tgsl/typeInference.test.ts +++ b/packages/typegpu/tests/tgsl/typeInference.test.ts @@ -317,6 +317,40 @@ describe('wgsl generator type inference', () => { `); }); + it('suggests wrapping an untyped null variable with a schema', () => { + const myFn = () => { + 'use gpu'; + const a = null; + }; + + expect(() => tgpu.resolve([myFn])).toThrowErrorMatchingInlineSnapshot(` + [Error: Resolution of the following tree failed: + - + - 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('suggests wrapping an untyped undefined variable with a schema', () => { + const myFn = () => { + 'use gpu'; + const a = undefined; + }; + + expect(() => tgpu.resolve([myFn])).toThrowErrorMatchingInlineSnapshot(` + [Error: Resolution of the following tree failed: + - + - fn*:myFn + - fn*:myFn(): 'const a = undefined' is invalid, cannot determine WGSL type of 'undefined' + ----- + - Try using or defining a schema that matches your desired value the most, and wrap the value with it: 'const a = Schema(undefined)' + -----] + `); + }); + it('throws when creating an empty untyped array', () => { const myFn = tgpu.fn([])(() => { const myArr = []; From 11511f3f9d9caad9f5de76ce65f9bc61d630ba47 Mon Sep 17 00:00:00 2001 From: huymobile Date: Mon, 31 Aug 2026 20:52:42 +0700 Subject: [PATCH 2/6] fix: avoid invalid schema wrapping hints --- packages/typegpu/src/tgsl/wgslGenerator.ts | 23 +++++++++------ .../typegpu/tests/tgsl/letDeclaration.test.ts | 12 ++------ .../typegpu/tests/tgsl/typeInference.test.ts | 28 ++++++++++++------- packages/typegpu/tests/tgslFn.test.ts | 5 +--- 4 files changed, 37 insertions(+), 31 deletions(-) diff --git a/packages/typegpu/src/tgsl/wgslGenerator.ts b/packages/typegpu/src/tgsl/wgslGenerator.ts index 460d176a7f..0a6b5ffcca 100644 --- a/packages/typegpu/src/tgsl/wgslGenerator.ts +++ b/packages/typegpu/src/tgsl/wgslGenerator.ts @@ -218,6 +218,17 @@ const usageToVarTemplateMap: Record */ 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 @@ -1339,11 +1350,9 @@ Try 'return ${typeStr}(${str});' instead. (eq.value === undefined && 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)}`, ); } @@ -1431,11 +1440,9 @@ Try 'return ${typeStr}(${str});' instead. (eq.value === undefined && 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)}`, ); } diff --git a/packages/typegpu/tests/tgsl/letDeclaration.test.ts b/packages/typegpu/tests/tgsl/letDeclaration.test.ts index 9290db5cb2..57bfed4fab 100644 --- a/packages/typegpu/tests/tgsl/letDeclaration.test.ts +++ b/packages/typegpu/tests/tgsl/letDeclaration.test.ts @@ -49,14 +49,11 @@ describe('let declarations', () => { [Error: Resolution of the following tree failed: - - 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('suggests wrapping an untyped undefined value with a schema', () => { + it('does not suggest wrapping undefined with a schema', () => { function foo() { 'use gpu'; let a = undefined; @@ -67,10 +64,7 @@ describe('let declarations', () => { [Error: Resolution of the following tree failed: - - fn*:foo - - fn*:foo(): 'let a = undefined' is invalid, cannot determine WGSL type of 'undefined' - ----- - - Try using or defining a schema that matches your desired value the most, and wrap the value with it: 'let a = Schema(undefined)' - -----] + - fn*:foo(): 'let a = undefined' is invalid, cannot determine WGSL type of 'undefined'] `); }); }); diff --git a/packages/typegpu/tests/tgsl/typeInference.test.ts b/packages/typegpu/tests/tgsl/typeInference.test.ts index 94a5a8ade6..b19235bb8d 100644 --- a/packages/typegpu/tests/tgsl/typeInference.test.ts +++ b/packages/typegpu/tests/tgsl/typeInference.test.ts @@ -317,7 +317,7 @@ describe('wgsl generator type inference', () => { `); }); - it('suggests wrapping an untyped null variable with a schema', () => { + it('does not suggest wrapping null with a schema', () => { const myFn = () => { 'use gpu'; const a = null; @@ -327,14 +327,11 @@ describe('wgsl generator type inference', () => { [Error: Resolution of the following tree failed: - - 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)' - -----] + - fn*:myFn(): 'const a = null' is invalid, cannot determine WGSL type of 'null'] `); }); - it('suggests wrapping an untyped undefined variable with a schema', () => { + it('does not suggest wrapping undefined with a schema', () => { const myFn = () => { 'use gpu'; const a = undefined; @@ -344,10 +341,21 @@ describe('wgsl generator type inference', () => { [Error: Resolution of the following tree failed: - - fn*:myFn - - fn*:myFn(): 'const a = undefined' is invalid, cannot determine WGSL type of 'undefined' - ----- - - Try using or defining a schema that matches your desired value the most, and wrap the value with it: 'const a = Schema(undefined)' - -----] + - 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: + - + - fn*:myFn + - fn*:myFn(): 'const a = "hello"' is invalid, cannot determine WGSL type of '"hello"'] `); }); diff --git a/packages/typegpu/tests/tgslFn.test.ts b/packages/typegpu/tests/tgslFn.test.ts index 198cfec524..3166f391e9 100644 --- a/packages/typegpu/tests/tgslFn.test.ts +++ b/packages/typegpu/tests/tgslFn.test.ts @@ -1322,10 +1322,7 @@ describe('nulls in TGSL', () => { [Error: Resolution of the following tree failed: - - 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)' - -----] + - fn*:myFn(): 'const a = null' is invalid, cannot determine WGSL type of 'null'] `); }); From 182e8c908d2b1cd907e928e5a06177921e5aafe3 Mon Sep 17 00:00:00 2001 From: huymobile Date: Tue, 1 Sep 2026 01:52:15 +0700 Subject: [PATCH 3/6] fix: preserve void declaration inference --- packages/typegpu/src/tgsl/wgslGenerator.ts | 10 ++---- .../typegpu/tests/tgsl/letDeclaration.test.ts | 35 +++++++++++++++++-- .../typegpu/tests/tgsl/typeInference.test.ts | 4 +-- 3 files changed, 37 insertions(+), 12 deletions(-) diff --git a/packages/typegpu/src/tgsl/wgslGenerator.ts b/packages/typegpu/src/tgsl/wgslGenerator.ts index 0a6b5ffcca..700b6f57e1 100644 --- a/packages/typegpu/src/tgsl/wgslGenerator.ts +++ b/packages/typegpu/src/tgsl/wgslGenerator.ts @@ -1345,10 +1345,7 @@ Try 'return ${typeStr}(${str});' instead. const definitionDataType = eq.dataType; - if ( - definitionDataType === UnknownData || - (eq.value === undefined && wgsl.isVoid(definitionDataType)) - ) { + if (definitionDataType === UnknownData) { const rhsStr = stringifyNode(eqNode); const declaration = `let ${rawId}`; throw new WgslTypeError( @@ -1435,10 +1432,7 @@ Try 'return ${typeStr}(${str});' instead. let varType: 'var' | 'let' | 'const' | '' = ''; let definitionDataType = eq.dataType; - if ( - definitionDataType === UnknownData || - (eq.value === undefined && wgsl.isVoid(definitionDataType)) - ) { + if (definitionDataType === UnknownData) { const rhsStr = stringifyNode(eqNode); const declaration = `const ${rawId}`; throw new WgslTypeError( diff --git a/packages/typegpu/tests/tgsl/letDeclaration.test.ts b/packages/typegpu/tests/tgsl/letDeclaration.test.ts index 57bfed4fab..2e5b9aabe7 100644 --- a/packages/typegpu/tests/tgsl/letDeclaration.test.ts +++ b/packages/typegpu/tests/tgsl/letDeclaration.test.ts @@ -4,6 +4,22 @@ import { tgpu, d } from 'typegpu'; import { expectSnippetOf } from '../utils/parseResolved.ts'; describe('let declarations', () => { + it('supports assigning the result of a void function', () => { + const noop = tgpu.fn([])(() => {}); + + const f = tgpu.fn([])(() => { + const a = noop(); + }); + + expect(tgpu.resolve([f])).toMatchInlineSnapshot(` + "fn noop() {} + + fn f() { + let a = noop(); + }" + `); + }); + it('initializes a local definition with a scalar value', () => { function foo() { 'use gpu'; @@ -53,7 +69,22 @@ describe('let declarations', () => { `); }); - it('does not suggest wrapping undefined with a schema', () => { + 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: + - + - fn*:foo + - fn*:foo(): 'let a = null' is invalid, cannot determine WGSL type of 'null'] + `); + }); + + it('reports that bare undefined cannot resolve to void', () => { function foo() { 'use gpu'; let a = undefined; @@ -64,7 +95,7 @@ describe('let declarations', () => { [Error: Resolution of the following tree failed: - - fn*:foo - - fn*:foo(): 'let a = undefined' is invalid, cannot determine WGSL type of 'undefined'] + - fn*:foo(): Value undefined is not resolvable to type void] `); }); }); diff --git a/packages/typegpu/tests/tgsl/typeInference.test.ts b/packages/typegpu/tests/tgsl/typeInference.test.ts index b19235bb8d..5291d5142a 100644 --- a/packages/typegpu/tests/tgsl/typeInference.test.ts +++ b/packages/typegpu/tests/tgsl/typeInference.test.ts @@ -331,7 +331,7 @@ describe('wgsl generator type inference', () => { `); }); - it('does not suggest wrapping undefined with a schema', () => { + it('reports that bare undefined cannot resolve to void', () => { const myFn = () => { 'use gpu'; const a = undefined; @@ -341,7 +341,7 @@ describe('wgsl generator type inference', () => { [Error: Resolution of the following tree failed: - - fn*:myFn - - fn*:myFn(): 'const a = undefined' is invalid, cannot determine WGSL type of 'undefined'] + - fn*:myFn(): Value undefined is not resolvable to type void] `); }); From 080c9cecfd475d1d9bb36a24642eccff234bf29f Mon Sep 17 00:00:00 2001 From: huymobile Date: Tue, 1 Sep 2026 21:58:27 +0700 Subject: [PATCH 4/6] fix: reject void declaration initializers --- packages/typegpu/src/tgsl/wgslGenerator.ts | 4 +-- .../typegpu/tests/tgsl/letDeclaration.test.ts | 28 +++++++++++++------ .../typegpu/tests/tgsl/typeInference.test.ts | 4 +-- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/packages/typegpu/src/tgsl/wgslGenerator.ts b/packages/typegpu/src/tgsl/wgslGenerator.ts index 700b6f57e1..604f94572b 100644 --- a/packages/typegpu/src/tgsl/wgslGenerator.ts +++ b/packages/typegpu/src/tgsl/wgslGenerator.ts @@ -1345,7 +1345,7 @@ 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( @@ -1432,7 +1432,7 @@ Try 'return ${typeStr}(${str});' instead. let varType: 'var' | 'let' | 'const' | '' = ''; let definitionDataType = eq.dataType; - if (definitionDataType === UnknownData) { + if (definitionDataType === UnknownData || wgsl.isVoid(definitionDataType)) { const rhsStr = stringifyNode(eqNode); const declaration = `const ${rawId}`; throw new WgslTypeError( diff --git a/packages/typegpu/tests/tgsl/letDeclaration.test.ts b/packages/typegpu/tests/tgsl/letDeclaration.test.ts index 2e5b9aabe7..f4ceedf057 100644 --- a/packages/typegpu/tests/tgsl/letDeclaration.test.ts +++ b/packages/typegpu/tests/tgsl/letDeclaration.test.ts @@ -4,19 +4,31 @@ import { tgpu, d } from 'typegpu'; import { expectSnippetOf } from '../utils/parseResolved.ts'; describe('let declarations', () => { - it('supports assigning the result of a void function', () => { + it('rejects assigning the result of a void function', () => { const noop = tgpu.fn([])(() => {}); const f = tgpu.fn([])(() => { const a = noop(); }); - expect(tgpu.resolve([f])).toMatchInlineSnapshot(` - "fn noop() {} + expect(() => tgpu.resolve([f])).toThrowErrorMatchingInlineSnapshot(` + [Error: Resolution of the following tree failed: + - + - fn:f: 'const a = noop()' is invalid, cannot determine WGSL type of 'noop()'] + `); + }); - fn f() { - let a = noop(); - }" + 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: + - + - fn:f: 'let a = noop()' is invalid, cannot determine WGSL type of 'noop()'] `); }); @@ -84,7 +96,7 @@ describe('let declarations', () => { `); }); - it('reports that bare undefined cannot resolve to void', () => { + it('rejects bare undefined because it has no WGSL type', () => { function foo() { 'use gpu'; let a = undefined; @@ -95,7 +107,7 @@ describe('let declarations', () => { [Error: Resolution of the following tree failed: - - fn*:foo - - fn*:foo(): Value undefined is not resolvable to type void] + - fn*:foo(): 'let a = undefined' is invalid, cannot determine WGSL type of 'undefined'] `); }); }); diff --git a/packages/typegpu/tests/tgsl/typeInference.test.ts b/packages/typegpu/tests/tgsl/typeInference.test.ts index 5291d5142a..489602da6f 100644 --- a/packages/typegpu/tests/tgsl/typeInference.test.ts +++ b/packages/typegpu/tests/tgsl/typeInference.test.ts @@ -331,7 +331,7 @@ describe('wgsl generator type inference', () => { `); }); - it('reports that bare undefined cannot resolve to void', () => { + it('rejects bare undefined because it has no WGSL type', () => { const myFn = () => { 'use gpu'; const a = undefined; @@ -341,7 +341,7 @@ describe('wgsl generator type inference', () => { [Error: Resolution of the following tree failed: - - fn*:myFn - - fn*:myFn(): Value undefined is not resolvable to type void] + - fn*:myFn(): 'const a = undefined' is invalid, cannot determine WGSL type of 'undefined'] `); }); From c84e186dd7a014aeb7fb68f64071b23913bf6e69 Mon Sep 17 00:00:00 2001 From: huymobile Date: Wed, 2 Sep 2026 02:52:38 +0700 Subject: [PATCH 5/6] test: move const void regression --- packages/typegpu/tests/tgsl/letDeclaration.test.ts | 14 -------------- packages/typegpu/tests/tgsl/typeInference.test.ts | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/typegpu/tests/tgsl/letDeclaration.test.ts b/packages/typegpu/tests/tgsl/letDeclaration.test.ts index f4ceedf057..3b7dafe8e4 100644 --- a/packages/typegpu/tests/tgsl/letDeclaration.test.ts +++ b/packages/typegpu/tests/tgsl/letDeclaration.test.ts @@ -4,20 +4,6 @@ import { tgpu, d } from 'typegpu'; import { expectSnippetOf } from '../utils/parseResolved.ts'; describe('let declarations', () => { - 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: - - - - fn:f: 'const a = noop()' is invalid, cannot determine WGSL type of 'noop()'] - `); - }); - it('rejects let assigning the result of a void function', () => { const noop = tgpu.fn([])(() => {}); diff --git a/packages/typegpu/tests/tgsl/typeInference.test.ts b/packages/typegpu/tests/tgsl/typeInference.test.ts index 489602da6f..ae51799429 100644 --- a/packages/typegpu/tests/tgsl/typeInference.test.ts +++ b/packages/typegpu/tests/tgsl/typeInference.test.ts @@ -359,6 +359,20 @@ describe('wgsl generator type inference', () => { `); }); + 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: + - + - 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 = []; From 4fa3c88edba070904171f76d278b740e0dc698f1 Mon Sep 17 00:00:00 2001 From: huymobile Date: Wed, 2 Sep 2026 22:18:29 +0700 Subject: [PATCH 6/6] test: remove duplicate null regression --- packages/typegpu/tests/tgslFn.test.ts | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/packages/typegpu/tests/tgslFn.test.ts b/packages/typegpu/tests/tgslFn.test.ts index 3166f391e9..4ec8a43a97 100644 --- a/packages/typegpu/tests/tgslFn.test.ts +++ b/packages/typegpu/tests/tgslFn.test.ts @@ -1312,20 +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: - - - - fn*:myFn - - fn*:myFn(): 'const a = null' is invalid, cannot determine WGSL type of 'null'] - `); - }); - it('allows comptime usage', () => { let externalNum: number | null; const myFn = () => {