Repository navigation
-mm=own, phase 2: borrows for locals - #402
Merged
Merged
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Verdicts run in the spec's order: move, then borrow, then error. The phase-1 move check now runs quietly first; when it fails and the receiver is an owning `let` that took its own reference, it borrows: its retain and every release of its slot go, and the owner keeps its own. That holds while no use of the borrower can run after a release or an assignment of the owner, and while nothing it holds is stored, returned, retained or assigned - each broken rule has its own error, naming both variables under --di. Five phase-1 negatives are now legal and are replaced by positives: an inner `let` of a value used again, a `let` in a loop of a value made outside it, a `let` on one branch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Where an owning local has more than one receiver, each must be a `let` that borrows it: a borrow pins its owner, so none of them may move it. `let b = a; let c = a;` is now legal; a field store beside a borrow reports "'a' is moved here while 'b' borrows it". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Spec section 13: the borrow rule as built, the several-receivers rule, 1M-iteration numbers (own 4.7-4.8 MB like rc; none up to 1.1 GB), the teeth run (8/8 borrow checks fail with the borrower's releases kept), the tests that changed, and phase 3's inputs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e borrower too A borrow checked the borrower's reads and the casts of them, but not what those reads produce: a bound method (`const f = b.m`), a field reference that a for...of reads on every iteration, a catch or finally clause's non-owning local. Each of those still points into the borrowed block, and four programs compiled into reads of freed memory when the owner was overwritten. Every use of anything derived from a read that can point into the block is now a use of the borrower; the walk stops at numbers and booleans, which also ends a false escape on `if (b)` for a nullable borrower. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phase 2 of
-mm=own(spec §2.2 verdict 2). The plan istslang/docs/superpowers/plans/2026-09-29-own-phase-2.mdand the results are in spec §13. OnlyOwnershipInferencePass.cppchanged, and gc, rc and none are unaffected.What changes
The verdicts now run in the spec's order: move, then borrow, then error.
letdeclared from it borrows it: an owningletthat took its own reference, not captured. The borrower's retain and all its releases are erased, and the owner keeps its own.let, or assigned.letis'a' is moved here while 'b' borrows it.letof a value used again;let b = a; let c = a;;letin a loop body of a value made outside the loop;leton one branch only.--di:'b' borrows 'a' but is used here after 'a' is released or overwritten;... cannot be stored, returned or captured;... cannot be assigned.Tests
own_borrow_inner_let,own_borrow_loop,own_borrow_branch,own_borrow_two_lets,own_borrow_nullable. Each runs under JIT and AOT with achurn()read, plus the no-counting check.own_err_borrow_escape,own_err_borrow_and_move,own_err_move_some_paths_field, and four from the review:own_err_borrow_bound_method,_catch_local,_finally_local,_derived_ref.Verification
owned-asyncandownership-verifier-4were excluded; they fail on main too.-O3and-O1.const f = b.m, afor…ofoverb.v, and a catch local ofbcompiled to reads of freed memory.if (b)on a nullable borrower was a false escape.Still rejected (spec §13.5, input to phase 3)
arr[i],h.c), bounded bypop/splice/overwrite;this(phase 4);An alias of the owner itself held across an assignment of it compiles to a use-after-free under rc as well (§12.5).
🤖 Generated with Claude Code