Skip to content

Commit b141752

Browse files
feat(proofs): statement typing judgment + metatheory (Lean + Coq) (#91)
## What Statements were **declared** (`Stmt`/`stmt`) in both provers but had **no typing rules** — so `progress`/`preservation` never saw them. This adds a context-threading **statement typing judgment** and its core metatheory, in **both** Lean (`WokeLang.lean`) and Coq (`WokeLang.v`), at parity. It directly retires the AUDIT.md row *"Statements: declared, no typing/exec."* ## Judgment (mutually inductive with the block form) - `StmtWellTyped Γ s Γ'` — a single statement threads the type context - `StmtsWellTyped Γ ss Γ'` — a block threads the context left-to-right All nine forms: `varDecl` (extends the context), `assign` (requires a prior declaration at the assigned type), `return`, `if`, `loop`, `attempt`, `consent`, `expr`, `complain`. Compound statements are **block-scoped** (they return the incoming context). This is the statement-level analogue of the expression `has_type`/`HasType`. ## Metatheorems (all proved) - `ctxDomSub_{refl,trans,extend}` — the context-domain preorder + the extension fact - `stmt_wellTyped_mono` / `stmts_wellTyped_mono` — **context monotonicity**: a well-typed statement/block never undeclares a variable - `stmts_wellTyped_append` — **sequencing composes**: typing one block then another from the resulting context types their concatenation - `stmts_wellTyped_example` — inhabitation smoke (`let x = 0; x`) ## Verification - **Lean 4.30.0:** `lean WokeLang.lean` → exit 0, no warnings, `sorry`-free. - **Coq 8.18.0:** `coqc WokeLang.v` → exit 0; `Print Assumptions` on all four statement lemmas reports **"Closed under the global context"** — i.e. axiom-free (they don't even touch the classical-reals axioms the float fragment needs). - Purely **additive** (new §5b in each file); existing theorems untouched. ## Method note The single-statement metatheorems are non-recursive (blocks are scoped), which breaks the mutual dependency; the block versions then go by ordinary **induction on the list**, sidestepping a mutual-induction scheme in both provers. ## Scope / next step (honest) This is the **static** story (typing + structural metatheory). The **dynamic** story — a statement execution relation + a store-typing *preservation* theorem — is the natural follow-up. Its prerequisite is generalising the expression `preservation`/`type_safety` to **open** terms (non-empty context + a store-typing agreement), since statement-embedded expressions reference declared variables and the current expression proofs assume the empty context. This is recorded in AUDIT.md ("Recommended next proof steps" #3). 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_013wg3Mtq2QFhYi4XVw1Z6z7 --- _Generated by [Claude Code](https://claude.ai/code/session_013wg3Mtq2QFhYi4XVw1Z6z7)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 9bb35ee commit b141752

3 files changed

Lines changed: 281 additions & 3 deletions

File tree

docs/proofs/verification/AUDIT.md

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ the Rust type checker (`src/typechecker/mod.rs`).
6565
| Result type | `okay`/`oops`/`unwrap`/`error`, `tOkay…tUnwrap` | present in **Rust** typechecker (`Result(_, _)`); **absent** from `core/eval.ml` |
6666
| Unary | `neg` (int/float), `not` | + measured propagation |
6767
| Calls / arrays | `call` (no typing rule); **arrays typed + evaluated** (`tArray`/`tArrayVal`, `sArrayStep`/`sArrayVal`/`sArrayErr`) | builtins + user functions; arrays typed |
68-
| Statements | declared (`Stmt`), **no typing/exec** | full eval in `core/eval.ml` |
68+
| Statements | **typing judgment** (`StmtWellTyped`, context-threading) + monotonicity/append metatheorems; execution/preservation not yet | full eval in `core/eval.ml` |
6969
| Units of measure | **not modelled** | `EMeasured` / `VMeasured`, unit-match checking |
7070
| Pattern matching, workers, custom types | **not modelled** | present |
7171
| Consent | `consent_monotonicity/preservation` | matches **spec** `axiomatic-semantics.md` (consent Hoare logic) |
@@ -113,9 +113,17 @@ the implementations toward it, or (b) build a *second* Lean model faithful to
113113
**DONE (2026-06-18).** `tArray`/`tArrayVal` + `sArrayStep`/`sArrayVal`/
114114
`sArrayErr`, with full `progress`/`preservation` coverage (see
115115
"Extensions landed" below). This brings Lean to parity with Coq on arrays.
116-
3. **Decide the eval-correspondence question** (a) vs (b) above with the
116+
3. ~~Statement typing~~**DONE (2026-06-18):** `StmtWellTyped`/
117+
`StmtsWellTyped` + monotonicity/append (see "Statement typing landed"
118+
below). **Next on statements: a dynamic story** — a statement execution
119+
relation + a store-typing *preservation* theorem. The prerequisite is
120+
generalising the expression `preservation`/`type_safety` to **open** terms
121+
(non-empty context + a store-typing agreement), since statement-embedded
122+
expressions reference declared variables (the current expression proofs
123+
assume the empty context, deriving a contradiction in the `var` case).
124+
4. **Decide the eval-correspondence question** (a) vs (b) above with the
117125
maintainer before attempting PROOF-NEEDS #2.
118-
4. **Compiler/VM track** (the file's §8 TODO stubs: bytecode, compiler,
126+
5. **Compiler/VM track** (the file's §8 TODO stubs: bytecode, compiler,
119127
VM semantics, compiler-correctness) remains open and is a larger effort.
120128

121129
## Echo-types design compatibility (checked 2026-06-14)
@@ -155,6 +163,38 @@ theorems (`weaken_collapses_distinction`, `affine_canonical`,
155163
- Still open in Tier 1: **float** arithmetic variants (`sub`/`mul`/`div` on
156164
`float`, mirroring `add` on float).
157165

166+
## Statement typing landed (2026-06-18) — both provers, in parity
167+
168+
Statements went from *declared datatype with no judgments* to a typed
169+
sub-language with machine-checked metatheory, in **both** Lean (`WokeLang.lean`)
170+
and Coq (`WokeLang.v`):
171+
172+
- **Judgment** `StmtWellTyped Γ s Γ'` / `StmtsWellTyped Γ ss Γ'` — a
173+
context-threading typing relation over all nine statement forms (`varDecl`,
174+
`assign`, `return`, `if`, `loop`, `attempt`, `consent`, `expr`, `complain`),
175+
mutually inductive (blocks contain statements). `varDecl` extends the
176+
context; `assign` requires a prior declaration at the assigned type;
177+
compound statements are block-scoped (they return the incoming context).
178+
This is the statement-level analogue of the expression `has_type`/`HasType`.
179+
- **Metatheorems (all proved):**
180+
- `ctxDomSub_{refl,trans,extend}` — the context-domain preorder + the
181+
extension fact;
182+
- `stmt_wellTyped_mono` / `stmts_wellTyped_mono`**context monotonicity**:
183+
a well-typed statement (or block) never undeclares a variable;
184+
- `stmts_wellTyped_append`**sequencing composes**: typing one block then
185+
another from the resulting context types their concatenation;
186+
- `stmts_wellTyped_example` — an inhabitation smoke (`let x = 0; x`).
187+
- **Method note:** the single-statement metatheorems are non-recursive (blocks
188+
are scoped), which breaks the mutual dependency; the block versions then go
189+
by ordinary induction on the *list* (sidestepping a mutual-induction scheme).
190+
- **Soundness:** Lean is `sorry`-free; in Coq `Print Assumptions` reports all
191+
four statement lemmas **"Closed under the global context"** — i.e. axiom-free
192+
(they do not touch the classical-reals axioms the float fragment needs).
193+
194+
**Still open on statements:** the *dynamic* story — an execution relation +
195+
store-typing preservation — which first needs open-term expression
196+
preservation (see "Recommended next proof steps" #3).
197+
158198
## Arrays landed (2026-06-18) — Lean now at parity with Coq
159199

160200
The one array-shaped Tier-1 gap is closed on the Lean side, mirroring Coq's

docs/proofs/verification/WokeLang.lean

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1171,6 +1171,120 @@ theorem type_safety : ∀ e t v ρ,
11711171
| step e₁ e₂ e₃ ρ₁ ρ₂ ρ₃ hs hms' ih =>
11721172
exact ih (preservation e₁ e₂ t ρ₁ ρ₂ ht hs) hev
11731173

1174+
-- =========================================================================
1175+
-- 5b. Statement Typing
1176+
-- =========================================================================
1177+
1178+
/- Statement typing threads a type context: `StmtWellTyped Γ s Γ'` reads
1179+
"in context `Γ`, statement `s` is well-typed and yields context `Γ'`".
1180+
`varDecl` extends the context with the declared binding; `assign` requires
1181+
the variable to already be declared at the assigned type; compound
1182+
statements (`if`/`loop`/`attempt`/`consent`) are block-scoped, so they
1183+
return the incoming context. Mutually inductive with `StmtsWellTyped`,
1184+
which threads the context left-to-right through a block.
1185+
1186+
This is the statement-level analogue of the expression `HasType` judgment;
1187+
statements were previously declared (`Stmt`) but had no typing rules. The
1188+
dynamic story (a statement execution relation + a store-typing preservation
1189+
theorem) is the natural next step and is noted in AUDIT.md. -/
1190+
mutual
1191+
inductive StmtWellTyped : TypeEnv → Stmt → TypeEnv → Prop where
1192+
| varDecl : ∀ Γ x e t,
1193+
HasType Γ e t →
1194+
StmtWellTyped Γ (.varDecl x e) (extendTypeEnv x t Γ)
1195+
| assign : ∀ Γ x e t,
1196+
Γ x = some t → HasType Γ e t →
1197+
StmtWellTyped Γ (.assign x e) Γ
1198+
| return_ : ∀ Γ e t,
1199+
HasType Γ e t →
1200+
StmtWellTyped Γ (.return_ e) Γ
1201+
| if_ : ∀ Γ c thn els Γ₁ Γ₂,
1202+
HasType Γ c .bool → StmtsWellTyped Γ thn Γ₁ → StmtsWellTyped Γ els Γ₂ →
1203+
StmtWellTyped Γ (.if_ c thn els) Γ
1204+
| loop : ∀ Γ c body Γ₁,
1205+
HasType Γ c .bool → StmtsWellTyped Γ body Γ₁ →
1206+
StmtWellTyped Γ (.loop c body) Γ
1207+
| attempt : ∀ Γ body hname Γ₁,
1208+
StmtsWellTyped Γ body Γ₁ →
1209+
StmtWellTyped Γ (.attempt body hname) Γ
1210+
| consent : ∀ Γ p body Γ₁,
1211+
StmtsWellTyped Γ body Γ₁ →
1212+
StmtWellTyped Γ (.consent p body) Γ
1213+
| expr : ∀ Γ e t,
1214+
HasType Γ e t →
1215+
StmtWellTyped Γ (.expr e) Γ
1216+
| complain : ∀ Γ m,
1217+
StmtWellTyped Γ (.complain m) Γ
1218+
inductive StmtsWellTyped : TypeEnv → List Stmt → TypeEnv → Prop where
1219+
| nil : ∀ Γ, StmtsWellTyped Γ [] Γ
1220+
| cons : ∀ Γ s ss Γ₁ Γ₂,
1221+
StmtWellTyped Γ s Γ₁ → StmtsWellTyped Γ₁ ss Γ₂ →
1222+
StmtsWellTyped Γ (s :: ss) Γ₂
1223+
end
1224+
1225+
/-- Domain containment on type contexts: every variable declared in `Γ` is
1226+
still declared (at some type) in `Γ'`. -/
1227+
def CtxDomSub (Γ Γ' : TypeEnv) : Prop := ∀ x t, Γ x = some t → ∃ t', Γ' x = some t'
1228+
1229+
theorem ctxDomSub_refl (Γ : TypeEnv) : CtxDomSub Γ Γ := fun _ t h => ⟨t, h⟩
1230+
1231+
theorem ctxDomSub_trans {Γ₁ Γ₂ Γ₃ : TypeEnv}
1232+
(h₁₂ : CtxDomSub Γ₁ Γ₂) (h₂₃ : CtxDomSub Γ₂ Γ₃) : CtxDomSub Γ₁ Γ₃ :=
1233+
fun x t h => let ⟨t', h'⟩ := h₁₂ x t h; h₂₃ x t' h'
1234+
1235+
theorem ctxDomSub_extend (Γ : TypeEnv) (x : String) (t : WokeType) :
1236+
CtxDomSub Γ (extendTypeEnv x t Γ) := by
1237+
intro y ty hy
1238+
by_cases hxy : x = y
1239+
· exact ⟨t, by simp [extendTypeEnv, hxy]⟩
1240+
· exact ⟨ty, by simp [extendTypeEnv, hxy, hy]⟩
1241+
1242+
/-- Context monotonicity (single statement): a well-typed statement never
1243+
undeclares a variable. Non-recursive — compound statements are
1244+
block-scoped, which breaks the mutual dependency for the proof. -/
1245+
theorem stmt_wellTyped_mono {Γ s Γ'} (h : StmtWellTyped Γ s Γ') : CtxDomSub Γ Γ' := by
1246+
cases h with
1247+
| varDecl Γ x e t _ => exact ctxDomSub_extend Γ x t
1248+
| assign => exact ctxDomSub_refl _
1249+
| return_ => exact ctxDomSub_refl _
1250+
| if_ => exact ctxDomSub_refl _
1251+
| loop => exact ctxDomSub_refl _
1252+
| attempt => exact ctxDomSub_refl _
1253+
| consent => exact ctxDomSub_refl _
1254+
| expr => exact ctxDomSub_refl _
1255+
| complain => exact ctxDomSub_refl _
1256+
1257+
/-- Context monotonicity (statement block), by induction on the block. -/
1258+
theorem stmts_wellTyped_mono {Γ ss Γ'} (h : StmtsWellTyped Γ ss Γ') : CtxDomSub Γ Γ' := by
1259+
induction ss generalizing Γ Γ' with
1260+
| nil => cases h; exact ctxDomSub_refl _
1261+
| cons s rest ih =>
1262+
cases h with
1263+
| cons _ _ _ Γ₁ _ hs hrest => exact ctxDomSub_trans (stmt_wellTyped_mono hs) (ih hrest)
1264+
1265+
/-- Sequencing composes: typing a block then another block from the resulting
1266+
context types their concatenation. -/
1267+
theorem stmts_wellTyped_append {Γ ss₁ Γ' ss₂ Γ''}
1268+
(h₁ : StmtsWellTyped Γ ss₁ Γ') (h₂ : StmtsWellTyped Γ' ss₂ Γ'') :
1269+
StmtsWellTyped Γ (ss₁ ++ ss₂) Γ'' := by
1270+
induction ss₁ generalizing Γ Γ' with
1271+
| nil => cases h₁; exact h₂
1272+
| cons s rest ih =>
1273+
cases h₁ with
1274+
| cons _ _ _ Γ₁ _ hs hrest => exact .cons _ _ _ _ _ hs (ih hrest h₂)
1275+
1276+
/-- Inhabitation smoke: `let x = 0; x` is a well-typed block, yielding a
1277+
context in which `x : int`. -/
1278+
theorem stmts_wellTyped_example :
1279+
StmtsWellTyped emptyTypeEnv
1280+
[Stmt.varDecl "x" (.lit (.vInt 0)), Stmt.expr (.var "x")]
1281+
(extendTypeEnv "x" .int emptyTypeEnv) :=
1282+
.cons _ _ _ _ _
1283+
(.varDecl _ "x" (.lit (.vInt 0)) .int (.tInt _ _))
1284+
(.cons _ _ _ _ _
1285+
(.expr _ (.var "x") .int (.tVar _ "x" .int (by simp [extendTypeEnv])))
1286+
(.nil _))
1287+
11741288
-- =========================================================================
11751289
-- 6. Consent System
11761290
-- =========================================================================

docs/proofs/verification/WokeLang.v

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1398,6 +1398,130 @@ Proof.
13981398
eapply preservation; eauto.
13991399
Qed.
14001400

1401+
(* ========================================================================= *)
1402+
(* 5b. Statement Typing *)
1403+
(* ========================================================================= *)
1404+
1405+
(** Statement typing threads a type context: [StmtWellTyped G s G'] reads
1406+
"in context [G], statement [s] is well-typed and yields context [G']".
1407+
[SVarDecl] extends the context; [SAssign] requires the variable already
1408+
declared at the assigned type; compound statements ([SIf]/[SLoop]/
1409+
[SAttempt]/[SConsent]) are block-scoped, so they return the incoming
1410+
context. Mutually inductive with [StmtsWellTyped] for blocks. Mirrors
1411+
StmtWellTyped/StmtsWellTyped in WokeLang.lean (cross-prover parity).
1412+
1413+
This is the statement-level analogue of the [has_type] expression
1414+
judgment; statements were previously declared ([stmt]) but had no typing
1415+
rules. A statement execution relation + store-typing preservation is the
1416+
natural next step (see AUDIT.md). *)
1417+
1418+
Inductive StmtWellTyped : type_env -> stmt -> type_env -> Prop :=
1419+
| SWT_VarDecl : forall G x e t,
1420+
has_type G e t ->
1421+
StmtWellTyped G (SVarDecl x e) (extend_type_env x t G)
1422+
| SWT_Assign : forall G x e t,
1423+
G x = Some t -> has_type G e t ->
1424+
StmtWellTyped G (SAssign x e) G
1425+
| SWT_Return : forall G e t,
1426+
has_type G e t ->
1427+
StmtWellTyped G (SReturn e) G
1428+
| SWT_If : forall G c thn els G1 G2,
1429+
has_type G c TBool ->
1430+
StmtsWellTyped G thn G1 -> StmtsWellTyped G els G2 ->
1431+
StmtWellTyped G (SIf c thn els) G
1432+
| SWT_Loop : forall G c body G1,
1433+
has_type G c TBool -> StmtsWellTyped G body G1 ->
1434+
StmtWellTyped G (SLoop c body) G
1435+
| SWT_Attempt : forall G body h G1,
1436+
StmtsWellTyped G body G1 ->
1437+
StmtWellTyped G (SAttempt body h) G
1438+
| SWT_Consent : forall G p body G1,
1439+
StmtsWellTyped G body G1 ->
1440+
StmtWellTyped G (SConsent p body) G
1441+
| SWT_Expr : forall G e t,
1442+
has_type G e t ->
1443+
StmtWellTyped G (SExpr e) G
1444+
| SWT_Complain : forall G m,
1445+
StmtWellTyped G (SComplain m) G
1446+
1447+
with StmtsWellTyped : type_env -> list stmt -> type_env -> Prop :=
1448+
| SsWT_nil : forall G, StmtsWellTyped G nil G
1449+
| SsWT_cons : forall G s ss G1 G2,
1450+
StmtWellTyped G s G1 -> StmtsWellTyped G1 ss G2 ->
1451+
StmtsWellTyped G (s :: ss) G2.
1452+
1453+
(** Domain containment: every variable declared in [G] is still declared
1454+
(at some type) in [G']. *)
1455+
Definition CtxDomSub (G G' : type_env) : Prop :=
1456+
forall x t, G x = Some t -> exists t', G' x = Some t'.
1457+
1458+
Lemma ctxDomSub_refl : forall G, CtxDomSub G G.
1459+
Proof. intros G x t H. exists t. exact H. Qed.
1460+
1461+
Lemma ctxDomSub_trans : forall G1 G2 G3,
1462+
CtxDomSub G1 G2 -> CtxDomSub G2 G3 -> CtxDomSub G1 G3.
1463+
Proof.
1464+
intros G1 G2 G3 H12 H23 x t H.
1465+
destruct (H12 x t H) as [t' H']. exact (H23 x t' H').
1466+
Qed.
1467+
1468+
Lemma ctxDomSub_extend : forall G x t, CtxDomSub G (extend_type_env x t G).
1469+
Proof.
1470+
intros G x t y ty Hy. unfold extend_type_env.
1471+
destruct (String.eqb x y) eqn:Hxy.
1472+
- exists t. reflexivity.
1473+
- exists ty. exact Hy.
1474+
Qed.
1475+
1476+
(** Context monotonicity (single statement): a well-typed statement never
1477+
undeclares a variable. *)
1478+
Lemma stmt_wellTyped_mono : forall G s G',
1479+
StmtWellTyped G s G' -> CtxDomSub G G'.
1480+
Proof.
1481+
intros G s G' H. inversion H; subst;
1482+
first [ apply ctxDomSub_refl | apply ctxDomSub_extend ].
1483+
Qed.
1484+
1485+
(** Context monotonicity (statement block), by induction on the block. *)
1486+
Lemma stmts_wellTyped_mono : forall ss G G',
1487+
StmtsWellTyped G ss G' -> CtxDomSub G G'.
1488+
Proof.
1489+
induction ss as [| s rest IH]; intros G G' H; inversion H; subst.
1490+
- apply ctxDomSub_refl.
1491+
- eapply ctxDomSub_trans.
1492+
+ eapply stmt_wellTyped_mono; eassumption.
1493+
+ eapply IH; eassumption.
1494+
Qed.
1495+
1496+
(** Sequencing composes: typing a block then another from the resulting
1497+
context types their concatenation. *)
1498+
Lemma stmts_wellTyped_append : forall ss1 G G' ss2 G'',
1499+
StmtsWellTyped G ss1 G' -> StmtsWellTyped G' ss2 G'' ->
1500+
StmtsWellTyped G (ss1 ++ ss2) G''.
1501+
Proof.
1502+
induction ss1 as [| s rest IH]; intros G G' ss2 G'' H1 H2;
1503+
inversion H1; subst; simpl.
1504+
- exact H2.
1505+
- eapply SsWT_cons.
1506+
+ eassumption.
1507+
+ eapply IH; eassumption.
1508+
Qed.
1509+
1510+
(** Inhabitation smoke: [let x = 0; x] is a well-typed block, yielding a
1511+
context in which [x : TInt]. *)
1512+
Example stmts_wellTyped_example :
1513+
StmtsWellTyped empty_type_env
1514+
(SVarDecl "x"%string (ELit (VInt 0)) :: SExpr (EVar "x"%string) :: nil)
1515+
(extend_type_env "x"%string TInt empty_type_env).
1516+
Proof.
1517+
eapply SsWT_cons.
1518+
- apply SWT_VarDecl with (t := TInt). apply T_Int.
1519+
- eapply SsWT_cons.
1520+
+ apply SWT_Expr with (t := TInt). apply T_Var.
1521+
unfold extend_type_env. rewrite String.eqb_refl. reflexivity.
1522+
+ apply SsWT_nil.
1523+
Qed.
1524+
14011525
(* ========================================================================= *)
14021526
(* 6. Consent System *)
14031527
(* ========================================================================= *)

0 commit comments

Comments
 (0)