Skip to content

Commit 8747d89

Browse files
proof(L3): preservation_l3 capstone — slice 4 (closes L3 wiring) (#196)
## Summary **L3 wiring slice 4 — capstone.** Per-layer preservation theorem for the two L3-specific echo-emitting step rules paired with their matching L3 typing rules. Closes the L3 wiring task in `PROOF-NEEDS.md §2`. ## What's new Three new Qed in `formal/Semantics_L1.v`: | Lemma | What it proves | |---|---| | `preservation_l3_region_active_echo` | `(T_Region_Active_L1_Echo, S_Region_Exit_Echo)` pair preserves typing at `TEcho T` | | `preservation_l3_drop_echo` | `(T_Drop_L1_Echo, S_Drop_Echo)` pair preserves typing at `TEcho T` | | `preservation_l3` (umbrella) | Conjunction of the two — Qed by `split; exact …` | Per-case alignment is **forced by `T_Echo_L1`'s witness-type premise**. The step rules are universally quantified over `T` (non-deterministic at the relation level); the typing derivation pins which `T` is in play. Non-deterministic crossover cases (e.g. legacy `T_Region_Active_L1` + L3 `S_Region_Exit_Echo`) are non-preserving by design — not covered by this theorem and not well-typed at the umbrella's `TEcho T` output. ## Seam audit (every admit/axiom classified) The user asked to "make sure that all the seams and gaps are checked and secure." Two outcomes: ### 1. One avoidable parallel-rule admit closed `region_shrink_preserves_typing_l1_gen_m`'s `T_Region_L1_Echo` case admit (was a mirror of a **fully-closed** original) is now Qed-style — count-monotonicity for the shadowed sub-case, `remove_first` commutation for the descend sub-case. Same treatment for the descend sub-case of `T_Region_Active_L1_Echo`. **Net debt: -1 internal admit.** ### 2. Every remaining admit classified in PROOF-NEEDS.md §4 After this PR, the complete admit/axiom inventory of `formal/*.v`: | Location | Class | |---|---| | `Semantics_L1.v:553` (`admit.`) | Pre-existing L1 structural (T_Region_Active_L1 shadowed) | | `Semantics_L1.v:621` (`admit.`) | True parallel mirror of `:553` | | `Semantics_L1.v:653` (`Admitted.`) | Outer marker over `:553` + `:621` | | `Semantics_L1.v:1256` (`admit.`) | Pre-existing L1 structural (T_Region_Active_L1 `r = rv`) | | `Semantics_L1.v:1276` (`admit.`) | True parallel mirror of `:1256` | | `Semantics_L1.v:1290` (`Admitted.`) | Outer marker over `:1256` + `:1276` | | `Semantics_L1.v:1694` (`admit.`) | Pre-existing — `preservation_l1` lambda-rigidity gap | | `Semantics_L1.v:1695` (`Admitted.`) | Outer marker over `:1694` | | `Semantics.v:9257` (`Admitted.`) | 🛑 Sacrosanct (provably false per `Counterexample.v`) | **Zero `Axiom` declarations**. `TypingL1.v` / `Modality.v` / `Echo.v` / `Counterexample.v` / `TypingL2.v` are all admit-free. ## Per CLAUDE.md owner directive 2026-05-27 - ✅ Zero new `Admitted.` or `Axiom.` declarations - ✅ No legacy `Semantics.v` patching - ✅ No cross-layer reasoning to close L1 admits - ✅ Re-uses pre-existing infrastructure only (`value_R_G_preserving_l1` Qed + `region_shrink_preserves_typing_l1_gen_m` conditional) - ✅ Cross-layer dependency on L1 structural admit annotated per `PRESERVATION-DESIGN.md §5.1` ## Build oracle `coqc 8.18.0` clean rebuild of all 9 `.v` files in dependency order: PASS. ## Test plan - [x] Slice 4 lemmas compile with zero warnings - [x] All 9 `formal/*.v` files compile (`coqc -R . Ephapax`) - [x] Zero new `admit.` or `Admitted.` introduced (verified via grep) - [x] One avoidable parallel-rule admit closed (line 574 was; gone now) - [x] Commit GPG-signed (`G` per `git log --format=%G?`) - [ ] CI green - [ ] Owner review 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 54006dc commit 8747d89

2 files changed

Lines changed: 223 additions & 16 deletions

File tree

PROOF-NEEDS.md

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ For the architectural background see
5656
| L1 judgment indexed by modality `m : Modality` | `formal/TypingL1.v` | landed via PRs #176 + #177 |
5757
| L2 modality core (`Modality.v`, `linear_to_affine`) | `formal/Modality.v` | 1 Qed, zero axioms |
5858
| L3 calculus (echo / residue fiber + degrade + no-section proof) | `formal/Echo.v` | 12 Qed, 0 admits |
59-
| Linear-mode forward progress lemmas | `formal/Semantics_L1.v` | 23 Qed; 3 residual admits (L2-β follow-up) |
59+
| Linear-mode forward progress lemmas | `formal/Semantics_L1.v` | 26 Qed (incl. 3 new from L3 slice 4: `preservation_l3_region_active_echo` / `preservation_l3_drop_echo` / `preservation_l3` umbrella); 3 outer `Admitted.` markers cover 5 internal `admit.` cases — all pre-existing L1 structural debt OR true parallel mirrors |
6060
| Counterexample regression witness | `formal/Counterexample.v` | 5 Qed (`bad_input_untypable_l1` proved under both modes) |
6161
| Operational checker (Rust, ephapax-linear sublanguage) | `ephapax-linear/src/linear.rs` | working — discharges resource-exact obligation |
6262

@@ -106,10 +106,10 @@ enters the typing rules" and (for the diagram) in §6 (to be added).
106106
| ✅ Extend AST with [TEcho : ty -> ty] and [EEcho : ty -> expr -> expr] | Type former + runtime value form for L3 echoes | done 2026-05-27 (L3 wiring slice 1 — Syntax.v + free_regions + value/shift/subst cases) |
107107
| ✅ Add `T_Observe_L1` typing rule + `EObserve` expr form | Consumes a `TEcho T` echo, returns `TBase TUnit` | done 2026-05-27 (L3 wiring slice 2 — modality-polymorphic single rule; mandatoriness via `is_linear_ty TEcho`/implicit-drop discipline is a follow-up) |
108108
| ✅ Add `T_Echo_L1` typing rule | Types runtime [EEcho T v] residue values at `TEcho T` | done 2026-05-27 (L3 wiring slice 3a — typing-side counterpart of forthcoming `S_Region_Exit_Echo` / `S_Drop_Echo` step rules; mode-polymorphic) |
109-
| ✅ Add parallel typing rules `T_Region_L1_Echo` / `T_Region_Active_L1_Echo` / `T_Drop_L1_Echo` | Output `TEcho T` instead of `T` / `TBase TUnit`. Programs choose at typing time which path | done 2026-05-27 (L3 wiring slice 3b — owner-approved parallel-rule strategy). +3 internal admits in [region_shrink_preserves_typing_l1_gen_m] + [region_liveness_at_split_l1_gen] are parallel-rule MIRRORS of pre-existing structural admits, NOT new structural debt |
109+
| ✅ Add parallel typing rules `T_Region_L1_Echo` / `T_Region_Active_L1_Echo` / `T_Drop_L1_Echo` | Output `TEcho T` instead of `T` / `TBase TUnit`. Programs choose at typing time which path | done 2026-05-27 (L3 wiring slice 3b — owner-approved parallel-rule strategy). Initial +3 internal admits in [region_shrink_preserves_typing_l1_gen_m] + [region_liveness_at_split_l1_gen] were parallel-rule MIRRORS of pre-existing structural admits; the avoidable T_Region_L1_Echo mirror was CLOSED in slice 4 — only the two true T_Region_Active_*_L1_Echo shadowed-case mirrors remain, both blocked by the same pre-existing list-vs-multiset structural debt as the originals |
110110
| ✅ Add collapse-function emission to step rules at irreversible boundaries | `S_Region_Exit_Echo` emits `EEcho T v` paralleling `S_Region_Exit` (untouched); `S_Drop_Echo` emits `EEcho T (ELoc l r)` paralleling `S_Drop` | done 2026-05-27 (L3 wiring slice 3c — owner-approved parallel-rule strategy in Semantics.v; updated `step_from_eregion` to 4-disjunct classification + `step_R_change_shape` + `no_leaks_gen` cascade); rules quantified over witness type T |
111111
| ~~Thread `G` (echo context) alongside `R` (region context) through compound rules~~ | ~~New context parameter on every L1 compound rule~~ | **OBSOLETE 2026-05-27**: under the owner-approved parallel-rules design (slices 3a–3c), echoes are values of type `TEcho T` that flow through the existing `ctx` G. No separate echo-context parameter is needed. |
112-
| State and prove `preservation_l3` | Per-layer preservation theorem against the L3 invariants for the new echo-emitting step rules + echo-typed paths | Cross-layer dependency annotated in `PRESERVATION-DESIGN.md §5.1`. The four prerequisite slices (1, 2, 3a, 3b, 3c) all landed; `preservation_l3` is the capstone. |
112+
| State and prove `preservation_l3` | Per-layer preservation theorem against the L3 invariants for the new echo-emitting step rules + echo-typed paths | done 2026-05-27 (L3 wiring slice 4 — capstone). Two per-case Qed lemmas (`preservation_l3_region_active_echo` for `S_Region_Exit_Echo` × `T_Region_Active_L1_Echo`, and `preservation_l3_drop_echo` for `S_Drop_Echo` × `T_Drop_L1_Echo`) plus an umbrella `preservation_l3` (their conjunction, Qed). Zero new admits or axioms. Per-case alignment forced by `T_Echo_L1`'s witness-type premise; non-deterministic crossover cases are non-preserving by design (typing derivation pins the path). Conditionally Qed under the pre-existing `region_shrink_preserves_typing_l1_gen_m` L1 structural admit per PRESERVATION-DESIGN.md §5.1. |
113113

114114
### Mid-term (L4 — not started)
115115

@@ -226,12 +226,37 @@ to the owner**:
226226
| `formal/Typing.v` (legacy) | n/a | 0 | 🛑 archaeology — `Counterexample.v` depends on falsity |
227227
| `formal/Counterexample.v` | **5** | 0 | ✅ pinned regression witness |
228228
| `formal/TypingL1.v` | **2** | 0 | ✅ active — L1 judgment, modality-indexed |
229-
| `formal/Semantics_L1.v` | **23** | **3** | ✅ active — bullet-structure regressions + subst_typing_gen_l1_m + region_shrink_preserves_typing_l1_gen_m closed 2026-05-27; 3 residual admits are deeper L2-β debt (region_shrink T_Region_Active_L1 list-vs-multiset case now isolated in _gen_m internal admit, region_liveness_at_split narrow admit per ERegion counterexample, preservation_l1 cap) |
229+
| `formal/Semantics_L1.v` | **26** | **3** | ✅ active — bullet-structure regressions + subst_typing_gen_l1_m + region_shrink_preserves_typing_l1_gen_m closed 2026-05-27; 3 residual outer `Admitted.` markers cover 5 internal `admit.` cases — all pre-existing L1 structural debt OR true parallel mirrors (T_Region_Active_*_L1_Echo shadowed sub-cases mirror T_Region_Active_L1's debt; the avoidable T_Region_L1_Echo mirror was closed in slice 4). Slice 4 added 3 new Qed: `preservation_l3_region_active_echo`, `preservation_l3_drop_echo`, `preservation_l3` (umbrella). Zero new admits. |
230230
| `formal/Modality.v` | **1** | 0 | ✅ active — L2 core, zero axioms |
231231
| `formal/Echo.v` | **12** | 0 | ✅ active — L3 calculus, not yet wired into L1 |
232232
| `formal/TypingL2.v` | (wrapper) | (wrapper) | ✅ thin re-indexing through `TypingL1.has_type_l1` |
233233
| `src/abi/Ephapax/…` (Idris2) | n/a | n/a | ✅ active — ABI, Region linearity, no `believe_me` / `sorry` / `assert_total` |
234234

235+
### Seam audit (slice 4, 2026-05-27): every admit/axiom classified
236+
237+
Every `admit.` and `Admitted.` in `formal/*.v` after L3 slice 4
238+
landed. No new debt was introduced; the avoidable T_Region_L1_Echo
239+
mirror was closed. The remaining set is exactly the **pre-existing
240+
L1 structural debt** (with two true parallel mirrors that close
241+
when their originals close) plus the **sacrosanct legacy
242+
preservation** (provably false per `Counterexample.v`).
243+
244+
| Location | Class | Closes when |
245+
|---|---|---|
246+
| `Semantics_L1.v:553` (`admit.`) | **Pre-existing** L1 structural — `region_shrink_preserves_typing_l1_gen_m` / T_Region_Active_L1 shadowed sub-case; list-vs-multiset gap | L1 perm/multiset bridge OR `T_Region_*_L1` redesign |
247+
| `Semantics_L1.v:621` (`admit.`) | **Parallel mirror** of `:553` — T_Region_Active_L1_Echo shadowed sub-case; structurally identical | Same as `:553` (mechanical replay) |
248+
| `Semantics_L1.v:653` (`Admitted.`) | **Outer marker** — depends on internal `:553` + `:621` | When both internal admits close |
249+
| `Semantics_L1.v:1256` (`admit.`) | **Pre-existing** L1 structural — `region_liveness_at_split_l1_gen` / T_Region_Active_L1 `r = rv` sub-case; "GENUINELY FALSE" counterexample documented at site | L2 effect-typed `TFun` per `PRESERVATION-DESIGN.md §5.1` |
250+
| `Semantics_L1.v:1276` (`admit.`) | **Parallel mirror** of `:1256` — T_Region_Active_L1_Echo `r = rv` sub-case | Same as `:1256` (mechanical replay) |
251+
| `Semantics_L1.v:1290` (`Admitted.`) | **Outer marker** — depends on internal `:1256` + `:1276` | When both internal admits close |
252+
| `Semantics_L1.v:1694` (`admit.`) | **Pre-existing**`preservation_l1` body; lambda-rigidity gap per `PRESERVATION-DESIGN.md §4.8` | L2 effect-typed `TFun` (Phase 2) |
253+
| `Semantics_L1.v:1695` (`Admitted.`) | **Outer marker** — depends on internal `:1694` | When `:1694` closes |
254+
| `Semantics.v:9257` (`Admitted.`) | **🛑 Sacrosanct** — legacy `Theorem preservation`, **provably false** per `Counterexample.v` (owner directive 2026-05-27) | Never. The `Admitted.` is correct. |
255+
256+
No `Axiom` declarations in `formal/*.v`. Counterexample.v carries 5
257+
Qed; TypingL1.v / Modality.v / Echo.v / TypingL2.v are all
258+
admit-free.
259+
235260
### Idris2 side (proof carriers, not Coq mechanisation)
236261

237262
| Concern | File(s) | Status |

formal/Semantics_L1.v

Lines changed: 194 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -564,18 +564,74 @@ Proof.
564564
* exact H0.
565565
* apply remove_first_preserves_other; [intro Hbad; apply Heq; exact Hbad | exact H1].
566566
* eapply IHHt. exact Hfree.
567-
- (* T_Region_L1_Echo — same structural shape as T_Region_L1; the
568-
proof is identical modulo the output-type [TEcho T] vs [T].
569-
Both subcases (shadowed + descend) follow the original's
570-
analysis. Admitted here as the outer lemma is already
571-
[Admitted] and the structural residual (shadowed sub-case in
572-
T_Region_Active_L1_Echo) bridges the same list-vs-multiset
573-
gap documented above. *)
574-
admit.
575-
- (* T_Region_Active_L1_Echo — parallels T_Region_Active_L1
576-
including the residual structural admit in the shadowed
577-
sub-case (PROOF-NEEDS.md §2). *)
578-
admit.
567+
- (* T_Region_L1_Echo — structurally identical to T_Region_L1
568+
modulo the output-type [TEcho T] vs [T]. Both subcases close
569+
directly (shadowed via count-monotonicity vacuity, descend
570+
via remove_first/remove_first_L1 commutation). Closed in
571+
slice 4 to honor the "no parallel-rule admit-debt" seam-audit
572+
directive — the original T_Region_L1 case has zero admits,
573+
so its mirror has zero admits too. *)
574+
destruct (String.eqb rr r) eqn:Heq.
575+
+ (* rr = r: shadowed. *)
576+
apply String.eqb_eq in Heq. subst r.
577+
rewrite (remove_first_not_in_id _ _ H).
578+
destruct (in_dec string_dec rr (remove_first_L1 rr R_body)) as [Hin | Hnotin].
579+
* (* Multiple rr's in R_body — VACUOUS by count monotonicity. *)
580+
exfalso.
581+
pose proof (count_occ_le_l1_m _ _ _ _ _ _ _ Ht rr) as Hle.
582+
unfold cnt in Hle. simpl in Hle.
583+
destruct (string_dec rr rr) as [_|Hbad]; [|apply Hbad; reflexivity].
584+
apply (count_occ_In string_dec) in Hin.
585+
pose proof (remove_first_L1_count_eq_self rr R_body) as Hself.
586+
unfold cnt in Hself. rewrite Hself in Hin.
587+
apply (count_occ_not_In string_dec) in H.
588+
lia.
589+
* (* At most one rr in R_body. *)
590+
rewrite (remove_first_not_in_id _ _ Hnotin).
591+
eapply T_Region_L1_Echo; eauto.
592+
+ (* rr <> r: descend. *)
593+
apply String.eqb_neq in Heq.
594+
assert (Hgoal_eq : remove_first rr (remove_first_L1 r R_body) =
595+
remove_first_L1 r (remove_first rr R_body)).
596+
{ rewrite (remove_first_eq_l1 r R_body).
597+
rewrite (remove_first_eq_l1 r (remove_first rr R_body)).
598+
apply remove_first_comm. }
599+
rewrite Hgoal_eq.
600+
eapply T_Region_L1_Echo.
601+
* intro Hin. apply H. eapply remove_first_subset; exact Hin.
602+
* exact H0.
603+
* apply remove_first_preserves_other; [intro Hbad; apply Heq; exact Hbad | exact H1].
604+
* specialize (IHHt rr Hfree).
605+
simpl in IHHt.
606+
destruct (String.eqb rr r) eqn:Heq2.
607+
-- exfalso. apply String.eqb_eq in Heq2. apply Heq. exact Heq2.
608+
-- exact IHHt.
609+
- (* T_Region_Active_L1_Echo — parallels T_Region_Active_L1.
610+
The descend sub-case closes directly; the shadowed sub-case
611+
(rr = r) is the same list-vs-multiset structural residual as
612+
in T_Region_Active_L1 above. Per slice 4 seam audit: the
613+
shadowed sub-case admit is a TRUE MIRROR of the pre-existing
614+
L1 structural admit at line 553. The descend sub-case is
615+
closed Qed-style in slice 4 (not an admit). *)
616+
destruct (String.eqb rr r) eqn:Heq.
617+
+ (* rr = r: shadowed — list-vs-multiset mirror of line 553.
618+
Resolution path is identical to its non-Echo counterpart:
619+
option (a) L1 perm lemma, (b) multiset reformulation, or
620+
(c) T_Region_*_L1 redesign. See line 542-552 design note. *)
621+
admit.
622+
+ (* rr <> r: descend. *)
623+
apply String.eqb_neq in Heq.
624+
assert (Hgoal_eq : remove_first rr (remove_first_L1 r R_body) =
625+
remove_first_L1 r (remove_first rr R_body)).
626+
{ rewrite (remove_first_eq_l1 r R_body).
627+
rewrite (remove_first_eq_l1 r (remove_first rr R_body)).
628+
apply remove_first_comm. }
629+
rewrite Hgoal_eq.
630+
eapply T_Region_Active_L1_Echo.
631+
* apply remove_first_preserves_other; [intro Hbad; apply Heq; exact Hbad | exact H].
632+
* exact H0.
633+
* apply remove_first_preserves_other; [intro Hbad; apply Heq; exact Hbad | exact H1].
634+
* eapply IHHt. exact Hfree.
579635
- (* T_Borrow_L1 *)
580636
eapply T_Borrow_L1. exact H.
581637
- (* T_Borrow_Val_L1 *)
@@ -1637,3 +1693,129 @@ Proof.
16371693
intros m mu R e mu0 R0 e0 Hstep G T R_final G_out Ht.
16381694
admit.
16391695
Admitted.
1696+
1697+
(** ** L3 preservation theorem (slice 4 of L3 wiring — capstone)
1698+
1699+
Per-layer preservation theorem for the two L3-specific
1700+
echo-emitting step rules [S_Region_Exit_Echo] and [S_Drop_Echo]
1701+
(Semantics.v slice 3c) paired with their matching L3 typing
1702+
rules [T_Region_Active_L1_Echo] / [T_Drop_L1_Echo] +
1703+
[T_Echo_L1] for the result (TypingL1.v slices 2 / 3a / 3b).
1704+
1705+
Stated as two per-case lemmas + an umbrella theorem
1706+
[preservation_l3] which is their conjunction.
1707+
1708+
Design constraint (PRESERVATION-DESIGN.md §6.3 +
1709+
PROOF-NEEDS.md §2): the L3 step rules are universally
1710+
quantified over the witness type [T] in the [EEcho T v]
1711+
residue. The non-determinism is resolved at the typing-
1712+
derivation level — each L3 typing rule pins [T] to its body
1713+
type. The per-case lemmas state preservation for the
1714+
matching (typing-rule, step-rule) pairs only; "cross-path"
1715+
pairs (e.g. legacy [T_Region_Active_L1] + L3
1716+
[S_Region_Exit_Echo]) are non-preserving by design and not
1717+
well-typed at the umbrella's [TEcho T] output.
1718+
1719+
Per CLAUDE.md owner directive 2026-05-27, slice 4:
1720+
- introduces ZERO new [Admitted.] or [Axiom] declarations;
1721+
- does NOT patch legacy [Semantics.v] preservation;
1722+
- re-uses already-proved infrastructure
1723+
([value_R_G_preserving_l1] Qed,
1724+
[region_shrink_preserves_typing_l1_gen_m] — admitted at
1725+
the L1 layer for the T_Region_Active_L1 shadowed
1726+
sub-case; cross-layer dependency annotated in
1727+
PRESERVATION-DESIGN.md §5.1).
1728+
1729+
Cross-layer accounting: the [region_shrink_preserves_typing_l1_gen_m]
1730+
dependency is a pre-existing L1 structural admit (closes at
1731+
L2-effect-typed-TFun per design doc §5.1). Slice 4 does NOT
1732+
widen, duplicate, or open it — preservation_l3 is
1733+
*conditionally* Qed under that pre-existing L1 obligation. *)
1734+
1735+
(** *** Case 1: [S_Region_Exit_Echo] paired with
1736+
[T_Region_Active_L1_Echo].
1737+
1738+
The typing premise extracts the body's typing
1739+
[R; G |=L1[m] v : T -| R_body; G']; since [v] is a value,
1740+
[value_R_G_preserving_l1] forces [R_body = R] and [G' = G].
1741+
Region shrinkage then provides the post-step witness typing
1742+
at [remove_first r R], and [T_Echo_L1] wraps it. *)
1743+
1744+
Lemma preservation_l3_region_active_echo :
1745+
forall m R G r v T R_body G',
1746+
(* T_Region_Active_L1_Echo premises *)
1747+
In r R ->
1748+
~ In r (Typing.free_regions T) ->
1749+
has_type_l1 m R G v T R_body G' ->
1750+
(* S_Region_Exit_Echo premises *)
1751+
is_value v ->
1752+
expr_free_of_region r v ->
1753+
(* Conclusion: post-step types at [TEcho T] *)
1754+
has_type_l1 m (remove_first r R) G
1755+
(EEcho T v) (TEcho T) (remove_first_L1 r R_body) G'.
1756+
Proof.
1757+
intros m R G r v T R_body G' HinR HnotT Htbody Hv Hfree.
1758+
(* Value-invariance: R_body = R and G' = G. *)
1759+
pose proof (value_R_G_preserving_l1 _ _ _ _ _ _ _ Hv Htbody)
1760+
as [HR HG].
1761+
subst R_body G'.
1762+
(* Bridge [remove_first_L1] (typing-side) to [remove_first]
1763+
(semantics-side) — both pointwise equal by
1764+
[remove_first_eq_l1] (Qed). *)
1765+
replace (remove_first_L1 r R) with (remove_first r R)
1766+
by (symmetry; apply remove_first_eq_l1).
1767+
(* Goal: (remove_first r R); G |- EEcho T v : TEcho T
1768+
-| (remove_first r R); G *)
1769+
apply T_Echo_L1; [ exact Hv | ].
1770+
(* Goal: (remove_first r R); G |- v : T
1771+
-| (remove_first r R); G
1772+
This is exactly region shrinkage at [v] (free of [r]). *)
1773+
apply (region_shrink_preserves_typing_l1_gen_m m _ _ _ _ _ _ Htbody _ Hfree).
1774+
Qed.
1775+
1776+
(** *** Case 2: [S_Drop_Echo] paired with [T_Drop_L1_Echo].
1777+
1778+
The typing premise extracts
1779+
[R; G |=L1[m] ELoc l r : T -| R'; G']. Since [ELoc l r] is
1780+
a value (VLoc), value-invariance forces [R' = R] and
1781+
[G' = G]. Then [T_Echo_L1] applies directly — no region
1782+
shrinkage needed (step does not change [R]). *)
1783+
1784+
Lemma preservation_l3_drop_echo :
1785+
forall m R G l r T R' G',
1786+
(* T_Drop_L1_Echo premises *)
1787+
is_linear_ty T = true ->
1788+
has_type_l1 m R G (ELoc l r) T R' G' ->
1789+
(* Conclusion: post-step types at [TEcho T] *)
1790+
has_type_l1 m R G (EEcho T (ELoc l r)) (TEcho T) R' G'.
1791+
Proof.
1792+
intros m R G l r T R' G' Hlin Htbody.
1793+
pose proof (value_R_G_preserving_l1 _ _ _ _ _ _ _
1794+
(VLoc l r) Htbody) as [HR HG].
1795+
subst R' G'.
1796+
apply T_Echo_L1; [ apply VLoc | exact Htbody ].
1797+
Qed.
1798+
1799+
(** *** Umbrella: [preservation_l3] is the conjunction of the
1800+
two per-case lemmas above. Each conjunct corresponds to one
1801+
(L3 typing rule, L3 step rule) matched pair. *)
1802+
1803+
Theorem preservation_l3 :
1804+
(forall m R G r v T R_body G',
1805+
In r R ->
1806+
~ In r (Typing.free_regions T) ->
1807+
has_type_l1 m R G v T R_body G' ->
1808+
is_value v ->
1809+
expr_free_of_region r v ->
1810+
has_type_l1 m (remove_first r R) G
1811+
(EEcho T v) (TEcho T) (remove_first_L1 r R_body) G')
1812+
/\
1813+
(forall m R G l r T R' G',
1814+
is_linear_ty T = true ->
1815+
has_type_l1 m R G (ELoc l r) T R' G' ->
1816+
has_type_l1 m R G (EEcho T (ELoc l r)) (TEcho T) R' G').
1817+
Proof.
1818+
split.
1819+
- exact preservation_l3_region_active_echo.
1820+
- exact preservation_l3_drop_echo.
1821+
Qed.

0 commit comments

Comments
 (0)