Skip to content

-mm=own phase 5a: a closure that does not escape borrows what it captures - #436

Merged
ASDAlexander77 merged 4 commits into
mainfrom
own-phase-5
Oct 2, 2026
Merged

ASDAlexander77 merged 4 commits into
mainfrom
own-phase-5

Conversation

@ASDAlexander77

Copy link
Copy Markdown
Owner

Phase 5a of -mm=own (spec §2.5, §4.2 step 6; plan docs/superpowers/plans/2026-10-02-own-phase-5.md; results in spec §16).

Before this, every closure that captured a variable was the error "closures that capture a variable are not supported by -mm=own yet". Now a closure that does not escape borrows what it captures:

  • The frame keeps its cells. rc's ts.RetainCell and the box's ts.Retain of each copy go. The closure is marked __own_borrows_captures, and its box's routine frees the box and nothing it holds (tsfrecb_).
  • Extent. Nothing that may run the closure (a call given it, a call through it, a devirtualized call through its box) may come after anything it borrows is given back: an owned cell's ts.ReleaseCell, or a copy owner's releases and takers. A copy of a container read is bounded by the read's own check, which now sees the closure's uses.
  • Captured parameters and this. Their cell borrows the caller's value. Its ts.ReleaseCell is marked __own_cell_borrows and frees the cell only. Assigning the variable is an error, in the frame or in a closure body. The signature pass computes the closure-body side (__own_assigns_captures), following nested closures over the same cell.
  • Drops. Assigning a captured variable (frame or closure body) drops borrows read out of it, in both passes. A call given a closure value or a box drops every chain when its callee may drop.
  • A closure that escapes (returned, stored, kept) is a closure that captures a variable and escapes here is not supported by -mm=own yet. That is 5b. A closure over copies only keeps phase 4's behaviour: its box owns them.

Found on the way, also fixed: a closure held by a local that owns nothing (let f: () => number; f = () => ...) lost its only release. rc's birth Retain read as the store's acquisition, so the box leaked. The closure now keeps its release, and calls through the alias must come before it.

MLIRGen does not change. gc/rc/none output does not change: the lowering changes are all under MemoryModelOwn.

Verification

  • Release suite: 3360 of 3361 pass. The one failure is test-compile-gc-defaultlib-collector: LNK2005 on Error..size etc. against the installed default library at I:\tslang (built 2026-09-28, before DefaultLib [Question] Speed difference #20, which needs a rebuild). It is a gc-only test, and this branch touches no gc code path.
  • WSL/GCC: builds, and 357 of 357 own tests pass.
  • Corpus under -mm=own --no-default-lib: 310 → 319 of 591, none lost. The 9 new ones pass under test-runner -mm=own, AOT and JIT. Their JIT output matches gc.
  • measure.ps1 on own_closure_borrow: gc 6.1, rc 4.3, none 1879.7, own 4.3 MB.
  • Teeth: with the owning routine used for a borrowing box, own_closure_borrow crashes under AOT (0xC0000005). With __own_cell_borrows ignored, it crashes too (0xC0000374). Each new negative compiles with its rule switched off.

Tests

  • Positive: own_closure_borrow (13 shapes), JIT, AOT and no-counting.
  • Negatives: own_err_closure (now: escapes), own_err_closure_outlives_cell, own_err_closure_copy_moved, own_err_closure_param_assigned, own_err_closure_assigns_param, own_err_cell_assigned_in_frame, own_err_cell_assigned_in_closure, own_err_closure_assigns_captured_field.

🤖 Generated with Claude Code

ASDAlexander77 and others added 4 commits October 2, 2026 09:46
…ures

The frame keeps its cells, the box frees itself only (__own_borrows_captures), and
nothing that may run the closure comes after anything it borrows is given back. A
captured parameter's cell borrows its value (__own_cell_borrows) and cannot be
assigned, in the frame or in a closure body (__own_assigns_captures, from the
signature pass). An assignment to a captured variable is a drop, and a call given a
closure may run it. A closure over a variable that escapes is still an error (5b).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ree would hit

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

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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