Core-N0 Ownership Return P1V5 Contract
Status: implemented and validated
Architecture decision:
ADR-0017
Core profile: P1V5
1. Admitted result
An internal function may return:
UniqueRef<T> = Ref<rho, Unique, T>
T = Bool | I64 | F64
No entry function or aggregate result may contain a reference.
2. Liveness anchor
An ownership-returning function must have exactly one parameter containing any
reference. That parameter must be a direct UniqueRef<T> exactly equal to the
result type.
f(owner: UniqueRef<T>, scalar_args...) -> UniqueRef<T>
The anchor proves that rho is active in the caller’s logical store throughout
the call.
3. Return move
return owner
consumes the direct unique local. Returning an already-moved owner is an
OwnershipViolation.
A successful Call binds the returned owner to its ANF result binder. The
source argument remains moved:
let owner2 = call f(owner1)
use owner1 // rejected
use owner2 // valid
4. Success cardinality
One owner enters and one owner returns on every successful value path.
The result may be:
- the original owner;
- a local move of that owner;
- an owner returned by a valid tail call;
- a fresh replacement allocated in the same anchored region.
Typed error, unhandled operation, or evaluation failure returns no owner. Affine dropping is valid.
5. Region safety
- result region and element type equal the anchor;
- the anchored region is active at function entry;
- the anchored region cannot be reopened;
- a different nested region cannot satisfy the result type;
- a function without an anchor cannot return an owner.
P1V5 does not transfer or escape the region itself.
6. Tail return
An ownership-returning function may tail-call another function with the same unique result type. The owner moves into the tail callee, the borrowed invocation retains the logical store, and the final owner returns to the original direct caller.
7. Fail-closed boundary
P1V5 rejects:
- ownership results in P1V0–P1V4;
- entry reference results or parameters;
- shared, tuple, sum, closure, or nested-reference results;
- no anchor, a shared anchor, a mismatched anchor, or multiple reference-bearing parameters;
- use-after-call of the consumed source;
- return-after-move and duplicate call arguments;
- aggregate, closure, handler, operation, or cell containment;
- closed-region and unanchored owner escape.
8. Canonical encoding
Semantic encoding v1 uses:
profile P1V5 = 5
No new type, mutability, rvalue, term, or effect tag is required. Existing P1V0–P1V4 vectors remain unchanged.
The locked P1V5 mutate-and-return vector is:
09006d69756a52fd1fe1dfc36cd198d1a40122a5201fbcfe73ccd6d538a9290a
9. Evidence required
- direct mutate-and-return followed by caller borrow/move;
- consumed caller source rejection;
- local move then return and return-after-move rejection;
- branch-local and tail-call ownership return;
- fresh same-region replacement;
- no/shared/multiple/mismatched anchor negatives;
- entry, aggregate, capture, operation, and closed-region negatives;
- typed error and exact budget behavior;
- deterministic semantic bytes/hash and tamper rejection;
- unchanged P1V0–P1V4 and T2 vectors;
- full workspace, strict lint, formatting, governance, shell, and link gates.
The executable corpus is
core_ownership_return_p1v5.rs.
10. Non-claims
P1V5 is not aggregate ownership, multi-value return, identity-preserving ownership, owner creation without an input anchor, general region transfer, escaping ownership, general borrowing, partial moves, ownership through closures/handlers, P1/P2, native lowering, or dependency closure.