Skip to content

fix: The expected type of an expression leaks into call arguments #3097

Description

@iwoplaza

Problem

When the generator knows what type an expression should have, it stores it in ctx.expectedType. Object and array literals use it to pick their struct/array type.

That value is never cleared for sub-expressions, so it also reaches places it was not meant for, for example the arguments of a function call:

const S = d.struct({ a: d.f32 });

const helper = (o: { b: number }) => {
  'use gpu';
  return S({ a: o.b });
};

const main = tgpu.fn([], S)(() => {
  'use gpu';
  return helper({ b: 1 });
});

tgpu.resolve([main]);
// Error: Missing property a in object literal for struct S

The { b: 1 } argument is checked against the return type of main (S), not against anything related to helper. The error message is confusing. If the shapes happen to match, the literal silently becomes an S.

Code: packages/typegpu/src/tgsl/wgslGenerator.ts. _typedExpression sets ctx.expectedType, and _expression reads it in three places (logical expressions, object literals, array literals), but nothing resets it for the children. Call arguments are generated with plain this._expression(arg) while the outer type is still set.

Proposed solution

Make the expected type apply only to the expression it was set for:

  • At the start of _expression, read ctx.expectedType into a local variable and set ctx.expectedType = undefined, so child expressions start with no expected type. Restore it at the end.
  • The three places that read the expected type use the local variable.
  • Where passing the type down is intended, pass it explicitly. This covers the two branches of a ternary (c ? {…} : {…} inside a typed context).

_typedExpression stays the only way to set an expected type, so all existing typed contexts (struct fields, array items, return in shelled functions, strict function signatures) keep working.

Effect on resolution time

None. This is a correctness fix. With the fix applied, a synthetic shader with 1600 unrolled iterations resolved in 55–57 ms (median of 20), the same as main (56–57 ms) within noise.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions