Skip to content

-mm=own: Array<string>, Array<Shared<T>>, f && f(x), captured callbacks - #706

Merged
ASDAlexander77 merged 3 commits into
mainfrom
own-defaultlib-batch3
Oct 28, 2026
Merged

ASDAlexander77 merged 3 commits into
mainfrom
own-defaultlib-batch3

Conversation

@ASDAlexander77

Copy link
Copy Markdown
Owner

The third batch of -mm=own DefaultLib fixes, specs 50-54. With it, the Array<T> class works under own for strings and Shared<T> handles. Callbacks also compile in more shapes: tested with &&, captured by a closure, or given a second name. The user doc's limits section is updated.

Changes

Array<string> (§50). fill stores its value parameter into each element in a loop, so a move of the parameter failed with "moved inside a loop".

  • A string stored by a statement that may run again is no longer a keep. The parameter stays a borrow, and each store takes a copy.
  • A string or a handle read out of a place and stored into a place rc does not retain it for now gets its own copy (ts.StringCopy) or count (ts.Retain). This covers if (f(v)) yield v in filter2, where the value goes into the yield's record.
  • A handle given to a callback that may release things gets a count for the call: ts.Retain before it, ts.Release after it.

Array<Shared<T>> (§51). Every method that hands this.data to a walking function failed with "may be released or overwritten". The walk's for...of local is a counted copy of an element, and its release counted as a drop of anything.

  • A local declared from a counted read and never assigned (isCountedReadSlot) is not a drop. The source still holds a count while it lives.
  • ArraySplice items are counted receivers, as push's are.

Callbacks that assign captured variables (§52).

  • A slot whose type owns no block (a captured number, t += v.n) releases nothing.
  • A captured string assigned frees that string only (OWN_CAPTURED_STRING_POSITION). It drops only a borrow that may itself be a string.
  • A call given a closure it does not keep is now judged by its callees' place drops.

f && f(x) (§53). With compare?: (x, y) => number, compare && compare(a, b) failed in every model with "invalid cast". A function is never falsy, so the false branch is now undefined, as in TypeScript (mlirGenAndOrLogic).

Captured and renamed callbacks (§54). A callback parameter captured by a closure that only calls it (a.forEach((v) => f(v))) now keeps its family.

  • The closure's reads of the cell out of its capture box resolve through the parameter's family (capturedCallbacks, resolveCallback).
  • This works whether the closure is bound to a box or called by name with it. Without the default library, the built-in forEach is inlined and calls it by name.
  • const g = f; g(v) resolves as well.

Tests

  • New positive tests, each run under own, rc, gc and none with churn between steps:
    • own_string_copies
    • own_shared_counted_reads
    • own_captured_string_callback
    • own_callback_and_call
    • own_callback_captured
  • Negative tests: own_err_captured_string_drops and own_err_callback_captured_drops.
  • Each positive test fails under own on the compiler without these changes.
  • Full ctest: 6020 tests. The only failure was own_callback_captured, which a later commit fixed: without the default library, the built-in forEach is inlined. After the fix, the own and Shared subsets pass 1968 of 1968.
  • DefaultLib, with the dependent PR's two new tests (array_class_string, array_class_shared), out of 174:
Model Passed Skipped
own 163 11 (marked not own)
rc, none 173 1
gc 174 0

These hold in release and debug, compiled and under the JIT.

Not in this PR

These callbacks still keep their families open:

  • a closure held in a local and called (const call = (v) => f(v); call(v));
  • a callback stored into a field;
  • a callback that is returned.

🤖 Generated with Claude Code

ASDAlexander77 and others added 3 commits October 27, 2026 23:31
… in callbacks

- A string stored by a statement that may run again (fill's loop) is
  not a move of the parameter: each store takes a copy (spec 50).
- A string or a handle read out of a place and stored into a place rc
  does not retain it for (a yield's record, `if (c) yield v`) gets a copy
  or a count of its own; a handle given to a callback that may release
  things gets a count for the call (spec 50).
- A for...of local over handles, declared from a counted read and never
  assigned, is not a drop when released: the array still holds a count
  (spec 51). ArraySplice items are counted receivers, as push's are.
- A callback that assigns a captured string frees only that string
  (OWN_CAPTURED_STRING_POSITION); a call given a closure it does not keep
  is judged by its callees' place drops (spec 52).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- `f && f(x)` of an optional function is `undefined | T` in every model:
  a function is never falsy, so the false branch is `undefined`, not the
  function (spec 53). It used to fail to compile.
- A slot whose type owns no block (a captured number assigned in a
  callback) releases nothing: no drop (spec 52).
- A callback parameter captured by a closure that only calls it
  (`a.forEach((v) => f(v))`) keeps its family: the closure's reads of the
  cell out of its box resolve through the parameter's family, whether the
  closure is bound or, inlined, called by name (spec 54).
- Spec sections 50-54, and the user doc's limits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Without the default library the built-in forEach is inlined, and the test
passed on the compiler without spec 52.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ASDAlexander77
ASDAlexander77 merged commit b799812 into main Oct 28, 2026
2 checks passed
@ASDAlexander77
ASDAlexander77 deleted the own-defaultlib-batch3 branch October 28, 2026 00:25
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