Eliminate the Implicit ownership default — totality minus borrow-returns #55
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Supersedes #54, which patched the leak on the return position only. The
real problem is not the leak; it is that an ownership default
(
ParamMode::Implicit) exists at all and is read two ways — thetypechecker treats
Implicit == Own(mode_eq,crates/ailang-core/src/ast.rs), while codegen omits the caller
decforit (
drop.rs~476), so the same fn-type means two different things. Seedesign/models/0008-ownership-totality.md, sections 1-2.
Direction
Delete
ImplicitfromParamModein a single cutover —ParamModebecomes
{Own, Borrow}— so no defaulted position survives anywhere.This is the totality result of section 2, scoped to what is provably
shippable today by carving out exactly the one piece the 2026-06-01
design review found unsound.
What ships
ParamMode::Implicitdeleted. One canonical-JSON hash reset, once,for the whole corpus (the hash-pin tests in
ailang-core/tests/hash_pin.rsandailang-surface/tests/prelude_module_hash_pin.rsreset together). Thisis the single irreversible step (section 6), signed off deliberately.
own/borrow. The existing uniqueness/consume analysis alreadyverifies these (
consume-while-borrowed,over-strict-mode, per-paramconsume_count) and can derive the correct mode for the migration.Value-type positions: trivial-
own;(borrow (con Int))is a checkerror (section 3.2).
own+ a first-order return-provenance check.An
ownreturn must be a fresh allocation or an owned-binder transfer— not an alias of a non-owned binder. This rejects the
fn f(x: (borrow T)) -> T { x }passthrough that is silently acceptedtoday (no return check exists).
borrowreturns are a check error in this cut, with a diagnosticnaming the liveness/escape gap. Re-enabling them is separate later work
behind the escape/liveness axis.
Why this is sound (addresses the 2026-06-01 review)
The review found full totality not atomically shippable because of
borrow-returns — they are refcount-invisible and can outlive their
source (sections 1, 8). This proposal forbids borrow-returns, so it
never enters that gap. And the first-order return-provenance check
suffices without the section-5.2 reachability pass, because
consume-while-borrowedalready forbids a borrowed value from escapinginto a returned constructor (constructor args are
Position::Consume,linearity.rs:527): regime A (section 5.1) holds as a consequence of theexisting parameter checks, so a compound
ownreturn cannot carry aborrowed field.
Why this is coherent (where #54 was not)
Implicitis removed entirely — no default remains on any position.Forbidding
borrowon the return position is not a surviving default; itis a position-dependent mode restriction, exactly like the
already-accepted "value-type positions forbid
borrow" rule(section 3.2). Section 2 forbids defaults, not position restrictions.
#54 left a default standing (parameters = Implicit); this does not.
The cut, stated plainly
#54 took too little (returns only; the default stays on parameters; two
hash resets). Full totality takes too much (borrow-returns need the
unbuilt liveness axis). The right cut is everything except
borrow-returns.
Next step
Hard-gate: a brainstorm spec under docs/specs/ before any plan or code.
Questions to settle there: keyword form (
own/borrowvs. adjectivalowned/borrowed, sections 3.4 / 7-Q3) and whether the shared type isrenamed off
ParamMode(section 3.4) — both should land before thecutover, since renaming after the hash reset would be a second
irreversible step. Multi-return (sections 5.3 / 7-Q4) stays out of scope.
Blocked on #56. De-risking the cutover revealed that deleting
Implicituniversal-activates the strict linearity check, which currently misfires on value-type and HOF params (~21% of fixtures, all false positives). The analysis must be hardened (value-types exempt from consume-tracking; function application = borrow) before this cutover can ship. Spec 0062 stands but is known-incomplete on that axis until #56 lands.Blocked: executing the cutover (plan 0121, Task 4) revealed that universal linearity activation is NOT clean after #56 — at least three more false-positive classes plus one genuine corpus over-consume surface (full diagnosis in #57). The irreversible variant deletion was correctly stopped before it ran; main is untouched. Tasks 1 (borrow-return reject,
e1c9089) and 3 (throwaway migration tool,39b674c) landed and are green. #55 is blocked on #57 (the #56 part 2 hardening) before the cutover can proceed.