Skip to content

-mm=own: untyped literals given later, fields taken out of temporaries; rc branch-temporary fix - #724

Merged
ASDAlexander77 merged 2 commits into
mainfrom
own-design-open
Oct 28, 2026
Merged

ASDAlexander77 merged 2 commits into
mainfrom
own-design-open

Conversation

@ASDAlexander77

@ASDAlexander77 ASDAlexander77 commented Oct 28, 2026 •

Copy link
Copy Markdown
Owner

The two -mm=own items left open after #723, plus an rc bug found on the way.

An untyped object literal given later to a function-typed field (own spec §62). const o = { w: (x: number) => x * 2 }; apply(o); o.w(1) was rejected under own: the literal's arrow was a method bound to the literal, and reading it into apply's field handed out the literal. Arrows in a literal given to a variable declared with no type are now closures, as #722 made them for literals with an object-type receiver, when they say no this and name no local around them. An arrow that may capture a local stays a method: copied out of the literal by a cast to an interface, its captures would still be the literal's. A literal returned or given with no receiver keeps its methods, which an interface's vtable takes (00interface_type_tag_in_vtable).

A field taken out of a temporary in a branch (own spec §63). let result = initial ?? next(xs, 0).value (DefaultLib's reduce shape) was rejected: the branch reads the field of next's result, and the result is released before the branch ends. When rc retains the read right after it, and the result is released after that and read nowhere else, own now moves the value out: the retain goes, and an empty value is stored into the field before the release. String fields too.

rc: a field read out of a temporary in one branch of ??/?: was freed with it. const item = given ?? pair(n).item read 0 under rc, and with let the program died: nothing retained the field before the temporary's release inside the branch. Only an any field, whose other branch boxed a fresh value, happened to be retained. consumeConditionalResults now has such a branch retain what it yields, so the ts.If carries the reference.

any to an interface (every model). <Adder>x of an any holding an object literal read the literal's block as the interface's (this, vtable) pair and crashed. The box says only that it holds a literal (typeof 'object' or 'tuple'), not which fields, so no vtable can be laid out: the cast now throws. An interface value made of a literal before boxing still round-trips. A boxed null cast to an interface was non-null under rc, none and own; it is now the null interface.

Tests (own, rc, gc, none): own_literal_variable_closures, own_temporary_field_take, 00any_literal_to_interface. Teeth: without the empty-value store, the own build of own_temporary_field_take fails.

Validated on Windows: ctest 6203/6203; DefaultLib release passes under gc (175), rc and none (174 + 1 gc-only skip), own (164 + 11 not-own skips).

Still not covered under own: a literal whose arrows capture a local, given later ({ w: x => x * k }); a field of a named class local (const r = next(); initial ?? r.value); a parameter on the other branch (given ?? pair(n).item, kept).

🤖 Generated with Claude Code

ASDAlexander77 and others added 2 commits October 28, 2026 23:07
…aries

An object literal a variable is declared with, with no type, makes its
this-free arrows closures when they name no local around them (spec 62):
`const o = { w: (x: number) => x * 2 }; apply(o); o.w(1)` compiles under
own. MLIRGen marks such a literal UntypedVariableInitializer. A literal
returned or given with no receiver keeps its methods, which an interface's
vtable takes, and so does an arrow that may capture a local.

A field read out of a call's own result in one branch of `??` or `?:`,
retained right after the read and the result released after that, is a
move out of the field under own (spec 63): `let result = initial ??
next(xs, 0).value`. The retain goes, and an empty value is stored into the
field before the release.

rc: a branch that yields a field read out of a temporary it made now
retains it before the temporary goes (consumeConditionalResults). Before,
`const item = given ?? pair(n).item` read freed memory, and with `let`
the program died.

Tests: own_literal_variable_closures, own_temporary_field_take (own, rc,
gc, none).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… null for null

An object literal boxed as it is (typeof 'object' or 'tuple') was read as
the interface's (this, vtable) pair, and the call through it crashed under
every model. The box says only that it holds a literal, not which fields,
so no vtable can be laid out: ___unbox_interface now throws "Can't cast
from any: an object literal has no vtable for interface ...". An interface
value made of a literal before it was boxed still round-trips.

A boxed null (or undefined) cast to an interface was not null under rc,
none and own, where it is a box of its own; it now gives the null
interface.

Test: 00any_literal_to_interface.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ASDAlexander77
ASDAlexander77 merged commit a9e1301 into main Oct 28, 2026
2 checks passed
@ASDAlexander77
ASDAlexander77 deleted the own-design-open branch October 28, 2026 23:47
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.

1 participant