Skip to content

-mm=own: a generator over its this parameter borrows it - #701

Merged
ASDAlexander77 merged 1 commit into
mainfrom
own-array-iterators
Oct 27, 2026
Merged

ASDAlexander77 merged 1 commit into
mainfrom
own-array-iterators

Conversation

@ASDAlexander77

Copy link
Copy Markdown
Owner

Summary

Under -mm=own, an array's a.keys() and a.values() failed with:

'this' is a parameter, kept here, but -mm=own cannot make the callers of '___Array.keys' give it up

Both are generators whose first parameter is this (function* keys<T>(this: T[])).

The cause is in the generator lowering. buildGeneratorWrapperDeclaration gives next the body's this through an alias, const .this = this, because next has a this of its own.

  • For a method, that is the class's this, which the state holds by value.
  • For a this parameter, the alias was a separate local initialised from the parameter and captured into the state's box as a cell. That cell is not the parameter's, so the state owned a copy of the caller's array.

The fix, in two parts:

  • When this is a parameter, the alias is now a ConstRef, so the name is the parameter's storage, as for-in's .a is its array's.
  • ConstRefs are normally never captured (setIgnoreCapturing, for .a); .this now is. So next captures the parameter's ts.Param itself, and the state borrows this like any other parameter (spec §19):
    • it is bounded by this
    • it is walked while the array lives
    • it can only be returned by a function that says @borrows("this")

Under the other models the alias was a second reference to the same array; now it is the parameter's own cell.

Spec §40 is new, and docs/own-memory-model.md is updated.

Tests

  • New own_generator_this_param, run under own, rc, gc and none with churn between steps:

    • an extension generator over this: C[] in a for...of
    • a generic one held in a local
    • one returned by a @borrows("this") function
    • one over a temporary array in the statement that made it

    Without the change it fails: @borrows("this") is refused because nothing the function returns borrows. AOT under own: 200000 iterations peak at 4.4 MB, against 4.3 MB at 1000.

  • New negative own_err_generator_this_param_returned: the generator returned by a function without @borrows("this").

  • Full ctest -C Release: 5892 passed, 0 failed.

  • DefaultLib suites, with the companion @borrows("this") on Array's iterator():

    • rc, gc and none pass every test.
    • own goes from 142 to 145/170 (array_keys, array_values, array_iterator), with no new failures.

Not here: entries(), whose yield [i, this[i]] puts a borrowed element into a tuple the caller owns.

🤖 Generated with Claude Code

`a.keys()` of an array was "'this' is a parameter, kept here": the generator lowering gives `next`
the body's `this` through `const .this = this`, and for a `this` parameter that alias was a local of
its own, captured as a cell - so the state owned a copy of the caller's array. The alias is now a
ConstRef to the parameter's storage (captured, unlike for-in's `.a`), so the closure captures the
parameter and the state borrows it like any other (spec 19). Spec section 40.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ASDAlexander77
ASDAlexander77 merged commit 26a08c1 into main Oct 27, 2026
2 checks passed
@ASDAlexander77
ASDAlexander77 deleted the own-array-iterators branch October 27, 2026 17:42
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