docs(ledger): fix two coherence gaps surfaced by the project skim
A read-only coherence skim across the project (the contract edits
themselves verified code-true, INDEX bijection exact, all pins green)
surfaced two pre-existing drifts — both predating this audit, neither
from the contract pass. Fixed in the same conservative style.
1. Model 0008 (ownership-totality) §1+§2 narrated the `Implicit`
leak in the PRESENT tense, contradicting the file's own STATUS
header (Implicit deleted via #55, 76b21c0), contract 0008 ("There
is no `Implicit`"), and the live fixture. §1 claimed "This is
documented intentional behaviour today ... the fixture asserts
`live = 1`"; the `rc_let_implicit_returning_app.ail` fixture now
asserts `live = 0` (its own comment marks the `live = 1` lane as
"Pre-0062"). §2 claimed "the typechecker already treats
`Implicit ≡ Own` (`ParamMode::mode_eq`)"; that variant and fn no
longer exist. Rewrote both to past tense (the leak the cutover
fixed). The STATUS header had been updated at cutover; these two
bodies had not. The design argument (§2-§8) is untouched — the
header frames it as the whitepaper's reasoning and points readers
to the contract for current state.
2. Contract 0010 (scope-boundaries) referenced 18 example fixtures as
`examples/*.ail.json` — files that exist only as `.ail` since the
form-A-default migration (JSON is derived in-process; only the
twelve carve-outs remain `.ail.json` on disk). All 18 → `.ail`
(every target verified present). Same single stale file-path ref in
model 0001 §3 (`list_map_poly`) corrected; model 0001's other two
`.ail.json` mentions are intentional references to the canonical
JSON *form* (the whitepaper's subject) and were left.
Honesty sweep clean; design_index_pin / docs_honesty_pin /
effect_doc_honesty_pin green; no dangling example path remains.
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
## Candidate notations (same `map` encoded in each)
|
||||
|
||||
The reference target — the polymorphic `map` from `examples/list_map_poly.ail.json`:
|
||||
The reference target — the polymorphic `map` from `examples/list_map_poly.ail`:
|
||||
|
||||
```
|
||||
data List a where Nil | Cons a (List a)
|
||||
|
||||
@@ -33,21 +33,21 @@ the sharpened §7 questions, and §8 (relation to Rust).*
|
||||
|
||||
---
|
||||
|
||||
## 1. The leak that motivates it
|
||||
## 1. The leak that motivated it
|
||||
|
||||
A user function whose return **type** is an RC-heap value (`Str`, or a
|
||||
boxed ADT) but whose signature omits the ownership mode carries
|
||||
A user function whose return **type** was an RC-heap value (`Str`, or a
|
||||
boxed ADT) but whose signature omitted the ownership mode carried
|
||||
`ret_mode == ParamMode::Implicit` — the parser's silent default, which
|
||||
nothing infers post-parse. Codegen reads `Implicit` faithfully: the
|
||||
caller does **not** `dec` the returned slab, so the owned allocation
|
||||
leaks (`live = 1` under `AILANG_RC_STATS`). This is documented
|
||||
intentional behaviour today: `../contracts/0008-memory-model.md` states
|
||||
`ret_mode` is author-required and never inferred, and the
|
||||
`examples/rc_let_implicit_returning_app.ail` fixture asserts `live = 1`
|
||||
as correct under the Implicit back-compat lane.
|
||||
nothing inferred post-parse. Codegen read `Implicit` faithfully: the
|
||||
caller did **not** `dec` the returned slab, so the owned allocation
|
||||
leaked (`live = 1` under `AILANG_RC_STATS`). The #55 cutover removed
|
||||
this lane: `../contracts/0008-memory-model.md` now requires an explicit
|
||||
author-supplied `ret_mode`, and the
|
||||
`examples/rc_let_implicit_returning_app.ail` fixture asserts `live = 0`
|
||||
(the returned cell frees at the call site).
|
||||
|
||||
The leak is the symptom. The cause is that an ownership default exists
|
||||
at all.
|
||||
The leak was the symptom. The cause was that an ownership default
|
||||
existed at all.
|
||||
|
||||
## 2. The principle: ownership is authored, never defaulted
|
||||
|
||||
@@ -60,12 +60,11 @@ annotations are kept*; *machine readability over human readability*.
|
||||
the owner of a value crossing a signature boundary — and must free it
|
||||
— is **not recoverable from the type alone**: a `Str`-returning `own`
|
||||
and a `Str`-returning `borrow` are indistinguishable by type; only the
|
||||
mode says who frees. The code makes this concrete: the typechecker
|
||||
already treats `Implicit ≡ Own` (`ParamMode::mode_eq`,
|
||||
`crates/ailang-core/src/ast.rs`), yet codegen reads `Implicit`
|
||||
separately and omits the caller `dec`. The *same* fn-type is read two
|
||||
ways depending on whether one knows codegen's special-casing — exactly
|
||||
the trust break local reasoning exists to forbid.
|
||||
mode says who frees. The code made this concrete: the typechecker
|
||||
treated `Implicit ≡ Own`, yet codegen read `Implicit` separately and omitted the
|
||||
caller `dec`. The *same* fn-type was read two ways depending on
|
||||
whether one knew codegen's special-casing — exactly the trust break
|
||||
local reasoning exists to forbid.
|
||||
|
||||
So the principle is: **ownership is authored, never defaulted.**
|
||||
|
||||
|
||||
Reference in New Issue
Block a user