Skip to content

fix: convert array elements when casting between array types - #3302

Open
DevShoaib78 wants to merge 1 commit into
software-mansion:mainfrom
DevShoaib78:fix/array-to-array-conversion
Open

DevShoaib78 wants to merge 1 commit into
software-mansion:mainfrom
DevShoaib78:fix/array-to-array-conversion

Conversation

@DevShoaib78

Copy link
Copy Markdown

Fixes #3251. Also covers the case in #2521.

Any two array schemas counted as the same type in getAutoConversionRank, so both snippets in the issue silently generated invalid WGSL. Arrays now get a cast when the lengths differ or the elements need one, the same way structs do.

What it generates now

const arr = [1.5, 2.5, 3.5];
const b = d.arrayOf(d.u32, 3)(arr);
// let b = array<u32, 3>(u32(arr[0]), u32(arr[1]), u32(arr[2]));

Arrays of different structs convert element by element too, as in #2521.

Decisions to check

  • Different lengths throw, since the issue doesn't say what should happen and I can't see a sensible conversion: Cannot auto-convert array ... because their element counts differ.
  • A source with possible side effects throws and asks to store it in a variable first, like the struct cast, because it would otherwise be evaluated once per element.
  • Array literals stay ArrayExpressions, so their elements can still be known at comptime. The nested array test in comptime.test.ts needs this.
  • Runtime-sized arrays (element count 0) are left as they were.
  • Two separate schemas of the same array type still don't cast.
  • The cast logs the existing implicit-conversion warning, even for an explicit d.arrayOf(...)(arr). Struct casts do the same.

Testing

Added 5 tests to array.test.ts. 4 of them fail without the fix; the fifth pins that identical schemas don't cast. tsc, oxlint and oxfmt are clean on the changed files.

On Windows, vitest run --project=!browser has the same 20 failures with and without this change, in tgpu-gen, unplugin-typegpu and the docs probability example, and nothing new. I did not run the built-package tests or the circular dependency check (dpdm found no entry files on Windows), and I only checked the generated WGSL text, not on a real GPU.

Any two array schemas counted as the same type, so a cast between different element types or lengths silently generated invalid WGSL. Convert element by element, keep array literals as array expressions, and throw when the lengths differ.
Copilot AI balanced review requested due to automatic review settings October 31, 2026 21:02

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

This PR introduces a circular dependency. dpdm reports one cycle on this branch and none on main, so the required test:circular-deps job (.github/workflows/test-misc.yml) will fail.

Reviewed changes

  • Array element-wise casting — needsArrayCast now flags two WgslArray schemas as needing a cast when their element counts differ or their element types need conversion, so mismatched arrays are no longer silently treated as the same type.
  • Cast codegen — the case 'cast' branch in applyActionToSnippet resolves an array→array cast to array<T,N>(elem0, elem1, …), converting each element; array literals stay ArrayExpressions to preserve comptime values.
  • Tests — five cases added to array.test.ts (variable→array element conversion, struct-element conversion, same-type no-cast, length mismatch throws, side-effecting source throws). I confirmed four of them fail on the pre-fix conversion.ts and that the rest of packages/typegpu/tests (3085 tests) and the docs example snapshots still pass; tsc, oxlint, and oxfmt are clean on the two files.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏

import { logger } from '../tgpuLogger.ts';
import type { ResolutionCtx } from '../types.ts';
import { accessStructProp } from './accessStructProp.ts';
import { ArrayExpression } from './generationHelpers.ts';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new import edge closes a dependency cycle and fails the repo's test:circular-deps gate. dpdm exits 0 on main but exits 1 on this branch, reporting:

core/function/createCallableSchema.ts
  -> tgsl/conversion.ts
  -> tgsl/generationHelpers.ts
  -> data/numeric.ts
  -> core/function/createCallableSchema.ts
Technical details
# Move `ArrayExpression` out of `generationHelpers.ts` to break the cycle

## Affected sites
- `packages/typegpu/src/tgsl/conversion.ts:30` — new `import { ArrayExpression } from './generationHelpers.ts'`.
- `packages/typegpu/src/tgsl/generationHelpers.ts:2` — imports `../data/numeric.ts`, which imports `../core/function/createCallableSchema.ts`, which imports `./conversion.ts` (closing the loop).

## Required outcome
- The import graph reachable from `packages/**/src/index.ts` must be acyclic (the repo enforces this with `pnpm test:circular-deps` in `.github/workflows/test-misc.yml` and in the `prepack` script).

## Suggested approach
- Move the `ArrayExpression` class (or just an `isArrayExpression` guard) into a leaf module that does not transitively reach `data/numeric.ts` / `createCallableSchema.ts` (its dependencies would be `data/snippet.ts`, `data/wgslTypes.ts`, `shared/symbols.ts`, `errors.ts`, `types.ts`), and import it from both `generationHelpers.ts` and `conversion.ts`.
- `conversion.ts`'s existing imports do not reach `createCallableSchema` (base `12f5cba` is cycle-free), so a genuinely leaf module breaks the loop.
- Verify with `pnpm test:circular-deps`.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Array conversion has critical gaps: nested-array inference depends on row order and can truncate fractional values, while aliases bypass required element casts and length checks.

2 open issues
What changed in this PR

This PR updates TypeGPU’s array conversions to address invalid WGSL reported in #3251 and #2521.

Changes:

  • Adds element-by-element casts between array schemas, including arrays of different structs.
  • Rejects mismatched lengths and conversions that would repeat possible side effects, while preserving compile-time array literals.
  • Adds five regression tests covering conversions, errors, and equivalent schemas.
File Description
packages/​typegpu/​tests/​array.test.ts Adds array-conversion regression tests.
packages/​typegpu/​src/​tgsl/​conversion.ts Adds array-cast detection, element conversion, and validation.

Review effort: Balanced

Comment on lines +65 to +66
if (isWgslArray(trueSrc) && isWgslArray(trueDst) && needsArrayCast(trueSrc, trueDst)) {
return { rank: 1, action: 'cast', targetType: dest };
Comment on lines +65 to +66
if (isWgslArray(trueSrc) && isWgslArray(trueDst) && needsArrayCast(trueSrc, trueDst)) {
return { rank: 1, action: 'cast', targetType: dest };

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: Array to array conversion

2 participants