typegpu 0.12.6 → 0.12.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/common/writeSoA.js +16 -0
  2. package/core/buffer/buffer.js +47 -11
  3. package/core/commandEncoder/attachments.js +5 -2
  4. package/core/function/autoIO.js +4 -2
  5. package/core/function/ioSchema.d.ts +7 -2
  6. package/core/function/ioSchema.js +41 -15
  7. package/core/function/tgpuFragmentFn.d.ts +1 -1
  8. package/core/function/tgpuFragmentFn.js +2 -1
  9. package/core/function/tgpuVertexFn.js +2 -1
  10. package/core/pipeline/connectAttachmentToShader.d.ts +2 -2
  11. package/core/pipeline/connectAttachmentToShader.js +16 -21
  12. package/core/pipeline/connectTargetsToShader.d.ts +5 -1
  13. package/core/pipeline/connectTargetsToShader.js +23 -18
  14. package/core/pipeline/renderPipeline.d.ts +5 -4
  15. package/core/pipeline/renderPipeline.js +10 -12
  16. package/core/resolve/namespace.js +2 -2
  17. package/core/root/rootTypes.d.ts +6 -2
  18. package/core/slot/accessor.d.ts +1 -1
  19. package/core/slot/accessor.js +72 -5
  20. package/core/texture/texture.js +8 -4
  21. package/data/autoStruct.d.ts +1 -1
  22. package/data/autoStruct.js +8 -4
  23. package/data/deepEqual.js +19 -9
  24. package/data/matrix.js +10 -3
  25. package/data/partialIO.d.ts +8 -1
  26. package/data/partialIO.js +36 -19
  27. package/data/ref.js +2 -2
  28. package/data/snippet.d.ts +6 -4
  29. package/data/snippet.js +8 -4
  30. package/data/wgslTypes.d.ts +3 -0
  31. package/internal.d.ts +1 -0
  32. package/nameUtils.d.ts +7 -0
  33. package/nameUtils.js +60 -0
  34. package/package.json +1 -1
  35. package/shared/meta.js +1 -1
  36. package/shared/symbols.js +1 -1
  37. package/shared/tseynit.js +54 -50
  38. package/std/boolean.js +9 -10
  39. package/std/operators.js +9 -4
  40. package/std/texture.d.ts +2 -2
  41. package/tgsl/accessIndex.js +19 -23
  42. package/tgsl/forOfUtils.js +6 -6
  43. package/tgsl/generationHelpers.js +2 -2
  44. package/tgsl/wgslGenerator.js +85 -23
package/shared/tseynit.js CHANGED
@@ -2,90 +2,94 @@ import * as tinyest from 'tinyest';
2
2
  const { NodeTypeCatalog: NODE } = tinyest;
3
3
  export function stringifyNode(node) {
4
4
  if (isExpression(node)) {
5
- return stringifyExpression(node, '');
5
+ return stringifyExpression(node);
6
6
  }
7
7
  return stringifyStatement(node, '');
8
8
  }
9
- function stringifyStatement(node, ident) {
9
+ function stringifyStatement(node, indent) {
10
10
  if (isExpression(node)) {
11
- return `${ident}${stringifyExpression(node, ident)};`;
11
+ return `${indent}${stringifyExpression(node)};`;
12
12
  }
13
13
  if (node[0] === NODE.block) {
14
- const statements = node[1].map((n) => stringifyStatement(n, ident + ' '));
15
- return `{\n${statements.join('\n')}\n${ident}}`;
14
+ const statements = node[1].map((n) => stringifyStatement(n, indent + ' '));
15
+ return `{\n${statements.join('\n')}\n${indent}}`;
16
16
  }
17
17
  if (node[0] === NODE.return) {
18
- const expr = node[1] === undefined ? '' : ` ${stringifyExpression(node[1], '')}`;
19
- return `${ident}return${expr};`;
18
+ const expr = node[1] === undefined ? '' : ` ${stringifyExpression(node[1])}`;
19
+ return `${indent}return${expr};`;
20
20
  }
21
21
  if (node[0] === NODE.if) {
22
- const cond = stringifyExpression(node[1], ident);
23
- const then = stringifyStatement(node[2], ident);
24
- const base = `${ident}if (${cond}) ${then}`;
22
+ const cond = stringifyExpression(node[1]);
23
+ const then = stringifyStatement(node[2], indent);
24
+ const base = `${indent}if (${cond}) ${then}`;
25
25
  if (node[3] !== undefined) {
26
- return `${base} else ${stringifyStatement(node[3], ident)}`;
26
+ return `${base} else ${stringifyStatement(node[3], indent)}`;
27
27
  }
28
28
  return base;
29
29
  }
30
30
  if (node[0] === NODE.let) {
31
- const decl = `${ident}let ${stringifyExpression(node[1], ident)}`;
31
+ const decl = `${indent}let ${stringifyExpression(node[1])}`;
32
32
  if (node[2] !== undefined) {
33
- return `${decl} = ${stringifyExpression(node[2], ident)};`;
33
+ return `${decl} = ${stringifyExpression(node[2])};`;
34
34
  }
35
35
  return `${decl};`;
36
36
  }
37
37
  if (node[0] === NODE.const) {
38
- const decl = `${ident}const ${stringifyExpression(node[1], ident)}`;
38
+ const decl = `${indent}const ${stringifyExpression(node[1])}`;
39
39
  if (node[2] !== undefined) {
40
- return `${decl} = ${stringifyExpression(node[2], ident)};`;
40
+ return `${decl} = ${stringifyExpression(node[2])};`;
41
41
  }
42
42
  return `${decl};`;
43
43
  }
44
44
  if (node[0] === NODE.for) {
45
45
  const init = node[1] ? stringifyStatement(node[1], '') : ';';
46
- const cond = node[2] ? stringifyExpression(node[2], ident) : '';
46
+ const cond = node[2] ? stringifyExpression(node[2]) : '';
47
47
  const update = node[3] ? stringifyStatement(node[3], '') : '';
48
- const body = stringifyStatement(node[4], ident);
49
- return `${ident}for (${init} ${cond}; ${update.slice(0, -1) /* trim the ';' */}) ${body}`;
48
+ const body = stringifyStatement(node[4], indent);
49
+ return `${indent}for (${init} ${cond}; ${update.slice(0, -1) /* trim the ';' */}) ${body}`;
50
50
  }
51
51
  if (node[0] === NODE.while) {
52
- const cond = stringifyExpression(node[1], ident);
53
- const body = stringifyStatement(node[2], ident);
54
- return `${ident}while (${cond}) ${body}`;
52
+ const cond = stringifyExpression(node[1]);
53
+ const body = stringifyStatement(node[2], indent);
54
+ return `${indent}while (${cond}) ${body}`;
55
55
  }
56
56
  if (node[0] === NODE.continue) {
57
- return `${ident}continue;`;
57
+ return `${indent}continue;`;
58
58
  }
59
59
  if (node[0] === NODE.break) {
60
- return `${ident}break;`;
60
+ return `${indent}break;`;
61
61
  }
62
62
  if (node[0] === NODE.forOf) {
63
63
  const leftKind = node[1][0] === NODE.const ? 'const' : 'let';
64
- const leftName = stringifyExpression(node[1][1], ident);
65
- const right = stringifyExpression(node[2], ident);
66
- const body = stringifyStatement(node[3], ident);
67
- return `${ident}for (${leftKind} ${leftName} of ${right}) ${body}`;
64
+ const leftName = stringifyExpression(node[1][1]);
65
+ const right = stringifyExpression(node[2]);
66
+ const body = stringifyStatement(node[3], indent);
67
+ return `${indent}for (${leftKind} ${leftName} of ${right}) ${body}`;
68
68
  }
69
69
  if (node[0] === NODE.switch) {
70
- const discriminant = stringifyExpression(node[1], ident);
70
+ const discriminant = stringifyExpression(node[1]);
71
71
  const cases = node[2].map((c) => {
72
- const test = c[0] === null ? 'default' : `case ${stringifyExpression(c[0], ident)}`;
72
+ const test = c[0] === null ? 'default' : `case ${stringifyExpression(c[0])}`;
73
73
  if (c[1].length === 0) {
74
74
  return `${test}:`;
75
75
  }
76
- const consequents = c[1].map((s) => stringifyStatement(s, ident + ' '));
76
+ const consequents = c[1].map((s) => stringifyStatement(s, indent + ' '));
77
77
  return `${test}:\n${consequents.join('\n')}`;
78
78
  });
79
- return `${ident}switch (${discriminant}) {\n${cases.map((c) => `${ident + ' '}${c}`).join('\n')}\n${ident}}`;
79
+ return `${indent}switch (${discriminant}) {\n${cases.map((c) => `${indent + ' '}${c}`).join('\n')}\n${indent}}`;
80
80
  }
81
81
  assertExhaustive(node);
82
82
  }
83
+ const JS_IDENTIFIER = /^[$_\p{ID_Start}][$_\u{200c}\u{200d}\p{ID_Continue}]*$/u;
84
+ function stringifyObjectPropertyKey(key) {
85
+ return JS_IDENTIFIER.test(key) ? key : JSON.stringify(key);
86
+ }
83
87
  export function stringifyObjectProperty([key, value, computed]) {
84
- const keyStr = computed ? `[${stringifyExpression(key, '')}]` : stringifyExpression(key, '');
85
- const valueStr = stringifyExpression(value, '');
88
+ const keyStr = computed ? `[${stringifyExpression(key)}]` : stringifyObjectPropertyKey(key);
89
+ const valueStr = stringifyExpression(value);
86
90
  return `${keyStr}: ${valueStr}`;
87
91
  }
88
- function stringifyExpression(node, ident) {
92
+ function stringifyExpression(node) {
89
93
  if (typeof node === 'string') {
90
94
  return node;
91
95
  }
@@ -105,52 +109,52 @@ function stringifyExpression(node, ident) {
105
109
  return JSON.stringify(node[1]);
106
110
  }
107
111
  if (node[0] === NODE.arrayExpr) {
108
- const elements = node[1].map((n) => stringifyExpression(n, ident));
112
+ const elements = node[1].map((n) => stringifyExpression(n));
109
113
  return `[${elements.join(', ')}]`;
110
114
  }
111
115
  if (node[0] === NODE.binaryExpr) {
112
- return `${wrapIfComplex(node[1], ident)} ${node[2]} ${wrapIfComplex(node[3], ident)}`;
116
+ return `${wrapIfComplex(node[1])} ${node[2]} ${wrapIfComplex(node[3])}`;
113
117
  }
114
118
  if (node[0] === NODE.assignmentExpr) {
115
- return `${stringifyExpression(node[1], ident)} ${node[2]} ${stringifyExpression(node[3], ident)}`;
119
+ return `${stringifyExpression(node[1])} ${node[2]} ${stringifyExpression(node[3])}`;
116
120
  }
117
121
  if (node[0] === NODE.logicalExpr) {
118
- return `${wrapIfComplex(node[1], ident)} ${node[2]} ${wrapIfComplex(node[3], ident)}`;
122
+ return `${wrapIfComplex(node[1])} ${node[2]} ${wrapIfComplex(node[3])}`;
119
123
  }
120
124
  if (node[0] === NODE.unaryExpr) {
121
125
  // Unary word operators like void, instanceof and delete require a space
122
126
  const sep = node[1].length > 1 ? ' ' : '';
123
- return `${node[1]}${sep}${wrapIfComplex(node[2], ident)}`;
127
+ return `${node[1]}${sep}${wrapIfComplex(node[2])}`;
124
128
  }
125
129
  if (node[0] === NODE.call) {
126
- const callee = wrapIfComplex(node[1], ident);
127
- const args = node[2].map((n) => stringifyExpression(n, ident)).join(', ');
130
+ const callee = wrapIfComplex(node[1]);
131
+ const args = node[2].map((n) => stringifyExpression(n)).join(', ');
128
132
  return `${callee}(${args})`;
129
133
  }
130
134
  if (node[0] === NODE.memberAccess) {
131
135
  if (Array.isArray(node[1]) && node[1][0] === NODE.numericLiteral) {
132
- return `(${stringifyExpression(node[1], ident)}).${stringifyExpression(node[2], ident)}`;
136
+ return `(${stringifyExpression(node[1])}).${stringifyExpression(node[2])}`;
133
137
  }
134
- return `${wrapIfComplex(node[1], ident)}.${stringifyExpression(node[2], ident)}`;
138
+ return `${wrapIfComplex(node[1])}.${stringifyExpression(node[2])}`;
135
139
  }
136
140
  if (node[0] === NODE.indexAccess) {
137
- return `${wrapIfComplex(node[1], ident)}[${stringifyExpression(node[2], ident)}]`;
141
+ return `${wrapIfComplex(node[1])}[${stringifyExpression(node[2])}]`;
138
142
  }
139
143
  if (node[0] === NODE.preUpdate) {
140
- return `${node[1]}${wrapIfComplex(node[2], ident)}`;
144
+ return `${node[1]}${wrapIfComplex(node[2])}`;
141
145
  }
142
146
  if (node[0] === NODE.postUpdate) {
143
- return `${wrapIfComplex(node[2], ident)}${node[1]}`;
147
+ return `${wrapIfComplex(node[2])}${node[1]}`;
144
148
  }
145
149
  if (node[0] === NODE.objectExpr) {
146
150
  if (Array.isArray(node[1])) {
147
151
  return `{ ${node[1].map(stringifyObjectProperty).join(', ')} }`;
148
152
  }
149
- const entries = Object.entries(node[1]).map(([key, val]) => `${key}: ${stringifyExpression(val, ident)}`);
153
+ const entries = Object.entries(node[1]).map(([key, val]) => `${stringifyObjectPropertyKey(key)}: ${stringifyExpression(val)}`);
150
154
  return `{ ${entries.join(', ')} }`;
151
155
  }
152
156
  if (node[0] === NODE.conditionalExpr) {
153
- return `${wrapIfComplex(node[1], ident)} ? ${wrapIfComplex(node[2], ident)} : ${wrapIfComplex(node[3], ident)}`;
157
+ return `${wrapIfComplex(node[1])} ? ${wrapIfComplex(node[2])} : ${wrapIfComplex(node[3])}`;
154
158
  }
155
159
  if (node[0] === NODE.nullLiteral) {
156
160
  return 'null';
@@ -199,8 +203,8 @@ const SIMPLE_NODES = [
199
203
  /**
200
204
  * Stringifies expression, and wraps it in parentheses if they cannot be trivially omitted
201
205
  */
202
- function wrapIfComplex(node, ident) {
203
- const s = stringifyExpression(node, ident);
206
+ function wrapIfComplex(node) {
207
+ const s = stringifyExpression(node);
204
208
  if (typeof node === 'string' || typeof node === 'boolean') {
205
209
  return s;
206
210
  }
package/std/boolean.js CHANGED
@@ -9,6 +9,7 @@ import { isBool, isVecBool, isVecInstance, } from "../data/wgslTypes.js";
9
9
  import { SignatureNotSupportedError, WgslTypeError } from "../errors.js";
10
10
  import { unify } from "../tgsl/conversion.js";
11
11
  import { cpuCopy } from "./copy.js";
12
+ import { coerceToSnippet } from "../tgsl/generationHelpers.js";
12
13
  function correspondingBooleanVectorSchema(dataType) {
13
14
  if (dataType.type.includes('2')) {
14
15
  return vec2b;
@@ -30,7 +31,7 @@ export const allEq = dualImpl({
30
31
  name: 'allEq',
31
32
  signature: (...argTypes) => ({ argTypes, returnType: bool }),
32
33
  normalImpl: (lhs, rhs) => cpuAll(cpuEq(lhs, rhs)),
33
- codegenImpl: (_ctx, [lhs, rhs]) => stitch `all(${lhs} == ${rhs})`,
34
+ codegenImpl: (ctx, [lhs, rhs]) => stitch `all(${ctx.gen.emitBinaryOp(lhs, '==', rhs)})`,
34
35
  sideEffects: false,
35
36
  });
36
37
  const cpuEq = (lhs, rhs) => {
@@ -54,7 +55,7 @@ export const eq = dualImpl({
54
55
  returnType: correspondingBooleanVectorSchema(argTypes[0]),
55
56
  }),
56
57
  normalImpl: cpuEq,
57
- codegenImpl: (_ctx, [lhs, rhs]) => stitch `(${lhs} == ${rhs})`,
58
+ codegenImpl: (ctx, [lhs, rhs]) => ctx.gen.emitBinaryOp(lhs, '==', rhs),
58
59
  sideEffects: false,
59
60
  });
60
61
  /**
@@ -72,7 +73,7 @@ export const ne = dualImpl({
72
73
  returnType: correspondingBooleanVectorSchema(argTypes[0]),
73
74
  }),
74
75
  normalImpl: (lhs, rhs) => cpuNot(cpuEq(lhs, rhs)),
75
- codegenImpl: (_ctx, [lhs, rhs]) => stitch `(${lhs} != ${rhs})`,
76
+ codegenImpl: (ctx, [lhs, rhs]) => ctx.gen.emitBinaryOp(lhs, '!=', rhs),
76
77
  sideEffects: false,
77
78
  });
78
79
  const cpuLt = (lhs, rhs) => {
@@ -95,7 +96,7 @@ export const lt = dualImpl({
95
96
  returnType: correspondingBooleanVectorSchema(argTypes[0]),
96
97
  }),
97
98
  normalImpl: cpuLt,
98
- codegenImpl: (_ctx, [lhs, rhs]) => stitch `(${lhs} < ${rhs})`,
99
+ codegenImpl: (ctx, [lhs, rhs]) => ctx.gen.emitBinaryOp(lhs, '<', rhs),
99
100
  sideEffects: false,
100
101
  });
101
102
  /**
@@ -113,7 +114,7 @@ export const le = dualImpl({
113
114
  returnType: correspondingBooleanVectorSchema(argTypes[0]),
114
115
  }),
115
116
  normalImpl: (lhs, rhs) => cpuOr(cpuLt(lhs, rhs), cpuEq(lhs, rhs)),
116
- codegenImpl: (_ctx, [lhs, rhs]) => stitch `(${lhs} <= ${rhs})`,
117
+ codegenImpl: (ctx, [lhs, rhs]) => ctx.gen.emitBinaryOp(lhs, '<=', rhs),
117
118
  sideEffects: false,
118
119
  });
119
120
  /**
@@ -131,7 +132,7 @@ export const gt = dualImpl({
131
132
  returnType: correspondingBooleanVectorSchema(argTypes[0]),
132
133
  }),
133
134
  normalImpl: (lhs, rhs) => cpuAnd(cpuNot(cpuLt(lhs, rhs)), cpuNot(cpuEq(lhs, rhs))),
134
- codegenImpl: (_ctx, [lhs, rhs]) => stitch `(${lhs} > ${rhs})`,
135
+ codegenImpl: (ctx, [lhs, rhs]) => ctx.gen.emitBinaryOp(lhs, '>', rhs),
135
136
  sideEffects: false,
136
137
  });
137
138
  /**
@@ -149,7 +150,7 @@ export const ge = dualImpl({
149
150
  returnType: correspondingBooleanVectorSchema(argTypes[0]),
150
151
  }),
151
152
  normalImpl: (lhs, rhs) => cpuNot(cpuLt(lhs, rhs)),
152
- codegenImpl: (_ctx, [lhs, rhs]) => stitch `(${lhs} >= ${rhs})`,
153
+ codegenImpl: (ctx, [lhs, rhs]) => ctx.gen.emitBinaryOp(lhs, '>=', rhs),
153
154
  sideEffects: false,
154
155
  });
155
156
  function cpuNot(value) {
@@ -274,9 +275,7 @@ export const isCloseTo = dualImpl({
274
275
  return stitch `(abs(f32(${lhs}) - f32(${rhs})) <= ${precision})`;
275
276
  }
276
277
  if (!isSnippetNumeric(lhs) && !isSnippetNumeric(rhs)) {
277
- // https://www.w3.org/TR/WGSL/#vector-multi-component:~:text=Binary%20arithmetic%20expressions%20with%20mixed%20scalar%20and%20vector%20operands
278
- // (a-a)+prec creates a vector of a.length elements, all equal to prec
279
- return stitch `all(abs(${lhs} - ${rhs}) <= (${lhs} - ${lhs}) + ${precision})`;
278
+ return stitch `all(abs(${lhs} - ${rhs}) <= ${_ctx.gen.typeInstantiation(lhs.dataType, [coerceToSnippet(precision)])})`;
280
279
  }
281
280
  return 'false';
282
281
  },
package/std/operators.js CHANGED
@@ -160,10 +160,15 @@ function cpuNeg(value) {
160
160
  }
161
161
  export const neg = dualImpl({
162
162
  name: 'neg',
163
- signature: (arg) => ({
164
- argTypes: [arg],
165
- returnType: arg,
166
- }),
163
+ signature: (arg) => {
164
+ if (getPrimitive(arg) === u32) {
165
+ throw new WgslTypeError(`Unsupported signature. Unary operator - cannot be applied to argument of kind '${arg.type}'.`);
166
+ }
167
+ return {
168
+ argTypes: [arg],
169
+ returnType: arg,
170
+ };
171
+ },
167
172
  normalImpl: cpuNeg,
168
173
  codegenImpl: (_ctx, [arg]) => stitch `-(${arg})`,
169
174
  sideEffects: false,
package/std/texture.d.ts CHANGED
@@ -61,8 +61,8 @@ declare function textureStoreCpu<T extends textureStorage3d>(texture: T, coords:
61
61
  export declare const textureStore: import("../types.ts").DualFn<typeof textureStoreCpu>;
62
62
  declare function textureDimensionsCpu<T extends texture1d | textureStorage1d>(texture: T): number;
63
63
  declare function textureDimensionsCpu<T extends texture1d>(texture: T, level: number): number;
64
- declare function textureDimensionsCpu<T extends texture2d | texture2dArray | textureCube | textureCubeArray | textureStorage2d | textureStorage2dArray | textureExternal>(texture: T): v2u;
65
- declare function textureDimensionsCpu<T extends texture2d | texture2dArray | textureCube | textureCubeArray>(texture: T, level: number): v2u;
64
+ declare function textureDimensionsCpu<T extends texture2d | texture2dArray | textureCube | textureCubeArray | textureMultisampled2d | textureDepth2d | textureDepth2dArray | textureDepthCube | textureDepthCubeArray | textureDepthMultisampled2d | textureStorage2d | textureStorage2dArray | textureExternal>(texture: T): v2u;
65
+ declare function textureDimensionsCpu<T extends texture2d | texture2dArray | textureCube | textureCubeArray | textureDepth2d | textureDepth2dArray | textureDepthCube | textureDepthCubeArray>(texture: T, level: number): v2u;
66
66
  declare function textureDimensionsCpu<T extends texture3d | textureStorage3d>(texture: T): v3u;
67
67
  declare function textureDimensionsCpu<T extends texture3d>(texture: T, level: number): v3u;
68
68
  export declare const textureDimensions: import("../types.ts").DualFn<typeof textureDimensionsCpu>;
@@ -12,33 +12,31 @@ const indexableTypeToResult = {
12
12
  mat3x3f: vec3f,
13
13
  mat4x4f: vec4f,
14
14
  };
15
+ function deriveIndexAccessOrigin(targetOrigin, indexOrigin) {
16
+ if (targetOrigin === 'constant-immutable-def') {
17
+ // Constant refs stay const unless the index forces runtime materialization
18
+ return indexOrigin === 'constant' || indexOrigin === 'constant-immutable-def'
19
+ ? 'constant-immutable-def'
20
+ : 'runtime-immutable-def';
21
+ }
22
+ if (targetOrigin === 'constant') {
23
+ // Ephemeral constants indexed with constants stay constant, otherwise they become runtime-known
24
+ return indexOrigin === 'constant' || indexOrigin === 'constant-immutable-def'
25
+ ? 'constant'
26
+ : 'runtime';
27
+ }
28
+ // Fallthrough
29
+ return targetOrigin;
30
+ }
15
31
  export function accessIndex(target, indexArg) {
16
32
  const index = typeof indexArg === 'number' ? coerceToSnippet(indexArg) : indexArg;
17
33
  // array
18
34
  if (isWgslArray(target.dataType) || isDisarray(target.dataType)) {
19
35
  const elementType = target.dataType.elementType;
20
- let origin;
21
- if (target.origin === 'constant-immutable-def') {
22
- // Constant refs stay const unless the element/index forces runtime materialization
23
- origin =
24
- index.origin === 'constant' || index.origin === 'constant-immutable-def'
25
- ? 'constant-immutable-def'
26
- : 'runtime-immutable-def';
27
- }
28
- else if (target.origin === 'constant') {
29
- // Ephemeral constants indexed with constants stay constant, otherwise they become runtime-known
30
- origin =
31
- index.origin === 'constant' || index.origin === 'constant-immutable-def'
32
- ? 'constant'
33
- : 'runtime';
34
- }
35
- else {
36
- // Fallthrough
37
- origin = target.origin;
38
- }
39
36
  if (target.value instanceof ArrayExpression && isKnownAtComptime(index)) {
40
37
  return target.value.elements[index.value];
41
38
  }
39
+ const origin = deriveIndexAccessOrigin(target.origin, index.origin);
42
40
  return snip(isKnownAtComptime(target) && isKnownAtComptime(index)
43
41
  ? // oxlint-disable-next-line typescript/no-explicit-any -- it's fine, it's there
44
42
  target.value[index.value]
@@ -50,8 +48,7 @@ export function accessIndex(target, indexArg) {
50
48
  return snip(isKnownAtComptime(target) && isKnownAtComptime(index)
51
49
  ? // oxlint-disable-next-line typescript/no-explicit-any -- it's fine, it's there
52
50
  target.value[index.value]
53
- : stitch `${target}[${index}]`, target.dataType.primitive,
54
- /* origin */ target.origin, target.possibleSideEffects || index.possibleSideEffects);
51
+ : stitch `${target}[${index}]`, target.dataType.primitive, deriveIndexAccessOrigin(target.origin, index.origin), target.possibleSideEffects || index.possibleSideEffects);
55
52
  }
56
53
  if (isPtr(target.dataType)) {
57
54
  // Sometimes values that are typed as pointers aren't instances of `d.ref`, so we
@@ -61,8 +58,7 @@ export function accessIndex(target, indexArg) {
61
58
  // matrix.columns
62
59
  if (target.value instanceof MatrixColumnsAccess) {
63
60
  const propType = indexableTypeToResult[target.value.matrix.dataType.type];
64
- return snip(stitch `${target.value.matrix}[${index}]`, propType,
65
- /* origin */ target.origin, target.possibleSideEffects || index.possibleSideEffects);
61
+ return snip(stitch `${target.value.matrix}[${index}]`, propType, deriveIndexAccessOrigin(target.origin, index.origin), target.possibleSideEffects || index.possibleSideEffects);
66
62
  }
67
63
  // matrix
68
64
  if (target.dataType.type in indexableTypeToResult) {
@@ -1,5 +1,5 @@
1
1
  import { UnknownData } from "../data/dataTypes.js";
2
- import { isAlias, snip } from "../data/snippet.js";
2
+ import { isAddressableOrigin, isStoredInMemory, snip } from "../data/snippet.js";
3
3
  import { stitch } from "../core/resolve/stitch.js";
4
4
  import * as wgsl from "../data/wgslTypes.js";
5
5
  import { i32, u32 } from "../data/numeric.js";
@@ -31,10 +31,7 @@ export function getElementType(elementSnippet, iterableSnippet) {
31
31
  throw new WgslTypeError(stitch `The elements in iterable ${iterableSnippet} are of unknown type`);
32
32
  }
33
33
  if (wgsl.isNaturallyEphemeral(elementSnippet.dataType) ||
34
- elementSnippet.origin === 'runtime' ||
35
- elementSnippet.origin === 'constant' ||
36
- elementSnippet.origin === 'constant-immutable-def' ||
37
- elementSnippet.origin === 'runtime-immutable-def') {
34
+ !isAddressableOrigin(elementSnippet.origin)) {
38
35
  return elementType;
39
36
  }
40
37
  if (!wgsl.isPtr(elementType)) {
@@ -56,7 +53,10 @@ export function getRangeSnippets(ctx, iterableSnippet, unroll = false) {
56
53
  comparison: step < 0 ? '>' : '<',
57
54
  };
58
55
  }
59
- if (!unroll && !isAlias(iterableSnippet)) {
56
+ if (iterableSnippet.possibleSideEffects) {
57
+ throw new Error('`for ... of ...` loops do not support iterables with possible side effects. Store the iterable in a variable first.');
58
+ }
59
+ if (!unroll && !isStoredInMemory(iterableSnippet)) {
60
60
  throw new Error(`\`for ... of ...\` loops only support std.range or iterables stored in variables.
61
61
  -----
62
62
  You can wrap iterable with \`tgpu.unroll(...)\`. If iterable is known at comptime, the loop will be unrolled.
@@ -1,7 +1,7 @@
1
1
  import { UnknownData } from "../data/dataTypes.js";
2
2
  import { abstractFloat, abstractInt, bool, f32, i32 } from "../data/numeric.js";
3
3
  import { isRef } from "../data/ref.js";
4
- import { isAlias, isSnippet, snip, withDataType } from "../data/snippet.js";
4
+ import { isStoredInMemory, isSnippet, snip, withDataType } from "../data/snippet.js";
5
5
  import { isMatInstance, isNaturallyEphemeral, isVecInstance, WORKAROUND_getSchema, } from "../data/wgslTypes.js";
6
6
  import { getOwnSnippet } from "../types.js";
7
7
  import { WgslTypeError } from "../errors.js";
@@ -81,7 +81,7 @@ export class ArrayExpression {
81
81
  [$resolve](ctx) {
82
82
  for (const elem of this.elements) {
83
83
  // We check if there are no references among the elements
84
- if (isAlias(elem) && !isNaturallyEphemeral(elem.dataType)) {
84
+ if (isStoredInMemory(elem) && !isNaturallyEphemeral(elem.dataType)) {
85
85
  const snippetStr = ctx.resolveSnippet(elem).value;
86
86
  const snippetType = ctx.resolve(concretize(elem.dataType)).value;
87
87
  throw new WgslTypeError(`'${snippetStr}' reference cannot be used in an array constructor.\n-----\nTry '${snippetType}(${snippetStr})' or 'arrayOf(${snippetType}, count)([...])' to copy the value instead.\n-----`);