-mm=own, phase 4: function signatures and any - #406
Merged
Merged
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
OwnershipSignaturePass (new, own only, before inference) resolves every call it can - direct, a virtual call whose family is all private and defined here, the .instanceOf slot - and pins __own_no_drops where no candidate may destroy what its caller can reach (least fixpoint; a constructor filling its own object is not a drop). The inference's call rule asks it first. A call given a borrow now uses it for as long as the callee runs: phase 3 skipped a call's own arguments, so f(h.c) with f resetting h.c through a global and reading its parameter compiled. The function value an indirect call goes through, and the object a field-held method is bound to, are read as the call starts. The helpers both passes need move to OwnershipFacts.h. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… call OwnershipSignaturePass gives a function __own_result_borrows = K when every heap value it returns is, through views, opaque casts, ts.Unbox, field and element reads, the object a method is called on and results that borrow, parameter K (never assigned) - and only in a closed world: the function is private, every use of its symbol is a call this pass resolves or a class vtable entry, and every class family it is in agrees. Otherwise it keeps rc's convention, and the error in its body says why (__own_facts_lost). A fixpoint lets a method that returns another's borrow borrow too. The inference treats such a call as a borrow read (wildcard places, roots through the argument): its temporary release and its consuming let's releases go, and a function returning a borrow drops the retain rc made for its caller. Found on the way: getSymbolUses(module) does not look inside the module (it is a symbol table), so the closed world saw no uses at all. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…; any Owned-by-callee parameters (__own_params): a parameter (never assigned) that the body stores into a field, element or global, pushes, or passes to a kept parameter, under the same closed world and family meet as borrowed results. The callee's retain goes, and the move must reach every return, not loop, and be the last use. The caller's call is a taker of each kept argument: out of a fresh value, an owning let or a kept parameter; anything else is an error. Found by own_param_kept: a move whose acquisition is the call erased the call. any: the boxing cast carries its birth reference (__owned_result + retain), as a printed number does. That fixes rc's leak of every box no let holds, and under own makes the box a fresh value with one owner. The catch copy thunk's store takes that reference over (the catch slot is runtime memory), and a thunk's read out of the exception object is a move: nothing gives it back. ___unbox gets a borrowed result through ts.Unbox, and its .instanceOf call is drop-free, so <C>a borrows the payload for as long as the box lives. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Spec section 15: the signature pass and its closed world, what the inference accepts, measured numbers (own flat at 4.8 MB where none climbs to 1.6 GB), teeth, tests and the corpus (272 -> 303 of 564, none lost), and the known limits: export/import of facts deferred, parameters kept on some paths only, wildcard places for borrowed results, interface calls unresolved. Found on the way and left for their own work: rc never releases a getter's result used as a temporary (12.6 MB in own_getter_borrow), and under every model <B>anyValue segfaults when B implements an interface, since mlirGenInstanceOfOpaque calls vtable slot 0 as .instanceOf. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GCC rejects a default argument that value-initializes a nested struct with default member initializers inside the enclosing class (walkBorrowed's Keeping). The struct is a plain aggregate now, and the default is spelled out. 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 4 of
-mm=own(spec §7): function signatures andany. Plan:docs/superpowers/plans/2026-09-29-own-phase-4.md. Results: spec §15.What changes
OwnershipSignaturePass(new module pass, own only, just before inference). It resolves each call it can: direct calls, virtual calls whose family is all private and defined here, and the.instanceOfslot. It computes three facts per function and pins on each call the facts its candidates agree on:__own_no_drops: the callee destroys nothing its caller can reach.__own_result_borrows = K: the result is a borrow of argumentK. Getters,return this.c, a returned parameter,___unbox.__own_params: parameters the callee keeps, by storing or pushing them.`${this.color} area=${this.area()}`compiles;let's releases erased;f(h.c)wherefresetsh.cthrough a global, then reads its parameter.any(this touches MLIRGen, so it changes rc and gc too):const, or passed straight to a call, was never released;<C>aborrows the payload through___unbox.Verification
test-jit-rc-owned-asyncandtest-jit-none-owned-async: the~AsyncTokenassert;ownership-verifier-4: the linknameDictionaryAttrassert, which also fires under gc.nonereaches 559–1658 MB. rc reads 12.6 MB onown_getter_borrow(see below).own_param_keptfails;own_getter_borrowandown_any_boxfail.churn(), and was checked under gc first.Rulings
<C>umoveu.export_class_abstract*files from §14.4 therefore stay rejected: their class is exported, soareamay be overridden elsewhere.Found, not fixed here
h.cc.x): 12.6 MB against own's 4.8 MB.<B>anyValuesegfaults whenBimplements an interface.mlirGenInstanceOfOpaquecalls vtable slot 0 as.instanceOf, but such a class keeps the interface's vtable there. The fix changes the vtable layout.🤖 Generated with Claude Code