Skip to content

A record literal with a run-time field can be assigned to a class type (#487) - #493

Merged
ASDAlexander77 merged 1 commit into
mainfrom
fix-record-to-class
Oct 4, 2026
Merged

ASDAlexander77 merged 1 commit into
mainfrom
fix-record-to-class

Conversation

@ASDAlexander77

Copy link
Copy Markdown
Owner

const p: P = { x: 1, items: [i, 2] } was "invalid cast from {x:s32, items:s32[]} to P". A record with a field known only at run time is a tuple, not a constant tuple, and castTupleLikeVariants reported the tuple-to-class cast as invalid. It now builds the instance from the record's fields with castTupleToClass, as it already did for a constant record. This covers a record passed to a class parameter, returned as a class, or assigned from a record variable (which then shares its arrays with the instance).

A literal assigned to a class is now built with the class's field types (getTypeByFieldNameFromReceiverType), as a literal assigned to a tuple or an interface already was:

  • rc leak: [i, 2] with let i = 7 was an s32[] that the cast copied into the field's number[] and then left with no count, so it was never freed. AOT, 1M iterations: 96.5 MB, now 4.4 MB. Constant records leaked this way on main too.
  • { items: [] }: was an any[] the field could not take ("invalid cast from any[] to number[]").

Under own (agreed): this shape is a borrow error, "'this value' borrows a field and cannot be stored, returned or captured", because the instance reads its fields out of the record's temporary slot. That now includes a constant record assigned to a class, which compiled before because the cast copied its arrays. Filed as #492.

New test 00record_to_class.ts covers:

  • a run-time record, with push;
  • a constant record;
  • an empty array;
  • a record variable, sharing its array;
  • a record passed to a class parameter;
  • a record returned as a class;
  • a fresh instance on each loop pass.
Gate Result
Windows full suite (Release) 3800/3800
Linux (WSL) full suite 3785/3785
DefaultLib suite, release + debug, compile + JIT gc 159/159; rc, none 158/159 (weakref_basic is gc-only, as on main)
own corpus no flips: 459/616 and 457/616 with --opt; the one new failure is the new test (#492)
rc memory, 1M iterations (AOT) constant, run-time, record variable, parameter: flat at 4.3-4.4 MB (constant and run-time were 96.5 MB)

Closes #487

🤖 Generated with Claude Code

#487)

`const p: P = { x: 1, items: [i, 2] }` was "invalid cast from
{x:s32, items:s32[]} to P": a record with a field known only at run
time is a tuple, not a constant tuple, and castTupleLikeVariants
reported the tuple-to-class cast as invalid. It now makes the instance
from the record's fields with castTupleToClass, as it already did for
a constant record. The same goes for a record passed to a class
parameter or returned as a class, and for a record variable assigned to
a class, which shares its arrays with the instance.

A literal assigned to a class is now built with the class's field
types, as a literal assigned to a tuple or an interface already was
(getTypeByFieldNameFromReceiverType):
- `[i, 2]` with `let i = 7` was an `s32[]` that the cast copied into
  the field's `number[]` and left with no count, never freed under rc
  (AOT, 1M iterations: 96.5 MB, now 4.4); constant records leaked so on
  main as well;
- `{ items: [] }` was an `any[]` the field could not take ("invalid
  cast from any[] to number[]").

Under own, the class takes its fields as reads out of the record's
temporary slot, which own reports as borrows ("'this value' borrows a
field and cannot be stored, returned or captured"): for a run-time
record, where it used to report the invalid cast, and now also for a
constant record, which compiled before because its arrays were copied
fresh by the cast. Moving a record literal's fields into the instance
is filed separately.

Closes #487

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ASDAlexander77
ASDAlexander77 merged commit 1bbc1ee into main Oct 4, 2026
2 checks passed
@ASDAlexander77
ASDAlexander77 deleted the fix-record-to-class branch October 4, 2026 18:44
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.

A record literal with a run-time field cannot be assigned to a class type (invalid cast)

1 participant