Skip to content

Commit cb54027

Browse files
Phase 1 core: the Substitution Lemma (type-safety.md Lemma 3.4) (#111)
Continues Tier 1 of the verification roadmap with the foundational piece of Phase 1. ## `subst_preserves_typing` — the Substitution Lemma Mechanizes type-safety.md **Lemma 3.4** on the existing hole-free `WokeLang.lean`: > If `e` is well typed under `Γ` extended with `x : tx`, and `v` is a value of type `tx` under `Γ`, then `subst x v e` is well typed under `Γ` at the same type. - Adds `subst : String → Value → Expr → Expr` — capture-free (values are closed; `Expr` has no binders). - Proof by induction on the **typing derivation** with the context generalized — all ~30 typing rules; the `tArray` case uses the per-element IH + `List.mem_map`, and the `tVar` case does the `x : tx`/`y : t'` lookup split. - Axioms: `propext`, `Quot.sound`. No holes. `WokeLang.lean` still compiles hole-free (CI-gated). This is the prerequisite the type-theory survey called **"the single biggest gap"** — it unblocks `[T-Call]`'s preservation case and Algorithm W soundness (roadmap Phase 3a). ## Roadmap finding (refined accordingly) While building this I confirmed `[T-Call]` is **not** the assumed simple body-substitution: `Expr.call` is **by name** and `TopItem.functionDef` bodies are **statement lists**, so call reduction entangles with statement *execution* and belongs with **Phase 1b** (statement dynamics), not this step. There's also no `index` AST constructor (so `[T-Index]` needs an AST extension). The substitution lemma is the clean, self-contained piece deliverable now; the rest of Phase 1 sequences after statement dynamics. `VERIFICATION-ROADMAP.md` progress updated. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_015oyMquf4daB6hMhmqB1wAL --- _Generated by [Claude Code](https://claude.ai/code/session_015oyMquf4daB6hMhmqB1wAL)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent 712b58a commit cb54027

2 files changed

Lines changed: 92 additions & 1 deletion

File tree

docs/proofs/VERIFICATION-ROADMAP.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,16 @@ unlock Phases 1c / 3a / 4a.
127127
`hasCapability_subsumes`). Coq parity (`cap_subsumes_trans`/`_antisymm`) remains
128128
a deliberate follow-up — the author left it unproven rather than ship fragile
129129
6×6×6 automation; a clean Coq proof wants a `cap_subsumes` characterization lemma.
130-
- [ ] Phase 1, 1b, 1c — type-safety + operational metatheory.
130+
- [~] **Phase 1 (partial)** — the **Substitution Lemma** (`subst` + `subst_preserves_typing`,
131+
type-safety.md Lemma 3.4) is mechanized on the hole-free core, by induction on the
132+
typing derivation with the context generalized (axioms `propext`, `Quot.sound`). This is
133+
the prerequisite for `[T-Call]`'s preservation case and for Algorithm W soundness (3a).
134+
*Finding:* `[T-Call]` dynamics here are NOT the assumed simple body-substitution —
135+
`Expr.call` is by name and `TopItem.functionDef` bodies are **statement lists**, so call
136+
reduction entangles with statement *execution* and belongs with **Phase 1b** (statement
137+
dynamics), not this step. There is also no `index` constructor, so `[T-Index]` requires an
138+
AST extension. Roadmap refined accordingly.
139+
- [ ] Phase 1 (cont.: `[T-Call]` after 1b), 1b, 1c — type-safety + operational metatheory.
131140
- [ ] Phase 2b, 2c — consent + capability state machines.
132141
- [ ] Phase 3a (+3b) — HM inference.
133142
- [ ] Phase 4a–4c — compiler / parser / WASM.

docs/proofs/verification/WokeLang.lean

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1391,6 +1391,88 @@ theorem hasCapability_subsumes {c c' : Capability} {cs : CapabilitySet}
13911391
obtain ⟨d, hmem, hd⟩ := h
13921392
exact ⟨d, hmem, capSubsumes_trans d c' c hd hsub⟩
13931393

1394+
-- =========================================================================
1395+
-- 7b. Substitution lemma (roadmap Phase 1 core / type-safety.md Lemma 3.4)
1396+
-- =========================================================================
1397+
1398+
/-- Substitute the (closed) value `v` for the free variable `x` in an expression.
1399+
Capture-avoiding is trivial here: values are closed, and `Expr` has no binders. -/
1400+
def subst (x : String) (v : Value) : Expr → Expr
1401+
| .lit w => .lit w
1402+
| .var y => if x == y then .lit v else .var y
1403+
| .binOp op a b => .binOp op (subst x v a) (subst x v b)
1404+
| .unOp op a => .unOp op (subst x v a)
1405+
| .call f args => .call f (args.map (subst x v))
1406+
| .array es => .array (es.map (subst x v))
1407+
| .okay a => .okay (subst x v a)
1408+
| .oops a => .oops (subst x v a)
1409+
| .unwrap a => .unwrap (subst x v a)
1410+
| .error msg => .error msg
1411+
1412+
/-- **Substitution Lemma.** If `e` is well typed under `Γ` extended with `x : tx`,
1413+
and `v` is a value of type `tx` under `Γ`, then `subst x v e` is well typed under
1414+
`Γ` at the same type. Proved by induction on the typing derivation, with the
1415+
context generalized. This is the prerequisite for `[T-Call]`'s preservation case
1416+
and for Algorithm W's soundness (roadmap Phases 1, 3a). -/
1417+
theorem subst_preserves_typing {x : String} {v : Value} {tx : WokeType} :
1418+
∀ {Δ e t}, HasType Δ e t → ∀ {Γ}, Δ = extendTypeEnv x tx Γ →
1419+
HasType Γ (.lit v) tx → HasType Γ (subst x v e) t := by
1420+
intro Δ e t h
1421+
induction h with
1422+
| tInt n => intro Γ _ _; simp only [subst]; exact .tInt _ _
1423+
| tFloat f => intro Γ _ _; simp only [subst]; exact .tFloat _ _
1424+
| tString s => intro Γ _ _; simp only [subst]; exact .tString _ _
1425+
| tBool b => intro Γ _ _; simp only [subst]; exact .tBool _ _
1426+
| tUnit => intro Γ _ _; simp only [subst]; exact .tUnit _
1427+
| tOkayVal w t' _ ih =>
1428+
intro Γ hΔ hv; simp only [subst]
1429+
exact .tOkayVal _ _ _ (by simpa only [subst] using ih hΔ hv)
1430+
| tOopsVal s t' => intro Γ _ _; simp only [subst]; exact .tOopsVal _ _ _
1431+
| tVar y t' hy =>
1432+
intro Γ hΔ hv; subst hΔ
1433+
by_cases hyx : (x == y) = true
1434+
· rw [show subst x v (.var y) = .lit v from by simp only [subst]; rw [if_pos hyx]]
1435+
have he : extendTypeEnv x tx Γ y = some tx := by
1436+
simp only [extendTypeEnv]; rw [if_pos hyx]
1437+
rw [he] at hy; injection hy with hty; subst hty; exact hv
1438+
· rw [show subst x v (.var y) = .var y from by simp only [subst]; rw [if_neg hyx]]
1439+
refine .tVar _ _ _ ?_
1440+
have he : extendTypeEnv x tx Γ y = Γ y := by
1441+
simp only [extendTypeEnv]; rw [if_neg hyx]
1442+
rw [he] at hy; exact hy
1443+
| tAddInt a b _ _ ih₁ ih₂ => intro Γ hΔ hv; simp only [subst]; exact .tAddInt _ _ _ (ih₁ hΔ hv) (ih₂ hΔ hv)
1444+
| tAddFloat a b _ _ ih₁ ih₂ => intro Γ hΔ hv; simp only [subst]; exact .tAddFloat _ _ _ (ih₁ hΔ hv) (ih₂ hΔ hv)
1445+
| tAddString a b _ _ ih₁ ih₂ => intro Γ hΔ hv; simp only [subst]; exact .tAddString _ _ _ (ih₁ hΔ hv) (ih₂ hΔ hv)
1446+
| tEq a b t' _ _ ih₁ ih₂ => intro Γ hΔ hv; simp only [subst]; exact .tEq _ _ _ _ (ih₁ hΔ hv) (ih₂ hΔ hv)
1447+
| tAnd a b _ _ ih₁ ih₂ => intro Γ hΔ hv; simp only [subst]; exact .tAnd _ _ _ (ih₁ hΔ hv) (ih₂ hΔ hv)
1448+
| tOr a b _ _ ih₁ ih₂ => intro Γ hΔ hv; simp only [subst]; exact .tOr _ _ _ (ih₁ hΔ hv) (ih₂ hΔ hv)
1449+
| tSubInt a b _ _ ih₁ ih₂ => intro Γ hΔ hv; simp only [subst]; exact .tSubInt _ _ _ (ih₁ hΔ hv) (ih₂ hΔ hv)
1450+
| tMulInt a b _ _ ih₁ ih₂ => intro Γ hΔ hv; simp only [subst]; exact .tMulInt _ _ _ (ih₁ hΔ hv) (ih₂ hΔ hv)
1451+
| tLt a b _ _ ih₁ ih₂ => intro Γ hΔ hv; simp only [subst]; exact .tLt _ _ _ (ih₁ hΔ hv) (ih₂ hΔ hv)
1452+
| tGt a b _ _ ih₁ ih₂ => intro Γ hΔ hv; simp only [subst]; exact .tGt _ _ _ (ih₁ hΔ hv) (ih₂ hΔ hv)
1453+
| tLe a b _ _ ih₁ ih₂ => intro Γ hΔ hv; simp only [subst]; exact .tLe _ _ _ (ih₁ hΔ hv) (ih₂ hΔ hv)
1454+
| tGe a b _ _ ih₁ ih₂ => intro Γ hΔ hv; simp only [subst]; exact .tGe _ _ _ (ih₁ hΔ hv) (ih₂ hΔ hv)
1455+
| tDivInt a b _ _ ih₁ ih₂ => intro Γ hΔ hv; simp only [subst]; exact .tDivInt _ _ _ (ih₁ hΔ hv) (ih₂ hΔ hv)
1456+
| tModInt a b _ _ ih₁ ih₂ => intro Γ hΔ hv; simp only [subst]; exact .tModInt _ _ _ (ih₁ hΔ hv) (ih₂ hΔ hv)
1457+
| tNegInt a _ ih => intro Γ hΔ hv; simp only [subst]; exact .tNegInt _ _ (ih hΔ hv)
1458+
| tNegFloat a _ ih => intro Γ hΔ hv; simp only [subst]; exact .tNegFloat _ _ (ih hΔ hv)
1459+
| tNot a _ ih => intro Γ hΔ hv; simp only [subst]; exact .tNot _ _ (ih hΔ hv)
1460+
| tOkay a t' _ ih => intro Γ hΔ hv; simp only [subst]; exact .tOkay _ _ _ (ih hΔ hv)
1461+
| tOops a t' _ ih => intro Γ hΔ hv; simp only [subst]; exact .tOops _ _ _ (ih hΔ hv)
1462+
| tUnwrap a tOk tErr _ ih => intro Γ hΔ hv; simp only [subst]; exact .tUnwrap _ _ _ _ (ih hΔ hv)
1463+
| tError msg t' => intro Γ _ _; simp only [subst]; exact .tError _ _ _
1464+
| tArray es t' _ ih =>
1465+
intro Γ hΔ hv
1466+
simp only [subst]
1467+
refine .tArray _ _ t' (fun e' he' => ?_)
1468+
rw [List.mem_map] at he'
1469+
obtain ⟨e0, he0, rfl⟩ := he'
1470+
exact ih e0 he0 hΔ hv
1471+
| tArrayVal vs t' _ ih =>
1472+
intro Γ hΔ hv
1473+
simp only [subst]
1474+
exact .tArrayVal _ _ _ (fun w hw => by simpa only [subst] using ih w hw hΔ hv)
1475+
13941476
-- =========================================================================
13951477
-- 8. TODO Stubs
13961478
-- =========================================================================

0 commit comments

Comments
 (0)