Skip to content

-mm=own: yielded values are the caller's, try locals, rest arrays borrow - #705

Merged
ASDAlexander77 merged 1 commit into
mainfrom
own-defaultlib-batch2
Oct 27, 2026
Merged

ASDAlexander77 merged 1 commit into
mainfrom
own-defaultlib-batch2

Conversation

@ASDAlexander77

Copy link
Copy Markdown
Owner

The second batch of -mm=own DefaultLib fixes. With it, every DefaultLib test that isn't marked not own passes under own. Spec sections 48–49, and the user doc's limits section is updated.

Changes

What a generator yields is the caller's (§48). for (const v of g()) a.push(v) and [...s.matchAll(re)] used to fail with borrows a field and cannot be stored. The value is now moved out of the value field of the record that next() returns. Two conditions must hold:

  • one move takes the value: a push, a store into a place or a global, a kept argument, or a store into an owning local;
  • nothing reads the value, the field or the record before that local is released or reassigned.

checkPlaceRead redoes the walk with the move's retain and its taker counted as uses (takenOps). rc's retain goes, and a ts.Default is stored into the field after the taker, so the record's release frees nothing twice. A value that isn't moved, for example one pushed on some paths only, stays in the field and is freed with the record.

Related fixes that unblocked the matchAll tests:

  • Locals in try. MLIRGen declares a local inside a try with no value and fills it with a store right before rc's ts.RetainSlot. That store now counts as the declaration (slotDeclarationStore, declarationRetainOf). try { const r = mk(); } used to fail with takes a second reference.
  • Script globals. A global given its value once in the program's main, when nothing calls main (a script's top level), is now a candidate immortal global. A class's ..new (a regex literal, for example) also counts as making a fresh value.
  • Built arrays into globals. A built array stored into a global now moves in, as one stored into a field already did.

Rest-parameter arrays borrow their elements (§49). a.concat(b) moved b into the array that ...other is given. Under own that array was never released, so b couldn't be used after the call, and the elements leaked with the array. classifyArgumentArrays now treats such an array as borrowing its elements when all of these hold:

  • its one use is an argument to a call that doesn't keep it;
  • the call's result doesn't borrow the array;
  • each element is either not fresh, or fresh and released by its own owner.

rc's references to the elements are dropped. A ts.Release marked OWN_SHALLOW_RELEASE_ATTR_NAME follows the call; it frees the array's data block and header but not the elements (emitReleaseArrayShallow).

Tests

  • own_generator_take and own_rest_array_borrows, under own, rc, gc and none. Each step churns memory, so a value freed too early reads garbage. Own emits no reference counting.
  • Negative test: own_err_generator_take_used_after.
  • Both positive tests fail under own on the compiler without these changes.
  • Full ctest: 0 failures. The own subset: 1567/1567.
  • DefaultLib under own: 161 passed and 11 skipped (marked not own) out of 172, in release and debug, compile and JIT. That was 159 passed with 3 failures before. rc, gc and none are unchanged.

Not in this PR

Array<string> and Array<Shared<T>> still don't compile under own:

  • fill stores a string parameter inside a loop.
  • The class's methods pass callbacks through methods called via the vtable, so own can't tell what those callbacks free.

The dependent DefaultLib PR marks array_reduceRight as not own.

🤖 Generated with Claude Code

- What a generator yields is taken out of the record next() returns (spec
  48). A for...of or spread that keeps the value now moves it out of the
  record's `value` field, and the field is then cleared. This applies when
  the value is moved once and nothing reads it, or the field, before the
  record local ends.
- A local declared in a `try` is now treated as declared by the store
  right before its ts.RetainSlot, so try { const r = mk(); } compiles.
- A global given its value once in the program's `main` is now a
  candidate immortal, as one given its value by a static constructor is.
  A class's ..new also counts as making a fresh value.
- A built array stored into a global moves in.
- The array a rest parameter is given borrows its elements (spec 49).
  rc's references for the elements go. A shallow ts.Release (the new
  OWN_SHALLOW_RELEASE_ATTR_NAME lowering) frees the array after the call
  without its elements. a.concat(b) no longer moves b.

DefaultLib: string_matchAll and string_match_lastIndex now compile and run
under own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ASDAlexander77
ASDAlexander77 merged commit 770203b into main Oct 27, 2026
2 checks passed
@ASDAlexander77
ASDAlexander77 deleted the own-defaultlib-batch2 branch October 27, 2026 23:03
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