Skip to content

Commit ed663ab

Browse files
Claude/adoring gates e57 wg (#125)
Co-authored-by: Claude <noreply@anthropic.com>
1 parent 74fe03a commit ed663ab

2 files changed

Lines changed: 2566 additions & 46 deletions

File tree

formal/PRESERVATION-HANDOFF.md

Lines changed: 201 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,102 @@ mechanical 28 are 1–2 days of tactic work.
139139
estate's "build is the only oracle" policy. The honest mark is one
140140
`Admitted.` on `preservation`, not 29.
141141

142+
## `step_preserves_type` per-case status (2026-05-24, late)
143+
144+
Empirically verified against `coqc 8.18.0`. The 12 cases identified
145+
as "remaining open" after the f499c82 clone-out have now each been
146+
given an explicit per-case proof skeleton. **Three iterations**:
147+
148+
1. **First pass**: dispatched on `step_R_eq_or_touches_region`,
149+
closed the LEFT (R = R') branches, admitted RIGHT. 8 admits remained.
150+
2. **Second pass**: introduced finer `step_R_change_shape` (3-way
151+
disjunction: equal / prepend r / remove_first r), closed the MIDDLE
152+
(prepend) branch for all 7 congruence cases via `region_add_typing`.
153+
Still 8 admits, but each tied to the strictly-narrower
154+
"remove_first r R" sub-case rather than the broader "touches_region".
155+
3. **Third pass**: added the `remove_first_then_cons_membership_eq`
156+
helper (proving `r :: remove_first r R` and `R` have the same
157+
membership whenever `In r R`, no `NoDup R` required) and used it
158+
with `region_env_perm_typing` to close the RIGHT (`remove_first`)
159+
sub-case in all 7 congruence proofs. **Now 1 admit remains**,
160+
tied to S_Region_Step's exfalso when the inner step exits the
161+
outer region from inside (r = r1 sub-case).
162+
163+
**Moved** `touches_region` + `step_R_eq_or_touches_region` to before
164+
`step_preserves_type` so the latter can dispatch on the LEFT
165+
(R = R') branch.
166+
167+
### Fully closed (4 of 12)
168+
169+
| Goal | Step rule | Closure |
170+
|------|-----------|---------|
171+
| 1 | `S_StringConcat_Step2` | outer T = TString r is structurally constrained; IH on (e2, e2') gives r = r' |
172+
| 8 | `S_Snd` atomic (ESnd (EPair v1 v2) → v2) | `value_context_unchanged` on v1 + `type_determinacy` on v2 |
173+
| 10 | `S_Region_Exit` (ERegion r v → v) | `region_shrink_preserves_typing` bridges R0 and remove_first r R0, then `type_determinacy` |
174+
| 12 | `S_Copy` atomic (ECopy v → EPair v v) | `value_context_unchanged` on first projection + two `type_determinacy` calls on v |
175+
176+
### Fully closed via R-shape dispatch (7 of 12 congruence cases)
177+
178+
After the third pass, every congruence case closes via the 3-way
179+
dispatch on `step_R_change_shape`:
180+
181+
| Goal | Step rule | LEFT (R = R') | MIDDLE (R' = r :: R) | RIGHT (R' = remove_first r R) |
182+
|------|-----------|----------------|----------------------|-------------------------------|
183+
| 2 | `S_Let_Step` || ✅ via `region_add_typing` | ✅ via lift+perm |
184+
| 3 | `S_LetLin_Step` ||||
185+
| 4 | `S_App_Step2` ||||
186+
| 5 | `S_If_Step` ||||
187+
| 6 | `S_Pair_Step1` ||||
188+
| 7 | `S_Pair_Step2` ||||
189+
| 9 | `S_Case_Step` ||||
190+
191+
The RIGHT sub-case (when the inner step exits a region) is closed by:
192+
1. `region_add_typing` lifts the post-step sibling typing from
193+
`remove_first r R0` to `r :: remove_first r R0`.
194+
2. `region_env_perm_typing` converts to `R0` via
195+
`remove_first_then_cons_membership_eq` — the new helper that proves
196+
`r :: remove_first r R0` and `R0` have the same membership when
197+
`In r R0` (NO `NoDup R` invariant needed; works even for duplicates).
198+
3. `type_determinacy` aligns the types under the now-shared `R0`.
199+
200+
### Partially closed — one sub-case admitted (1 of 12)
201+
202+
| Goal | Step rule | Closures | Remaining admit |
203+
|------|-----------|----------|-----------------|
204+
| 11 | `S_Region_Step` (ERegion r e → ERegion r e') | T_Region (Hte) contradicted by `In r R0`; T_Region_Active × T_Region_Active via IH; T_Region_Active × T_Region 3 of 4 R-shape sub-cases close (R = R', R' = r1::R0, R' = remove_first r1 R0 with r ≠ r1) — all by contradiction with `~In r R0'` | One sub-case: T_Region_Active × T_Region with `R0' = remove_first r R0` (the outer r is exited from inside). Closing requires `expr_free_of_region r e'` to shrink the post-step inner typing, derivable but requires further inversion on `Hstep` to reach the underlying `S_Region_Exit`'s premise. |
205+
206+
### Net effect
207+
208+
- **Before**: `step_preserves_type` was `all: admit` with 12 open
209+
goals — every case admitted with no structure.
210+
- **After (pass 1)**: 4 of 12 closed fully, 7 of 12 had LEFT (R = R')
211+
branch closed, 1 of 12 had main branch closed. 8 admits.
212+
- **After (pass 2)**: each congruence admit narrowed to just the
213+
`R' = remove_first r R` sub-case via `step_R_change_shape` +
214+
`region_add_typing`. Still 8 admits but each strictly narrower.
215+
- **After (pass 3)**: all 7 congruence RIGHT sub-cases closed via the
216+
new `remove_first_then_cons_membership_eq` helper +
217+
`region_env_perm_typing`. **1 admit remains** — the
218+
`T_Region_Active × T_Region` cross-case of `S_Region_Step` when
219+
the inner step exits the outer region from inside.
220+
221+
New supporting lemmas added (all `Qed.`):
222+
- `step_R_change_shape` (~10 LOC): refines the 2-way disjunction
223+
into 3-way, also exposing `~In r R` / `In r R` for the prepend /
224+
remove cases.
225+
- `remove_first_then_cons_membership_eq` (~15 LOC): proves
226+
`(r :: remove_first r R)` and `R` have the same membership when
227+
`In r R`. NO `NoDup R` required.
228+
229+
The single remaining admit is genuinely the hardest sub-case: the
230+
inner step exits the outer ERegion's own region, producing a typing
231+
that under `r :: remove_first r R` re-introduces `r` as fresh. To
232+
close it, we need `expr_free_of_region r e'` for the post-step inner
233+
expression. This follows from the underlying `S_Region_Exit`'s
234+
`expr_free_of_region r v` premise, but extracting it requires
235+
further inversion on `Hstep` to reach that base step. Small
236+
follow-up — `step_exit_implies_free_of_exited_region` would settle it.
237+
142238
## Lemma B per-case status (2026-05-24)
143239

144240
Empirically verified against `coqc 8.18.0`. The Phase 1 scaffold for
@@ -155,43 +251,113 @@ atomic-axiom closure tactic. **4 of 35 step rules close**; 31 remain.
155251
| `S_Drop` | atomic: `EDrop (ELoc _) → EUnit`, both T_Drop and T_Unit are identity-output |
156252
| `S_Borrow_Step` | **accidental congruence closure**: both `T_Borrow` and `T_Borrow_Val` output the input context unchanged, so `Ga = G = Gb` regardless of whether the inner step is reachable. Vacuous-but-closes. |
157253

158-
### Open (31)
159-
160-
Three clusters, ordered by closure tractability:
161-
162-
#### Cluster A — β-reduction (~7)
163-
164-
Need a strengthened `subst_preserves_typing_strong` exposing the
165-
specific output context (currently hidden behind an existential).
166-
`subst_typing_gen` already has the `remove_at k Gout` shape — wrap
167-
it.
168-
169-
| Step rule | Post-step expr (from coqc dump) |
170-
|-----------|---------------------------------|
171-
| `S_Let_Val` | `subst 0 v1 e2` |
172-
| `S_LetLin_Val` | `subst 0 v1 e2` |
173-
| `S_App_Fun` | `subst 0 v2 ebody` |
174-
| `S_If_True` | `e0'` (the `e2` branch, abstract) |
175-
| `S_If_False` | `e0'` (the `e3` branch, abstract) |
176-
| `S_Case_Inl` | `subst 0 v e1` |
177-
| `S_Case_Inr` | `subst 0 v e2` |
178-
179-
#### Cluster B — congruence (~18)
180-
181-
Every `S_*_Step` except `S_Borrow_Step`. Recipe per case: apply
182-
`step_R_eq_or_touches_region` → in the R-equal branch, apply IH for
183-
the inner step + recursive Lemma B for siblings. RIGHT branch
184-
(`touches_region`) is blocked on the same region-env weakening
185-
lemma as preservation Phase 3 — these cases share the bottleneck.
254+
### Open (24, was 31)
255+
256+
#### Cluster A — β-reduction ✅ FULLY CLOSED (2026-05-24)
257+
258+
All 7 β-reduction cases closed via
259+
`subst_preserves_typing_strong` (PR: this branch) + `output_ctx_det`
260+
(PR: this branch). Recipe per case:
261+
1. Invert the outer compound typing (`T_Let`, `T_App`, `T_If`,
262+
`T_Case`) to expose body + value premises.
263+
2. For T_App: also invert `T_Lam` on the function value.
264+
3. For T_Case: apply `value_context_unchanged` on the EInl/EInr
265+
premise, then invert `T_Inl`/`T_Inr`.
266+
4. Apply `value_context_unchanged` on the value premise(s) to
267+
align intermediate contexts with the input context.
268+
5. `destruct (subst_preserves_typing_strong ...)` to construct a
269+
typing of the substituted form at the specific output context.
270+
6. `eapply output_ctx_det` against `Htype_e'` to conclude
271+
`Ga = Gb`.
272+
273+
Closed cases: `S_Let_Val`, `S_LetLin_Val`, `S_App_Fun`,
274+
`S_If_True`, `S_If_False`, `S_Case_Inl`, `S_Case_Inr`.
275+
276+
#### Cluster B — congruence (10 of 18 closed, 8 open)
277+
278+
**Closed (2026-05-24)**: `S_StringConcat_Step1`,
279+
`S_StringConcat_Step2`, `S_Pair_Step1`, `S_Pair_Step2`,
280+
`S_Inl_Step`, `S_Inr_Step`, `S_Copy_Step`, `S_If_Step`,
281+
`S_StringLen_Step`. (Plus `S_Borrow_Step` closed accidentally
282+
earlier.)
283+
284+
Recipe (canonical two-child congruence, e.g.
285+
`S_StringConcat_Step1`):
286+
1. Invert both `Hte` and `Hte'`.
287+
2. `pose proof step_R_eq_or_touches_region` to dispatch `R = R'`.
288+
3. LEFT (R = R'): apply IH on inner step's typings to get
289+
`Gmid = Gmid'`; `output_ctx_det` on the unchanged sibling
290+
closes.
291+
4. RIGHT (`touches_region`): locally `admit` per-case.
292+
293+
Variants:
294+
- Second-child congruences (`S_StringConcat_Step2`,
295+
`S_Pair_Step2`): use `value_context_unchanged` on the first
296+
child (the value) to align contexts before IH on the second
297+
child.
298+
- Single-child congruences (`S_Inl_Step`, `S_Inr_Step`,
299+
`S_Copy_Step`): no sibling, IH directly closes.
300+
- `S_If_Step`: condition at `TBase TBool` (fixed type), branches
301+
at outer `T` — fully constrained.
302+
- `S_StringLen_Step`: vacuous via inversion chain (T_StringLen →
303+
T_Borrow / T_Borrow_Val: the inner must be `EVar` or a value;
304+
neither steps).
305+
306+
**Open (6 of original 8)** — blocked on **type-alignment circularity WITHOUT independent-context sibling**:
307+
308+
S_App_Step1 and S_App_Step2 closed via the "sibling type_determinacy"
309+
trick (their sibling's context — `Gmid` for Step1, `G` for Step2 via
310+
value_context_unchanged — is independent of the unconstrained `T1`).
311+
312+
For the remaining cases, no such sibling exists or its context
313+
depends on the unconstrained type:
314+
315+
| Step rule | Inner-type that's NOT fixed by outer T |
316+
|-----------|----------------------------------------|
317+
| `S_Let_Step` | `T_Let`'s binding type `T1` |
318+
| `S_LetLin_Step` | `T_LetLin`'s binding type `T1` |
319+
| `S_Case_Step` | `T_Case`'s scrutinee `TSum T1 T2` |
320+
| `S_Drop_Step` | `T_Drop`'s arg type `T` (outer is `TBase TUnit`) |
321+
| `S_Fst_Step` | `T_Fst`'s second component `T2` |
322+
| `S_Snd_Step` | `T_Snd`'s first component `T1` |
323+
324+
For each: T_X inversion of Hte and Hte' produces independent
325+
fresh type-vars for the unconstrained inner types. To apply
326+
Lemma B's IH on the inner step, we'd need to know both typings
327+
are at the SAME inner type — but establishing that requires
328+
preservation. Circular.
329+
330+
**Resolution paths**:
331+
1. Prove Lemma B and preservation by **simultaneous mutual
332+
induction** (restructure both proofs).
333+
2. **Re-state Lemma B** with a conclusion that doesn't need
334+
shared T (weaker output-context-equivalence).
335+
3. Add a **type-preservation-under-step** sub-lemma (essentially
336+
the type-only part of preservation) and prove it separately
337+
via a more restricted induction.
338+
339+
#### Cluster C — region / compound-value ✅ FULLY CLOSED (2026-05-24)
340+
341+
**Closed**: `S_Fst`, `S_Snd`, `S_Copy`, `S_Region_Exit`,
342+
`S_StringLen` (atomic), `S_Region_Enter`. All 6.
343+
344+
Recipes:
345+
- `S_Fst`, `S_Snd`, `S_Copy`, `S_Region_Exit`: invert the compound
346+
rule (T_Fst → T_Pair, T_Snd → T_Pair, T_Copy, T_Region_Active),
347+
apply `value_context_unchanged` on each value-typing premise to
348+
align intermediate contexts with G, then `reflexivity` closes.
349+
- `S_StringLen` atomic: 3-level inversion chain T_StringLen →
350+
T_Borrow_Val (T_Borrow's EVar form contradicted by ELoc) → T_Loc.
351+
Each preserves the context, so Ga = G. T_I32 on Hte' gives Gb = G.
352+
- `S_Region_Enter`: 4 inversion sub-goals from T_Region /
353+
T_Region_Active × Hte / Hte'. Two contradiction patterns:
354+
`tauto` closes Hn-vs-`In r R0` cases; explicit
355+
`apply H; left; reflexivity` closes the `~ In r (r :: R0)`
356+
case. The valid sub-goal (Hte = T_Region, Hte' = T_Region_Active)
357+
closes via `output_ctx_det` on the two inner typings of `e` at
358+
`(r :: R0); G`.
186359

187-
`S_StringConcat_Step1`, `S_StringConcat_Step2`, `S_StringLen_Step`,
188-
`S_Let_Step`, `S_LetLin_Step`, `S_App_Step1`, `S_App_Step2`,
189-
`S_If_Step`, `S_Pair_Step1`, `S_Pair_Step2`, `S_Fst_Step`,
190-
`S_Snd_Step`, `S_Inl_Step`, `S_Inr_Step`, `S_Case_Step`,
191-
`S_Drop_Step`, `S_Copy_Step`. (17 listed; +1 region congruence
192-
`S_Region_Step` belongs to cluster C.)
193360

194-
#### Cluster C — region / compound-value (~6)
195361

196362
| Step rule | What's needed |
197363
|-----------|---------------|

0 commit comments

Comments
 (0)