From d4eecf9291cf1e6ca86d0c702934ab9c2c18400c Mon Sep 17 00:00:00 2001 From: Aleksander Katan <56294622+aleksanderkatan@users.noreply.github.com> Date: Fri, 24 Apr 2026 13:13:18 +0200 Subject: [PATCH 1/5] Better error --- packages/typegpu/src/tgsl/accessIndex.ts | 16 ++++++++++------ packages/typegpu/src/tgsl/wgslGenerator.ts | 2 +- .../typegpu/tests/tgsl/wgslGenerator.test.ts | 17 ++++++++++++++++- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/packages/typegpu/src/tgsl/accessIndex.ts b/packages/typegpu/src/tgsl/accessIndex.ts index 6dd00045e5..ad02395293 100644 --- a/packages/typegpu/src/tgsl/accessIndex.ts +++ b/packages/typegpu/src/tgsl/accessIndex.ts @@ -101,12 +101,16 @@ export function accessIndex(target: Snippet, indexArg: Snippet | number): Snippe ); } - if ((isKnownAtComptime(target) && isKnownAtComptime(index)) || target.dataType === UnknownData) { - // No idea what the type is, so we act on the snippet's value and try to guess - return coerceToSnippet( - // oxlint-disable-next-line typescript/no-explicit-any -- we're inspecting the value, and it could be any value - (target.value as any)[index.value as number], - ); + if (isKnownAtComptime(target)) { + if (isKnownAtComptime(index)) { + // No idea what the type is, so we act on the snippet's value and try to guess + return coerceToSnippet( + // oxlint-disable-next-line typescript/no-explicit-any -- we're inspecting the value, and it could be any value + (target.value as any)[index.value as number], + ); + } else { + return undefined; // this will throw an error in wgslGenerator + } } if ( diff --git a/packages/typegpu/src/tgsl/wgslGenerator.ts b/packages/typegpu/src/tgsl/wgslGenerator.ts index fc413e08fe..38d2a2ee44 100644 --- a/packages/typegpu/src/tgsl/wgslGenerator.ts +++ b/packages/typegpu/src/tgsl/wgslGenerator.ts @@ -556,7 +556,7 @@ ${this.ctx.pre}}`; const propertyStr = this.ctx.resolve(property.value, property.dataType).value; throw new Error( - `Unable to index value ${targetStr} of unknown type with index ${propertyStr}. If the value is an array, to address this, consider one of the following approaches: (1) declare the array using 'tgpu.const', (2) store the array in a buffer, or (3) define the array within the GPU function scope.`, + `Index access '${targetStr}[${propertyStr}]' is invalid. If the value is an array, to address this, consider one of the following approaches: (1) declare the array using 'tgpu.const', (2) store the array in a buffer, or (3) define the array within the GPU function scope.`, ); } diff --git a/packages/typegpu/tests/tgsl/wgslGenerator.test.ts b/packages/typegpu/tests/tgsl/wgslGenerator.test.ts index 64189675d7..429cccb7a6 100644 --- a/packages/typegpu/tests/tgsl/wgslGenerator.test.ts +++ b/packages/typegpu/tests/tgsl/wgslGenerator.test.ts @@ -1757,7 +1757,22 @@ describe('wgslGenerator', () => { expect(() => tgpu.resolve([testFn])).toThrowErrorMatchingInlineSnapshot(` [Error: Resolution of the following tree failed: - - - fn:testFn: Value undefined (as json: undefined) is not resolvable to type u32] + - fn:testFn: Index access 'array(9, 8, 7, 6)[i]' is invalid. If the value is an array, to address this, consider one of the following approaches: (1) declare the array using 'tgpu.const', (2) store the array in a buffer, or (3) define the array within the GPU function scope.] + `); + }); + + it('throws a descriptive error when accessing an external object with a runtime known index', () => { + const Boid = d.struct({ 0: d.u32 }); + + const testFn = tgpu.fn([Boid])((b) => { + const i = 0; + const v = b[i]; + }); + + expect(() => tgpu.resolve([testFn])).toThrowErrorMatchingInlineSnapshot(` + [Error: Resolution of the following tree failed: + - + - fn:testFn: Index access 'b[i]' is invalid. If the value is an array, to address this, consider one of the following approaches: (1) declare the array using 'tgpu.const', (2) store the array in a buffer, or (3) define the array within the GPU function scope.] `); }); From 290bb37ac88d33f68c17ec21071dfaddc46de0f0 Mon Sep 17 00:00:00 2001 From: Aleksander Katan <56294622+aleksanderkatan@users.noreply.github.com> Date: Fri, 24 Apr 2026 13:19:55 +0200 Subject: [PATCH 2/5] Update test name --- packages/typegpu/tests/tgsl/wgslGenerator.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/typegpu/tests/tgsl/wgslGenerator.test.ts b/packages/typegpu/tests/tgsl/wgslGenerator.test.ts index 429cccb7a6..e1129649ac 100644 --- a/packages/typegpu/tests/tgsl/wgslGenerator.test.ts +++ b/packages/typegpu/tests/tgsl/wgslGenerator.test.ts @@ -1761,7 +1761,7 @@ describe('wgslGenerator', () => { `); }); - it('throws a descriptive error when accessing an external object with a runtime known index', () => { + it('throws an error when accessing an object with a runtime known index', () => { const Boid = d.struct({ 0: d.u32 }); const testFn = tgpu.fn([Boid])((b) => { From f8bad388bf6918376b18c66c80a246d6fd3ec3ff Mon Sep 17 00:00:00 2001 From: Aleksander Katan <56294622+aleksanderkatan@users.noreply.github.com> Date: Fri, 24 Apr 2026 14:31:10 +0200 Subject: [PATCH 3/5] Update packages/typegpu/src/tgsl/accessIndex.ts --- packages/typegpu/src/tgsl/accessIndex.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/typegpu/src/tgsl/accessIndex.ts b/packages/typegpu/src/tgsl/accessIndex.ts index ad02395293..0e3371205b 100644 --- a/packages/typegpu/src/tgsl/accessIndex.ts +++ b/packages/typegpu/src/tgsl/accessIndex.ts @@ -1,5 +1,5 @@ import { stitch } from '../core/resolve/stitch.ts'; -import { isDisarray, MatrixColumnsAccess, UnknownData } from '../data/dataTypes.ts'; +import { isDisarray, MatrixColumnsAccess } from '../data/dataTypes.ts'; import { derefSnippet } from '../data/ref.ts'; import { isEphemeralSnippet, type Origin, snip, type Snippet } from '../data/snippet.ts'; import { vec2f, vec3f, vec4f } from '../data/vector.ts'; From 501376d8dd2b5348109001b01ab99f246aa5b201 Mon Sep 17 00:00:00 2001 From: Aleksander Katan <56294622+aleksanderkatan@users.noreply.github.com> Date: Fri, 24 Apr 2026 14:55:54 +0200 Subject: [PATCH 4/5] Update packages/typegpu/src/tgsl/accessIndex.ts Co-authored-by: Iwo Plaza --- packages/typegpu/src/tgsl/accessIndex.ts | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/packages/typegpu/src/tgsl/accessIndex.ts b/packages/typegpu/src/tgsl/accessIndex.ts index 0e3371205b..c900df7834 100644 --- a/packages/typegpu/src/tgsl/accessIndex.ts +++ b/packages/typegpu/src/tgsl/accessIndex.ts @@ -101,17 +101,12 @@ export function accessIndex(target: Snippet, indexArg: Snippet | number): Snippe ); } - if (isKnownAtComptime(target)) { - if (isKnownAtComptime(index)) { - // No idea what the type is, so we act on the snippet's value and try to guess - return coerceToSnippet( - // oxlint-disable-next-line typescript/no-explicit-any -- we're inspecting the value, and it could be any value - (target.value as any)[index.value as number], - ); - } else { - return undefined; // this will throw an error in wgslGenerator - } - } + if ((isKnownAtComptime(target) && isKnownAtComptime(index))) { + // No idea what the type is, so we act on the snippet's value and try to guess + return coerceToSnippet( + // oxlint-disable-next-line typescript/no-explicit-any -- we're inspecting the value, and it could be any value + (target.value as any)[index.value as number], + ); if ( isWgslStruct(target.dataType) && From eca918e5477d568441859513c65b7f84310cffe1 Mon Sep 17 00:00:00 2001 From: Aleksander Katan <56294622+aleksanderkatan@users.noreply.github.com> Date: Fri, 24 Apr 2026 15:17:18 +0200 Subject: [PATCH 5/5] Missing } --- packages/typegpu/src/tgsl/accessIndex.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/typegpu/src/tgsl/accessIndex.ts b/packages/typegpu/src/tgsl/accessIndex.ts index c900df7834..811c136d0e 100644 --- a/packages/typegpu/src/tgsl/accessIndex.ts +++ b/packages/typegpu/src/tgsl/accessIndex.ts @@ -101,12 +101,13 @@ export function accessIndex(target: Snippet, indexArg: Snippet | number): Snippe ); } - if ((isKnownAtComptime(target) && isKnownAtComptime(index))) { + if (isKnownAtComptime(target) && isKnownAtComptime(index)) { // No idea what the type is, so we act on the snippet's value and try to guess return coerceToSnippet( // oxlint-disable-next-line typescript/no-explicit-any -- we're inspecting the value, and it could be any value (target.value as any)[index.value as number], ); + } if ( isWgslStruct(target.dataType) &&