a84ba596cf
Executable projection of spec 0062 (#55) into four tasks: (1) the borrow-return reject, (2) the borrow-over-value reject, (3) a throwaway `ail migrate-modes` pass (parse -> Implicit->Own, keep explicit own/borrow -> print explicit), (4) the atomic cutover (run the migration over the corpus + hand-fix the read-only-heap params the now-universal linearity check flags, delete the variant + all compile sites compiler-driven, parser bare-slot reject, reset the hash pins, flip the leak pin, update both contracts, drop the throwaway). Placeholder-free; all seven surface fixtures parse-gated against HEAD. Spec 0062 marked approved (user, 2026-06-01) and its soundness re-validated against post-#56 HEAD: the three own-return-provenance / regime-A fixtures still exit 1 under [consume-while-borrowed]; #56's application-is-a-borrow change does not weaken them (none is an application of a borrowed binder). plan-recon folded four spec-enumeration corrections into the plan: - kernel migration target is raw_buf/source.ail, not the retired kernel_stub/source.ail the spec cited; - FIVE hash pins shift, not the two the spec named (embed_export_hash, eq_ord_e2e, mono_hash_stability are the missed twins); - design/contracts/0002-data-model.md also documents the "implicit" JSON form and is drift-anchored -> updates alongside 0008; - real counts are 261 fn-type fixtures (spec ~208) and 17 fn_implicit references (spec 16); the compiler is the authoritative enumerator. refs #55
495 lines
20 KiB
Markdown
495 lines
20 KiB
Markdown
# Eliminate the Implicit ownership default — Design Spec
|
||
|
||
**Date:** 2026-06-01
|
||
**Status:** Approved (user, 2026-06-01). Soundness re-validated against
|
||
post-#56 HEAD: the three own-return-provenance / regime-A fixtures still
|
||
exit 1 under `[consume-while-borrowed]` — #56's application-is-a-borrow
|
||
change does not weaken them (none is an application of a borrowed binder;
|
||
all are return/let/ctor consume positions, untouched by #56).
|
||
**Authors:** orchestrator + Claude
|
||
|
||
## Goal
|
||
|
||
Delete `ParamMode::Implicit` from the language in a single cutover.
|
||
`ParamMode` becomes binary `{Own, Borrow}`. After this cycle no
|
||
fn-type slot — parameter or return, authored or compiler-synthesised
|
||
— carries a defaulted ownership mode. Every authored slot reads
|
||
`(own T)` or `(borrow T)`; every compiler-synthesised fn-type carries
|
||
a concrete `Own`.
|
||
|
||
This closes #55 and supersedes #54. It also fixes the typed-MIR
|
||
fieldtest leak (spec 0061): the leak is the observable symptom of the
|
||
real defect, which is that `Implicit` is read two ways — the
|
||
typechecker treats `Implicit ≡ Own` (`mode_eq`,
|
||
`crates/ailang-core/src/ast.rs:894`), while codegen omits the caller
|
||
`dec` for it (`crates/ailang-codegen/src/drop.rs:476`), so the same
|
||
fn-type means two different things. Removing the variant removes the
|
||
fork.
|
||
|
||
The design rationale is `design/models/0008-ownership-totality.md`
|
||
§§1–3 (the totality principle: a default cannot be half-removed) and
|
||
the 2026-06-01 design review (full totality is not atomically
|
||
shippable *because of* borrow-returns; this spec carves them out). The
|
||
model is `status: Design exploration` — its claims are validated
|
||
against the live tool by this spec, not assumed.
|
||
|
||
### What the grounding work changed in this spec
|
||
|
||
Both #55 and the first draft of this spec assumed a **new
|
||
return-provenance check** was needed to reject an `own` return that
|
||
aliases a borrowed binder (the `fn f(x: (borrow T)) -> (own T) { x }`
|
||
passthrough), and that this passthrough is "silently accepted today".
|
||
Running the candidate fixtures through the live `ail check` (traces in
|
||
the Testing section) **falsified that assumption**: the existing
|
||
`consume-while-borrowed` analysis already rejects all three provenance
|
||
paths —
|
||
|
||
- direct passthrough `(body b)` → rejected today;
|
||
- let-indirection `(body (let y b y))` → rejected today;
|
||
- borrow escaping into a returned constructor `(body (term-ctor Wrap Wrap b))`
|
||
(regime A, model §5.1) → rejected today.
|
||
|
||
A borrow parameter holds a live borrow for the whole function body, so
|
||
*any* use of it at a consume position (own-return, ctor arg, own-param
|
||
arg) trips `consume-while-borrowed`. **Own-return soundness and regime
|
||
A are therefore already-green properties, not new work.** The cutover
|
||
adds exactly **one** new check (borrow-return rejection) plus one
|
||
signature-level reject (borrow-over-value), not two new provenance
|
||
analyses. This is the central correction the grounding pass produced.
|
||
|
||
### Scope decisions ratified here
|
||
|
||
1. **Keyword form stays verbal** (`own` / `borrow`, today's surface).
|
||
No adjectival rename, no keyword churn. The internal type keeps the
|
||
name `ParamMode`. *(User decision, 2026-06-01.)*
|
||
|
||
2. **Totality is strict, including value-type slots.** Every fn-type
|
||
slot carries `(own T)` or `(borrow T)`, value types included.
|
||
`(own (con Int))` is trivial-own (no inc/dec ever emitted);
|
||
`(borrow (con Int))` is a check error (§3.2 of the model). Bare type
|
||
in a fn-type slot is rejected by the parser everywhere. Rationale:
|
||
this gives an LLM author a heap/value-agnostic rule — *always write
|
||
a mode; `own` is always safe; reach for `borrow` only when you want
|
||
a heap view* — so the author never needs to know which types are
|
||
boxed. The alternative (bare = trivial-own for value types only)
|
||
forces exactly that heap/value classification on every slot and is
|
||
rejected.
|
||
|
||
### Out of scope
|
||
|
||
- **Re-enabling borrow-returns.** `(ret (borrow T))` is a check error
|
||
in this cut, with a diagnostic naming the liveness/escape gap. The
|
||
escape/liveness axis that would make borrow-returns sound is separate
|
||
later work (model §5.2, §8).
|
||
- **Multi-return** (model §5.3, §7-Q4).
|
||
- **Mode polymorphism** (model §4). A type-variable slot takes a
|
||
concrete `own`/`borrow`; the surface already cannot express a mode
|
||
variable, so no change is needed.
|
||
|
||
## Architecture
|
||
|
||
The cutover has four schema/representation edits, one new check, one
|
||
new signature-level reject, and one corpus migration. They land
|
||
together: a deleted `Implicit` means returns must be `own`/`borrow`,
|
||
every checked-in `.ail` source carries `Implicit` slots today, and the
|
||
hash shift is one-time for the whole corpus.
|
||
|
||
**Schema / representation (`crates/ailang-core`):**
|
||
|
||
1. `ParamMode` loses the `Implicit` variant and `#[derive(Default)]` —
|
||
ownership has no default, so the type has no default.
|
||
2. `Type::Fn::param_modes` / `ret_mode` lose their `skip_serializing_if`
|
||
elision. Modes are always serialised. This is the one-time
|
||
canonical-JSON hash shift for the whole corpus.
|
||
3. `mode_eq` (the `Implicit ≡ Own` interchange) is deleted; mode
|
||
equality becomes structural `==`.
|
||
4. `Type::fn_implicit` is renamed `Type::fn_owned` and sets `Own` on
|
||
every synthesised slot (justified in Data flow).
|
||
|
||
**New check (`crates/ailang-check`):**
|
||
|
||
5. **Borrow-return rejection.** `(ret (borrow T))` is a check error
|
||
with a diagnostic pointing at the out-of-scope liveness axis. This
|
||
is the *only* genuinely new ownership analysis in the cycle.
|
||
|
||
**New signature-level reject (`crates/ailang-check`):**
|
||
|
||
6. **Borrow-over-value.** `(borrow value-type)` in any slot is a check
|
||
error, fired on the signature before body analysis so it does not
|
||
depend on the body tripping `consume-while-borrowed`. Specified by
|
||
model §3.2; this cycle makes it reachable now that every value slot
|
||
is moded.
|
||
|
||
**Parser (`crates/ailang-surface`):**
|
||
|
||
7. A bare type in a fn-type slot is a parse error. The slot grammar
|
||
becomes `fn-type-param ::= "(" ("borrow" | "own") type ")"` — the
|
||
bare-`type` alternative is removed.
|
||
|
||
**Migration:**
|
||
|
||
8. Every `.ail` source (prelude, kernel `raw_buf`, the ~208 example
|
||
fixtures) gets explicit modes. Param modes are derived from the
|
||
existing uniqueness/consume analysis (consumed ⇒ `own`, read-only ⇒
|
||
`borrow`; value types always `own`); return modes are `own`
|
||
everywhere (no borrow-return survives). The hash-pin tests reset
|
||
their pinned hashes once.
|
||
|
||
**Already-green, pinned not built:** own-return provenance and regime A
|
||
are enforced by the existing `consume-while-borrowed` check. The cycle
|
||
adds regression pins (Testing) proving the schema deletion does not
|
||
weaken them, but builds no new analysis for them.
|
||
|
||
## Concrete code shapes
|
||
|
||
Every block below was run through the configured `ail check`; the
|
||
exit-code traces are in the Testing section.
|
||
|
||
### User-facing: the post-cutover authoring surface (checks clean today)
|
||
|
||
The canonical form an LLM author writes after this cycle. Every slot
|
||
carries a mode; value-type slots use trivial-`own`:
|
||
|
||
```ail
|
||
(module ownership_total
|
||
|
||
(data List
|
||
(doc "Monomorphic singly-linked Int list — boxed, recursive.")
|
||
(ctor Nil)
|
||
(ctor Cons (con Int) (con List)))
|
||
|
||
(fn list_length
|
||
(doc "Borrow the list, count its elements.")
|
||
(type
|
||
(fn-type
|
||
(params (borrow (con List)))
|
||
(ret (own (con Int)))))
|
||
(params xs)
|
||
(body
|
||
(match xs
|
||
(case (pat-ctor Nil) 0)
|
||
(case (pat-ctor Cons h t)
|
||
(app + 1 (app list_length t))))))
|
||
|
||
(fn sum_list
|
||
(doc "Consume the list, sum its elements.")
|
||
(type
|
||
(fn-type
|
||
(params (own (con List)))
|
||
(ret (own (con Int)))))
|
||
(params xs)
|
||
(body
|
||
(match xs
|
||
(case (pat-ctor Nil) 0)
|
||
(case (pat-ctor Cons h t)
|
||
(app + h (app sum_list t))))))
|
||
|
||
(fn main
|
||
(doc "Build [1,2,3]; print length (3) then sum (6).")
|
||
(type (fn-type (params) (ret (own (con Unit))) (effects IO)))
|
||
(params)
|
||
(body
|
||
(let xs
|
||
(term-ctor List Cons 1
|
||
(term-ctor List Cons 2
|
||
(term-ctor List Cons 3
|
||
(term-ctor List Nil))))
|
||
(seq
|
||
(seq (app print (app list_length xs)) (do io/print_str "\n"))
|
||
(seq (app print (app sum_list xs)) (do io/print_str "\n")))))))
|
||
```
|
||
|
||
This is the feature-acceptance evidence: an LLM author writes a mode
|
||
on every slot, never a bare type, and never needs to know that `Int`
|
||
is unboxed and `List` is boxed — `own` is the always-safe choice,
|
||
`borrow` the deliberate heap-view on `list_length`'s parameter.
|
||
|
||
### New RED fixture: borrow-return rejection
|
||
|
||
The one new check. Wellformed today (no return check exists), a check
|
||
error post-cutover; the diagnostic names the liveness gap and the
|
||
out-of-scope status:
|
||
|
||
```ail
|
||
(module borrow_return_reject
|
||
|
||
(data Box
|
||
(doc "Heap cell holding one Int.")
|
||
(ctor Box (con Int)))
|
||
|
||
(fn peek
|
||
(doc "MUST FAIL post-cutover: borrow-return is refcount-invisible and can outlive its source; re-enabling needs the escape axis.")
|
||
(type
|
||
(fn-type
|
||
(params (own (con Box)))
|
||
(ret (borrow (con Box)))))
|
||
(params b)
|
||
(body b)))
|
||
```
|
||
|
||
### New RED fixture: borrow-over-value
|
||
|
||
Wellformed today, a signature-level check error post-cutover. The body
|
||
ignores the param so the rejection is unambiguously about the
|
||
signature, not a body-level consume:
|
||
|
||
```ail
|
||
(module borrow_value_reject
|
||
|
||
(fn ignore
|
||
(doc "MUST FAIL post-cutover: borrow over a value type is meaningless.")
|
||
(type
|
||
(fn-type
|
||
(params (borrow (con Int)))
|
||
(ret (own (con Int)))))
|
||
(params n)
|
||
(body 0)))
|
||
```
|
||
|
||
### New parser reject: bare type in a fn-type slot
|
||
|
||
Wellformed today (bare = `Implicit`), a parse error post-cutover:
|
||
|
||
```ail
|
||
(module bare_slot_reject
|
||
|
||
(fn id
|
||
(doc "MUST FAIL post-cutover: bare `(con Int)` slot carries no mode.")
|
||
(type
|
||
(fn-type
|
||
(params (con Int))
|
||
(ret (con Int))))
|
||
(params x)
|
||
(body x)))
|
||
```
|
||
|
||
### Already-green regression pin: own-return provenance + regime A
|
||
|
||
These are **rejected today** by `consume-while-borrowed`. They are
|
||
pinned as regressions to prove the schema deletion does not weaken the
|
||
existing guard — not as new checks:
|
||
|
||
```ail
|
||
(module own_return_provenance_reject
|
||
|
||
(data Box
|
||
(doc "Heap cell holding one Int.")
|
||
(ctor Box (con Int)))
|
||
|
||
(fn passthrough
|
||
(doc "Already rejected today (consume-while-borrowed): own-return aliases a borrowed binder.")
|
||
(type
|
||
(fn-type
|
||
(params (borrow (con Box)))
|
||
(ret (own (con Box)))))
|
||
(params b)
|
||
(body b)))
|
||
```
|
||
|
||
### North-star: the fixed leak
|
||
|
||
`examples/rc_let_implicit_returning_app.ail` asserts `live=1` today as
|
||
an intentional Implicit back-compat lane. Post-cutover its return is
|
||
`own`, the caller `dec` is emitted, and the property flips to `live=0`.
|
||
The fixture header and its property-3 assertion are rewritten from
|
||
"Implicit leaks by design" to "own-return frees correctly".
|
||
|
||
### Secondary: implementation shapes (before → after)
|
||
|
||
*Supporting detail, not the headline.*
|
||
|
||
**`crates/ailang-core/src/ast.rs` — `ParamMode`:**
|
||
|
||
```text
|
||
before:
|
||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||
#[serde(rename_all = "lowercase")]
|
||
pub enum ParamMode { #[default] Implicit, Own, Borrow }
|
||
|
||
after:
|
||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||
#[serde(rename_all = "lowercase")]
|
||
pub enum ParamMode {
|
||
Own, // (own T) — caller transfers ownership; callee consumes
|
||
Borrow, // (borrow T) — caller retains ownership; callee may not consume
|
||
}
|
||
// no #[derive(Default)]: ownership has no default.
|
||
```
|
||
|
||
**`crates/ailang-core/src/ast.rs` — `Type::Fn` serde:**
|
||
|
||
```text
|
||
before:
|
||
#[serde(default, skip_serializing_if = "all_implicit")] param_modes: Vec<ParamMode>,
|
||
#[serde(default, skip_serializing_if = "ParamMode::is_implicit")] ret_mode: ParamMode,
|
||
|
||
after:
|
||
param_modes: Vec<ParamMode>, // always present, len == params.len()
|
||
ret_mode: ParamMode, // always present
|
||
// all_implicit / is_implicit helpers deleted.
|
||
```
|
||
|
||
**`crates/ailang-core/src/ast.rs` — `mode_eq` deleted:**
|
||
|
||
```text
|
||
before: fn mode_eq(a, b) { Implicit and Own interchangeable; Borrow distinct }
|
||
after: removed; modes compared with derived PartialEq (a == b)
|
||
```
|
||
|
||
**`crates/ailang-core/src/ast.rs` — `fn_implicit` → `fn_owned`:**
|
||
|
||
```text
|
||
before: pub fn fn_implicit(params, ret, effects) -> Type // sets Implicit
|
||
after: pub fn fn_owned(params, ret, effects) -> Type // sets Own
|
||
// 16 call sites updated; Own is correct by the Data-flow argument.
|
||
```
|
||
|
||
**`crates/ailang-codegen/src/drop.rs` — App ret-mode gate (`:467–478`):**
|
||
|
||
```text
|
||
before: matches!(m, ParamMode::Own) with Implicit/Borrow → no dec (the leak)
|
||
after: matches!(m, ParamMode::Own) unchanged in form; the Implicit case no
|
||
longer exists, so every former-Implicit return now matches Own and is
|
||
dec'd. Comment updated to drop the Implicit back-compat language.
|
||
```
|
||
|
||
**`crates/ailang-surface/src/parse.rs` — slot grammar (`:32`, `parse_param_with_mode`):**
|
||
|
||
```text
|
||
before: fn-type-param ::= type | "(" "borrow" type ")" | "(" "own" type ")"
|
||
after: fn-type-param ::= "(" "borrow" type ")" | "(" "own" type ")"
|
||
// bare type → ParseError (no mode on slot)
|
||
```
|
||
|
||
## Components
|
||
|
||
- **`ailang-core::ast`** — the enum, the `Type::Fn` fields, the
|
||
constructor, equality. Blast-radius origin.
|
||
- **`ailang-surface::parse` / `print`** — slot grammar gains a reject
|
||
arm; printer drops the `Implicit` arm (`print.rs:360`).
|
||
- **`ailang-check::uniqueness` / `linearity`** — host the borrow-return
|
||
reject and the borrow-over-value reject (both signature inspections).
|
||
The migration mode-derivation reuses `consume_count`. No new
|
||
provenance analysis — `consume-while-borrowed` (`linearity.rs:1272`)
|
||
already covers own-return provenance and regime A.
|
||
- **`ailang-codegen::drop` / `lambda` / `match_lower` / `lib`** — every
|
||
`ParamMode::Implicit` match arm deleted; gate logic unchanged in form.
|
||
- **`.ail` corpus** — prelude, `raw_buf`, examples; mechanically
|
||
re-moded then reviewed.
|
||
|
||
## Data flow
|
||
|
||
**Why `Own` is correct for every synthesised slot (`fn_owned`).**
|
||
`Implicit` had two roles: the authoring default (bare type) and the
|
||
internal marker for fn-types the compiler builds (lambda types, builtin
|
||
signatures, inferred application results). The authoring role is
|
||
deleted. The synthesis role resolves to `Own` with no behaviour
|
||
change: the old typechecker made `Implicit` and `Own` indistinguishable
|
||
(`mode_eq` true for every `Implicit`/`Own` pairing), so every
|
||
synthesised fn-type was already semantically `Own` under typechecking —
|
||
no check ever depended on a slot being `Implicit` rather than `Own`.
|
||
Setting `Own` at synthesis changes no typecheck outcome. It changes
|
||
exactly one thing: the codegen gate (`drop.rs:476`) flips from "skip
|
||
the caller `dec`" to "emit it" for those former-`Implicit` returns —
|
||
precisely the leak fix. The synthesis sites do not need the consume
|
||
analysis (that is for *authored* params during migration); `Own` is
|
||
correct for them by construction. The corpus typecheck/codegen suite is
|
||
the regression net for this claim (Testing).
|
||
|
||
**Migration mode-derivation.** For each authored param: run the
|
||
uniqueness pass, read `consume_count` — `> 0 ⇒ (own T)`, `== 0 ⇒
|
||
(borrow T)` for heap types, `(own T)` for value types. For each return:
|
||
`(own T)` (no borrow-return survives; any borrow-passthrough body is
|
||
now rejected and must be restructured — and is already rejected today,
|
||
so the corpus contains none). Derived sources are reviewed by the
|
||
orchestrator before commit — the author owns the annotations; the
|
||
derivation is a drafting aid, not a surviving default.
|
||
|
||
**Check flow for the new rejects.** Both the borrow-return reject and
|
||
the borrow-over-value reject are signature inspections on the fn-type,
|
||
fired before body dataflow — no new tracking. Own-return provenance and
|
||
regime A need no flow here: `consume-while-borrowed` already fires
|
||
because a borrow parameter's borrow is live across the whole body, so
|
||
any consume of it (return-as-own, ctor arg) is caught. Ctor args are
|
||
`Position::Consume` (`linearity.rs:527`), which is why regime A holds —
|
||
a borrowed value cannot escape into a returned constructor.
|
||
|
||
## Error handling
|
||
|
||
- **Bare slot (parse error).** `parse_param_with_mode` returns a
|
||
`ParseError` when a slot is a bare type, naming the slot and
|
||
requiring `(own …)` or `(borrow …)`. Rendered in AILang syntax.
|
||
- **Borrow-return (check error, new).** New `CheckError` variant.
|
||
States `(ret (borrow …))` is not permitted in this language version
|
||
and points at the liveness/escape axis — an honest "not yet".
|
||
- **Borrow-over-value (check error, new).** New `CheckError` variant.
|
||
Signature-level: borrow is meaningless over an unboxed value type.
|
||
- **Own-return provenance.** No new diagnostic — `consume-while-borrowed`
|
||
already covers it; its message is unchanged.
|
||
|
||
Each new `CheckError` variant gets a stable code via the existing
|
||
`CheckError::code()` registry (the diagnostic registry — not a separate
|
||
doc list).
|
||
|
||
## Testing strategy
|
||
|
||
Live `ail check` traces of the spec's fixtures (run 2026-06-01,
|
||
`target/debug/ail`):
|
||
|
||
```text
|
||
ownership_total.ail : exit 0 ok (surface checks + runs)
|
||
bare_slot_reject.ail : exit 0 (legal today; parser reject post-cutover)
|
||
borrow_return_reject.ail : exit 0 (legal today; new check post-cutover)
|
||
borrow_value_reject.ail : exit 0 (legal today; new sig reject post-cutover)
|
||
own_return_provenance_reject.ail : exit 1 error: [consume-while-borrowed] passthrough: `b` ...
|
||
let-indirection (let y b y) : exit 1 error: [consume-while-borrowed] f: `b` ...
|
||
ctor-escape (term-ctor Wrap b) : exit 1 error: [consume-while-borrowed] f: `b` ... (regime A)
|
||
```
|
||
|
||
- **New RED fixtures** (`borrow_return_reject`, `borrow_value_reject`,
|
||
`bare_slot_reject`) land under `examples/`, each asserting the
|
||
specific diagnostic fires post-cutover. These are the RED side of the
|
||
one new check, the one new signature reject, and the parser reject.
|
||
- **Already-green regression pins** (`own_return_provenance_reject` and
|
||
the let-indirection / ctor-escape variants) assert
|
||
`consume-while-borrowed` still fires after the schema deletion. They
|
||
protect the own-return-soundness and regime-A properties the cycle
|
||
relies on but does not build.
|
||
- **North-star leak flip.** `rc_let_implicit_returning_app.ail` and its
|
||
pin family (`crates/ail/tests/print_no_leak_pin.rs`) flip from
|
||
`live=1` to `live=0` under `AILANG_RC_STATS`.
|
||
- **Hash-pin reset.** `crates/ailang-core/tests/hash_pin.rs` and
|
||
`crates/ailang-surface/tests/prelude_module_hash_pin.rs` get their
|
||
pinned hashes regenerated once and re-asserted (memory: hash-pin
|
||
blast-radius audit — both sites).
|
||
- **Round-trip invariant.** `ailang-surface` round-trip tests stay
|
||
green with modes always present; add a case proving a bare-slot input
|
||
now fails to parse.
|
||
- **Synthesis-site behaviour.** The corpus typecheck/codegen suite is
|
||
the regression net for `fn_implicit → fn_owned`: if any synthesised
|
||
slot was not semantically `Own`, a test breaks. Green corpus ratifies
|
||
the Data-flow argument.
|
||
- **Full workspace suite**, not just e2e — typed-MIR re-synth
|
||
strictness (memory) means mode changes surface in `lower_to_mir`
|
||
re-unification; run `cargo test --workspace`.
|
||
|
||
## Acceptance criteria
|
||
|
||
1. `ParamMode` has exactly two variants `{Own, Borrow}`, no `Default`,
|
||
no `Implicit` anywhere in `crates/` (grep clean).
|
||
2. The parser rejects a bare type in any fn-type slot; round-trip tests
|
||
prove it.
|
||
3. `borrow_return_reject` and `borrow_value_reject` fail their checks
|
||
with the specified diagnostics; the post-cutover authoring-surface
|
||
module checks and runs clean.
|
||
4. The own-return-provenance / regime-A pins stay red
|
||
(`consume-while-borrowed` fires) after the deletion.
|
||
5. `rc_let_implicit_returning_app.ail` reports `live=0` under
|
||
`AILANG_RC_STATS` (leak fixed).
|
||
6. `cargo test --workspace` green, with hash-pins regenerated once and
|
||
re-asserted.
|
||
7. `design/contracts/0008-memory-model.md` sections describing
|
||
`Implicit` (`:180-182`, `:288-298`) are updated to the binary model
|
||
as part of this cycle (a contract change rides with the feature that
|
||
forces it).
|