Skip to content

Commit 74fe03a

Browse files
Phase 1 scaffold for Lemma B (step_output_context_eq) (#124)
## Summary This PR introduces the Phase 1 scaffold for `step_output_context_eq` (Lemma B), which proves that a single step preserves the output context. The lemma body is stated with a `cfg`-remember pattern and an atomic-axiom closure tactic, with 31 of 35 step-rule cases remaining as `admit`. ## Key Changes - **New lemma `subst_preserves_typing_strong`**: A strengthened variant of `subst_preserves_typing` that exposes the specific output context (instead of hiding it behind an existential). This is needed by the 7 β-reduction step cases (`S_Let_Val`, `S_LetLin_Val`, `S_App_Fun`, `S_If_True/False`, `S_Case_Inl/Inr`) where the existential in the original lemma blocks output-context equality proofs. - **`step_output_context_eq` Phase 1 scaffold**: Introduced with the `cfg`-remember pattern (mirroring `step_R_eq_or_touches_region` and `preservation`) plus an atomic-axiom closure tactic. This closes 4 of 35 step rules: - `S_StringNew`, `S_StringConcat`, `S_Drop` (atomic cases with identity-output typing rules) - `S_Borrow_Step` (accidental congruence closure: both `T_Borrow` and `T_Borrow_Val` preserve the input context) - **Documentation**: Added detailed per-case status map in `formal/PRESERVATION-HANDOFF.md` clustering the remaining 31 cases into three groups: - **Cluster A** (~7): β-reduction cases needing `subst_preserves_typing_strong` - **Cluster B** (~18): congruence cases needing `step_R_eq_or_touches_region` dispatch + IH + recursive Lemma B - **Cluster C** (~6): region/compound-value cases with mixed closure requirements - **Effort revision**: Updated ROADMAP from "3–4 hours" to "8–15 focused hours" based on empirical baseline from `coqc 8.18.0`. ## Notable Implementation Details - `subst_preserves_typing_strong` leverages the explicit `remove_at k Gout` shape already present in `subst_typing_gen`, which collapses to `G` for `k = 0` via `remove_at 0 ((T, b) :: G) = G`. - The atomic-axiom closure tactic uses a `repeat match` to force re-inversion of leaf values (`ELoc`, `EI32`, `EUnit`, `EBool`) to pin their output contexts to the input. - Identified circularity risk: some Cluster B cases may need "step preserves type" before applying the IH, but that lemma is part of preservation itself. https://claude.ai/code/session_01USr4ovnvN6SLrwi6z9MUhQ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 8617b04 commit 74fe03a

3 files changed

Lines changed: 214 additions & 11 deletions

File tree

ROADMAP.adoc

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,14 @@ up the proof should follow.
188188
| After per-case manual closures
189189
| **12**
190190
| 10 β-reduction / value-step cases discharged. PR #116.
191+
192+
| Phase 1 scaffold (Lemma B stated, body `Admitted`)
193+
| 12
194+
| `step_output_context_eq` introduced. PR #121.
195+
196+
| Phase 1 empirical baseline (2026-05-24)
197+
| 12 (preservation) / **31 of 35** (Lemma B itself)
198+
| `cfg`-remember pattern + atomic-axiom tactic on Lemma B closes 4 of its 35 step-rule cases (`S_StringNew`, `S_StringConcat`, `S_Drop`, `S_Borrow_Step`); the remaining 31 cluster across β-reduction / congruence / region-and-compound-value branches. Per-case map in `formal/PRESERVATION-HANDOFF.md`.
191199
|===
192200

193201
98.7% reduction across one day. Remaining 12 = 11 congruence cases
@@ -218,11 +226,31 @@ via `G' = G_out`.
218226
**Approach**: induction on `step` with `type_determinacy` per case.
219227
Same pattern as `step_R_eq_or_touches_region`.
220228

221-
**Effort**: 3–4 hours focused session.
222-
223-
**Risk**: `G_end = G_out` may need weakening to "context-equivalent up
224-
to flag positions" via the existing `types_agree` machinery. If so,
225-
the lemma is slightly harder but still tractable.
229+
**Effort**: ~~3–4 hours focused session~~ → **8–15 focused hours**
230+
(revised 2026-05-24). Empirical baseline from `coqc 8.18.0`: the
231+
`cfg`-remember + atomic-axiom tactic closes 4 of 35 step rules
232+
(`S_StringNew`, `S_StringConcat`, `S_Drop`, `S_Borrow_Step`); the
233+
remaining 31 split across 3 clusters needing distinct recipes. Full
234+
per-case map in `formal/PRESERVATION-HANDOFF.md` §"Lemma B per-case
235+
status".
236+
237+
**Risk**:
238+
239+
. **Type-determinacy across stepped pairs**: some Cluster B
240+
congruence cases need to know `e` and `e'` have the same type
241+
before applying the IH. `type_determinacy` only handles
242+
same-expression pairs; the natural "step preserves type" lemma
243+
is part of preservation itself. Watch for circularity when
244+
attacking the first congruence case.
245+
. **Output-context equality may need weakening** to
246+
"context-equivalent up to flag positions" via the existing
247+
`types_agree` machinery. If so, the lemma is slightly harder
248+
but still tractable.
249+
. **Strengthened subst lemma needed for Cluster A**: the existing
250+
`subst_preserves_typing` hides the output context behind an
251+
existential; the 7 β-reduction cases need a
252+
`subst_preserves_typing_strong` variant exposing the specific
253+
`remove_at k Gout` shape already present in `subst_typing_gen`.
226254

227255
=== Phase 2 — Apply Lemma B → close 11 congruence cases
228256

formal/PRESERVATION-HANDOFF.md

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,98 @@ 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+
## Lemma B per-case status (2026-05-24)
143+
144+
Empirically verified against `coqc 8.18.0`. The Phase 1 scaffold for
145+
`step_output_context_eq` now uses the `cfg`-remember pattern that
146+
mirrors `step_R_eq_or_touches_region` and `preservation`, plus an
147+
atomic-axiom closure tactic. **4 of 35 step rules close**; 31 remain.
148+
149+
### Closed (4)
150+
151+
| Step rule | Why it closes |
152+
|-----------|---------------|
153+
| `S_StringNew` | atomic: `EStringNew → ELoc`, both type to identity-output |
154+
| `S_StringConcat` | atomic: `EStringConcat (ELoc _) (ELoc _) → ELoc`, all premises invert to identity-output T_Loc |
155+
| `S_Drop` | atomic: `EDrop (ELoc _) → EUnit`, both T_Drop and T_Unit are identity-output |
156+
| `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. |
157+
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.
186+
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.)
193+
194+
#### Cluster C — region / compound-value (~6)
195+
196+
| Step rule | What's needed |
197+
|-----------|---------------|
198+
| `S_Region_Enter` | `T_Region``T_Region_Active` dispatch via `In r R`; needs `output_ctx_det` sub-lemma on `e_inner` |
199+
| `S_Region_Exit` | needs `region_shrink_preserves_typing` (already Qed) + value-output-context invariance |
200+
| `S_Region_Step` | **blocked on Phase 3** — region-env weakening for non-values |
201+
| `S_StringLen` (atomic!) | inversion of `T_StringLen``T_Borrow_Val``T_Loc` chain needs explicit nested invocation; my repeat-match doesn't cover the `EBorrow (ELoc _ _)` shape |
202+
| `S_Copy` | atomic but compound: `T_Copy` outputs `G'` where `G'` is value-input; needs `value_context_unchanged` invocation |
203+
| `S_Fst` / `S_Snd` | atomic but inversion of `T_Pair` premise needs `value_context_unchanged` to align inner v1/v2 typings |
204+
205+
### Effort revision
206+
207+
The ROADMAP's "3-4 hours focused session" estimate for Phase 1 was
208+
optimistic. Empirical evidence:
209+
210+
- 4 trivial cases closed by a uniform tactic in ~30 minutes.
211+
- Each of the remaining 31 needs a hand-rolled per-case tactic block.
212+
- Cluster A (7) needs one shared sub-lemma first
213+
(`subst_preserves_typing_strong`).
214+
- Cluster B (17–18) shares the same recipe but each case names
215+
different premises and constructor arguments — call it ~10 minutes
216+
per case once the recipe is debugged on the first one.
217+
- Cluster C (6) is a mixed bag; `S_Region_Step` carries Phase 3 risk.
218+
219+
**Revised estimate**: **8–15 focused hours** for Lemma B alone,
220+
assuming no circularity surprises with `type_determinacy` across
221+
stepped pairs. Phase 2 (apply Lemma B to preservation's congruence
222+
cases) remains ~2 hours of mechanical wiring once Lemma B is closed.
223+
224+
### Watch for: circularity risk
225+
226+
Some Cluster B cases require knowing that `e` and `e'` have the
227+
same type before applying the IH. `type_determinacy` operates on
228+
same-expression pairs, not stepped pairs. The natural lemma —
229+
"step preserves type" — is part of what preservation itself
230+
proves. If the inductive structure of Lemma B turns out to need
231+
preservation as a sub-lemma, the closure path needs revision.
232+
Watch for this when attacking the first Cluster B case.
233+
142234
## Unwind checklist (when finally closed)
143235

144236
1. Replace `Admitted.` with `Qed.`

formal/Semantics.v

Lines changed: 89 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2826,6 +2826,34 @@ Proof.
28262826
- reflexivity.
28272827
Qed.
28282828

2829+
(** Strengthened variant that exposes the specific output context
2830+
(instead of an existential). Used by [step_output_context_eq]
2831+
(Lemma B, Phase 1) to discharge the beta-reduction step cases
2832+
where the existential in [subst_preserves_typing] blocks the
2833+
output-context equality proof.
2834+
2835+
Both the witness ([G_v = G]) and the specific output context
2836+
([G']) come from [subst_typing_gen]'s explicit
2837+
[remove_at k Gout] shape, which collapses for [k = 0] via
2838+
[remove_at 0 ((T, b) :: G) = G]. *)
2839+
Lemma subst_preserves_typing_strong :
2840+
forall R G e T2 G' T1 v G_v,
2841+
R; (T1, false) :: G |- e : T2 -| (T1, true) :: G' ->
2842+
R; G |- v : T1 -| G_v ->
2843+
is_value v ->
2844+
G_v = G /\ R; G |- subst 0 v e : T2 -| G'.
2845+
Proof.
2846+
intros R G e T2 G' T1 v G_v Htype Hv Hval.
2847+
assert (HGv: G_v = G) by (eapply value_context_unchanged; eassumption).
2848+
split; [exact HGv|].
2849+
subst G_v.
2850+
assert (Hlin: is_linear_ty T1 = true).
2851+
{ eapply (flag_false_to_true_implies_linear _ _ _ _ _ 0 T1 Htype); simpl; reflexivity. }
2852+
pose proof (subst_typing_gen _ _ _ _ _ Htype 0 T1 v false eq_refl Hval Hlin Hv true eq_refl) as Hsubst.
2853+
simpl in Hsubst.
2854+
exact Hsubst.
2855+
Qed.
2856+
28292857
(** Helper: types_agree and false_preserved are reflexive *)
28302858
Lemma ctx_types_agree_refl : forall G, ctx_types_agree G G.
28312859
Proof.
@@ -3318,13 +3346,68 @@ Lemma step_output_context_eq :
33183346
R'; G |- e' : T -| G_b ->
33193347
G_a = G_b.
33203348
Proof.
3349+
(* Phase 1 scaffold (PR #121) + cfg-remember pattern + atomic-axiom
3350+
closure. cfg-remember mirrors [step_R_eq_or_touches_region] and
3351+
[preservation] (PRs #102 / #106): substitutes the outer
3352+
expression slots into each induction case so per-case inversion
3353+
sees concrete syntax. *)
33213354
intros mu R e mu' R' e' Hstep.
3322-
induction Hstep; intros G0 T0 Ga Gb Htype_e Htype_e'.
3323-
(* Attempt: aggressive inversion + auto on each case. The hope is
3324-
that for the syntax-directed typing relation, inversion of both
3325-
typings extracts component typings with matching output
3326-
contexts, which `eauto` then unifies. *)
3327-
all: try (inversion Htype_e; inversion Htype_e'; subst; auto).
3355+
remember (mu, R, e) as cfg eqn:Hcfg.
3356+
remember (mu', R', e') as cfg' eqn:Hcfg'.
3357+
revert mu R e mu' R' e' Hcfg Hcfg'.
3358+
induction Hstep;
3359+
intros mu0 R0 e0 mu0' R0' e0' Hcfg Hcfg';
3360+
inversion Hcfg; subst;
3361+
inversion Hcfg'; subst;
3362+
intros G0 T0 Ga Gb Htype_e Htype_e'.
3363+
(* Atomic-axiom + accidental-congruence cases that close by full
3364+
inversion of both typings + leaf-value re-inversion +
3365+
reflexivity. Empirically (coqc 8.18.0) closes 4 of 35 step
3366+
rules:
3367+
S_StringNew ([EStringNew r s] -> [ELoc l r])
3368+
S_StringConcat ([EStringConcat (ELoc _ _) (ELoc _ _)]
3369+
-> [ELoc l' r])
3370+
S_Drop ([EDrop (ELoc _ _)] -> [EUnit])
3371+
S_Borrow_Step ([EBorrow e] -> [EBorrow e'])
3372+
(closes accidentally: both T_Borrow and T_Borrow_Val output
3373+
the input context unchanged, so Ga = G = Gb regardless of
3374+
the inner step's IH.)
3375+
The leaf re-inversion forces inner [has_type _ _ (ELoc _ _) _ _]
3376+
and friends to pin their output to the input (T_Loc /
3377+
T_StringNew / T_I32 / T_Unit all preserve the linearity
3378+
context). *)
3379+
all: try (
3380+
inversion Htype_e; subst;
3381+
inversion Htype_e'; subst;
3382+
repeat match goal with
3383+
| [ H : has_type _ _ (ELoc _ _) _ _ |- _ ] =>
3384+
inversion H; subst; clear H
3385+
| [ H : has_type _ _ (EI32 _) _ _ |- _ ] =>
3386+
inversion H; subst; clear H
3387+
| [ H : has_type _ _ EUnit _ _ |- _ ] =>
3388+
inversion H; subst; clear H
3389+
| [ H : has_type _ _ (EBool _) _ _ |- _ ] =>
3390+
inversion H; subst; clear H
3391+
end;
3392+
reflexivity).
3393+
(* 31 cases remain. Per-case map: PRESERVATION-HANDOFF.md
3394+
section "Lemma B per-case status". Three clusters:
3395+
- beta-reduction (~7): S_Let_Val, S_LetLin_Val, S_App_Fun,
3396+
S_If_True/False, S_Case_Inl/Inr. Need a strengthened
3397+
subst-output-context lemma (subst_typing_gen already has
3398+
the [remove_at k Gout] shape; subst_preserves_typing hides
3399+
it behind an existential).
3400+
- congruence (~18, every S_*_Step except S_Borrow_Step):
3401+
apply [step_R_eq_or_touches_region] to dispatch [R = R'];
3402+
in the R-eq branch, apply IH for the inner step + recursive
3403+
Lemma B for siblings. RIGHT branch (touches_region) is
3404+
blocked on the same region-env weakening lemma as
3405+
preservation Phase 3.
3406+
- region / compound-value (~6): S_Region_Enter/Exit/Step,
3407+
S_StringLen (atomic, blocked on TBorrow/EStringLen
3408+
inversion shape — does NOT close trivially despite being
3409+
atomic), S_Copy, S_Fst, S_Snd. S_Region_Step blocks on
3410+
Phase 3. *)
33283411
all: admit.
33293412
Admitted.
33303413

0 commit comments

Comments
 (0)