Skip to content

Commit 81b191b

Browse files
committed
feat(L9): outlives preorder laws + loadSafe theorem (A4)
Closes PROOF-NEEDS §P0.3. The Outlives relation already had a full case-analysis transitivity proof (`outlivesTransitive`, seven-case pattern match over constructor combinations). This change names the two preorder laws explicitly and lands the Level 9 load-safety theorem. Preorder laws: outlivesRefl : (l : Lifetime) -> Outlives l l — reflexivity; thin wrapper over `OutlivesSelf`. outlivesTrans : Outlives a b -> Outlives b c -> Outlives a c — alias of the pre-existing `outlivesTransitive` under the PROOF-NEEDS §P0.3 naming. Load-safety theorem: loadSafe : (ref : LiveRef refLife a) -> Outlives refLife scopeLife -> Nat — the L9 guarantee in proof-term form: you cannot dereference without first producing an outlives witness. Operational counterpart is the existing `useRef` packaged via `ValidIn`; `loadSafe` is the propositional form asked for by PROOF-NEEDS. loadSafeOffset : loadSafe ref p = refOffset ref — behavioural lemma: load-safety returns the stored offset and nothing more. loadSafeIrrelevant : loadSafe ref p = loadSafe ref q — proof irrelevance at the value level; the outlives witness can be erased without changing the load result. idris2 --check clean (Lifetime + downstream Linear). panic-attack assail: 0 weak points. Zero dangerous patterns. %default total throughout.
1 parent cbf2a43 commit 81b191b

2 files changed

Lines changed: 73 additions & 1 deletion

File tree

LEVEL-STATUS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ the remaining v1.1 work.**
5050
| 6 | Result-type | TypedAccess.idr | Type flow | ECHIDNA 10^5 | **E2E complete** |
5151
| 7 | Aliasing safety | Pointer.idr (Unique) | Erased (QTT) | ECHIDNA 10^4 | **Proven [sfap], erased** |
5252
| 8 | Effect-tracking | Effects.idr | Erased (QTT) | ECHIDNA 10^4 | **Proven [sfap], erased** |
53-
| 9 | Lifetime safety | Lifetime.idr | Erased (QTT) | ECHIDNA 10^4 | **Proven [sfap], erased** |
53+
| 9 | Lifetime safety | Lifetime.idr | Erased (QTT) | ECHIDNA 10^4 | **Proven [sfap], erased. Preorder + load-safety theorems added A4 (2026-04-18): `outlivesRefl`, `outlivesTrans` (alias of pre-existing `outlivesTransitive` with 7-constructor case analysis), `loadSafe` proof-term, behavioural lemmas `loadSafeOffset` and `loadSafeIrrelevant` (proof irrelevance at the value level).** |
5454
| 10 | Linearity | Linear.idr (QTT q=1) | Erased (QTT) | ECHIDNA 10^4 | **Proven [sfap], erased. Propositional state-machine theorems added A3 (2026-04-18): distinctUsage, consumePreservesData, noReuse, noReuseEcho — usage-indexed handle `LinHandleU Fresh/Consumed tok` with `consume` state transition, alongside the QTT structural layer.** |
5555
| 11 | Tropical cost-tracking | Tropical.idr | Not yet | None | **In package (A1, 2026-04-18). Commutative-semiring closure PROVEN (A2, 2026-04-18): all 12 axioms — tropAddLeftId/RightId/Comm/Assoc, tropMulLeftId/RightId/Comm/Assoc, tropMulLeftAnn/RightAnn, tropMulDistrib/DistribR. Uses structural `tropMin` (007-lang template). Zero dangerous patterns.** |
5656
| 12 | Epistemic safety | Epistemic.idr | Not yet | None | **In package (A1, 2026-04-18); `writerKnowsFresh`, `freshOrStale`, `syncRestoresFresh` theorems live. Full freshness propagation under concurrent writes deferred.** |

src/abi/TypedWasm/ABI/Lifetime.idr

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,27 @@ outlivesTransitive FnOutlivesNamed (NamedNesting _) = FnOutlivesNamed
9393
outlivesTransitive (NamedNesting p1) (NamedNesting p2) =
9494
NamedNesting (transitive p1 p2)
9595

96+
-- ---- Preorder laws (PROOF-NEEDS §P0.3) ----
97+
98+
||| Reflexivity of the outlives preorder: every lifetime outlives itself.
99+
|||
100+
||| This is the first of the two preorder laws for Outlives; the second
101+
||| is `outlivesTrans` below. Together they make Outlives a preorder on
102+
||| Lifetime, which is the structural precondition for the load-safety
103+
||| theorem (`loadSafe`).
104+
public export
105+
outlivesRefl : (l : Lifetime) -> Outlives l l
106+
outlivesRefl _ = OutlivesSelf
107+
108+
||| Transitivity of the outlives preorder.
109+
|||
110+
||| Matches PROOF-NEEDS §P0.3 naming; thin alias over the existing
111+
||| `outlivesTransitive` which carries the case analysis.
112+
public export
113+
outlivesTrans : {a, b, c : Lifetime} ->
114+
Outlives a b -> Outlives b c -> Outlives a c
115+
outlivesTrans = outlivesTransitive
116+
96117
-- ============================================================================
97118
-- Reference Validity
98119
-- ============================================================================
@@ -190,3 +211,54 @@ weakenLifetime : Outlives long short
190211
-> LiveRef long a
191212
-> LiveRef short a
192213
weakenLifetime _ (MkLiveRef off) = MkLiveRef off
214+
215+
-- ============================================================================
216+
-- L9 Load-Safety Theorem (PROOF-NEEDS §P0.3)
217+
-- ============================================================================
218+
219+
||| Load-safety theorem: dereferencing a reference is safe when the
220+
||| reference's lifetime outlives the scope it is used in.
221+
|||
222+
||| Given:
223+
||| - `ref : LiveRef refLife a` — reference tagged with lifetime refLife,
224+
||| - `proof : Outlives refLife scopeLife` — refLife outlives the scope,
225+
|||
226+
||| `loadSafe` produces the reference's stored offset, witnessing that
227+
||| the dereference is type-safe in this scope. The Level 9 invariant is
228+
||| "use-after-free cannot type-check"; this theorem is its proof-term
229+
||| form — you cannot call `loadSafe` without first producing an Outlives
230+
||| witness.
231+
|||
232+
||| The existing `useRef` combinator is the operational form (same
233+
||| content, packaged via `ValidIn`); `loadSafe` is the propositional
234+
||| form asked for in PROOF-NEEDS.
235+
public export
236+
loadSafe : {0 refLife, scopeLife : Lifetime}
237+
-> (ref : LiveRef refLife a)
238+
-> (outlives : Outlives refLife scopeLife)
239+
-> Nat
240+
loadSafe ref _ = refOffset ref
241+
242+
||| Behavioural lemma: `loadSafe` returns the reference's stored offset
243+
||| and nothing more. Reading: the load-safety proof does not fabricate
244+
||| data; it only checks the outlives precondition and returns the
245+
||| underlying offset.
246+
public export
247+
loadSafeOffset : {0 refLife, scopeLife : Lifetime}
248+
-> (ref : LiveRef refLife a)
249+
-> (outlives : Outlives refLife scopeLife)
250+
-> loadSafe ref outlives = refOffset ref
251+
loadSafeOffset _ _ = Refl
252+
253+
||| `loadSafe` is stable under weakening of the outlives witness: any
254+
||| two outlives proofs at the same indices produce the same offset
255+
||| (proof irrelevance at the value level). This is the lemma that
256+
||| lets the compiler erase the outlives argument without changing the
257+
||| load result.
258+
public export
259+
loadSafeIrrelevant :
260+
{0 refLife, scopeLife : Lifetime}
261+
-> (ref : LiveRef refLife a)
262+
-> (p, q : Outlives refLife scopeLife)
263+
-> loadSafe ref p = loadSafe ref q
264+
loadSafeIrrelevant _ _ _ = Refl

0 commit comments

Comments
 (0)