Skip to content

Commit 5a42e6e

Browse files
hyperpolymathhyperpolymathclaude
authored
feat(formal): real-lift R1 — first real ⟦compile p⟧ = ⟦p⟧ (RealCompile.v) (#640)
## What Climbs the **REAL-LIFT ladder** (`formal/REAL-LIFT.adoc`) from **R0 → R1**: the **first mechanized `⟦compile p⟧ = ⟦p⟧` on the *real* objects** — the actual `lib/wasm.ml` instruction IR and the real (resolved) `lib/ast.ml` expression core. This **retires the toy `K1`/`K1Let` on real objects**. ## `RealWasm.v` (grown for R1) Adds **locals** (`LocalGet`/`LocalSet`) and the comparison ops `I32Eq`/`I32LtS`, threading a mutable locals store through `wexec`. `wexec` is refactored through a per-instruction `step1`, so the sequencing lemmas `wexec_app` / `wexec_seq` (which the preservation proof composes with) are one line each. `wexec_sound` (R0) is retained for the pure (`no_local`) fragment. ## `RealCompile.v` (new, R1) - **`rexpr`** — the resolved int/bool/`let`/binary core of `lib/ast.ml` (de Bruijn **levels**; name resolution is a separate obligation, P-7; bool ≔ 0/1, int ≔ Z, so one observable); - **`eval`** — the reference dynamic semantics (mirrors `lib/interp.ml`); - **`compile`** — lowers `let` to `LocalSet d` into a pre-sized locals array (slot = binding depth `d`; siblings reuse slots), mirroring `lib/codegen.ml`; - **`compile_correct`** — the theorem: ```coq eval env e = Some v → agree env locals → |env| = d → d + depth e ≤ |locals| → ∃ locals', wexec (compile d e) locals st = Some (locals', v :: st) ∧ |locals'| = |locals| ∧ (∀ i < d, nth_error locals' i = nth_error locals i) ``` carrying an **env↔locals agreement** invariant with **fresh-slot allocation** and **low-slot preservation** through the induction (the genuine compiler-correctness simulation); - **`compile_program_correct`** — runs a closed program from the zero-initialised locals array. Concrete end-to-end check (`let x = 2+3 in x*4 ⇒ 20`): `r1_eval_demo` (source) and `r1_exec_demo` (`compile` then `wexec` ⇒ `[20]`). ## Honest scope R1 is the int/bool/`let`/binop fragment on de Bruijn-resolved source; `LocalTee`/i64, structured control, memory, floats, calls, and effects are later rungs (`REAL-LIFT.adoc` §4). **R2** (structured control on a fuel-indexed `wexec`) is next, and is the rung that **settles #601 concretely**. ## Verification All axiom-free — every `Print Assumptions` reports *Closed under the global context*. `formal/` now **17 files, 29 closure reports** (full dep-order recompile, no `Axioms:`). Wired into `_CoqProject` / `justfile` / `.hypatia-ignore`; `README.adoc`, `REAL-LIFT.adoc` (R1 landed, R2 next), and `PROOF-NEEDS.adoc` (K-1 row, §1, §6 Wave 3) updated. Follow-on to #638 (R0 + plan, merged). `QTT-dynamic #637 ✅ → F-5 + R0 #638 ✅ → **R1 (this)**`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01KPG9mEQXFyA3k7NWAzMNMr --- _Generated by [Claude Code](https://claude.ai/code/session_01KPG9mEQXFyA3k7NWAzMNMr)_ Co-authored-by: hyperpolymath <paraordinate@yahoo.co.uk> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 41dfce9 commit 5a42e6e

8 files changed

Lines changed: 409 additions & 108 deletions

File tree

.hypatia-ignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,5 @@ cicd_rules/banned_language_file:formal/RealWasm.v
8282
# not a glob, so a new .v needs its own lines).
8383
cicd_rules/vlang_detected:formal/Rows.v
8484
cicd_rules/banned_language_file:formal/Rows.v
85+
cicd_rules/vlang_detected:formal/RealCompile.v
86+
cicd_rules/banned_language_file:formal/RealCompile.v

docs/PROOF-NEEDS.adoc

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ than the prose corpus suggests:
7070
`TypeSafety.v` is example lemmas about list length, not AffineScript. Not
7171
core-metatheory.
7272
* **`formal/`** — the directory #513 names as the mechanized-proof target now
73-
**exists** (Coq/Rocq 8.18), axiom-free throughout — **16 files, 27 `Print
73+
**exists** (Coq/Rocq 8.18), axiom-free throughout — **17 files, 29 `Print
7474
Assumptions` closure reports**, all "Closed under the global context". Codegen
7575
keystone: `K1_CodegenPreservation.v` (K-1 minimal) +
7676
`K1Let_CodegenPreservation.v` (K-1 grown with variables/`let`/environment).
@@ -83,7 +83,9 @@ than the prose corpus suggests:
8383
Affine/QTT layer: `QttSemiring.v` (`{0,1,ω}`) +
8484
`AffineUsage.v` (`λx.x` affine, `λx. x x` not) + `QttTyping.v` (quantitative
8585
typing, `usage x t = Γ x` sound) + `QttDynamic.v` (the dynamic half:
86-
β-reduction preserves the usage profile). See <<outstanding>>, <<faces>>.
86+
β-reduction preserves the usage profile). Real lift (`REAL-LIFT.adoc`):
87+
`RealWasm.v` (real `lib/wasm.ml` IR — i32 core + locals) + `RealCompile.v`
88+
(R1: the first real `⟦compile p⟧ = ⟦p⟧`). See <<outstanding>>, <<faces>>.
8789
* **Research tracks** (not core soundness): `docs/academic/tropical-session-types/`
8890
(Lean), `proposals/echo-types/EchoEncodingFaithfulness.agda`,
8991
`proposals/idaptik/migrated/**/*Boundary.agda` (echo-types loss-with-residue,
@@ -223,11 +225,14 @@ The work in flight changes which obligations are *load-bearing* and which are
223225
fragment compiled to a stack machine — `K1Let_CodegenPreservation.v` grows it
224226
with de Bruijn variables, `let`, and an environment. The **real lift** onto the
225227
real AffineScript AST + real typed-WASM semantics is scoped in
226-
`formal/REAL-LIFT.adoc` (fragment ladder R0→R-eff); its first stone is
227-
**landed** — `formal/RealWasm.v` (R0) re-targets the *actual* `lib/wasm.ml`
228-
`instr`/`value_type` with a stack-arity soundness theorem (checked code never
229-
traps). R1 (real `RealAst`/`RealAstSem`/`RealCompile` for the int/bool/let
230-
fragment) is the first real `⟦compile p⟧ = ⟦p⟧`.
228+
`formal/REAL-LIFT.adoc` (fragment ladder R0→R-eff); **R0 + R1 landed**.
229+
`formal/RealWasm.v` (R0) re-targets the *actual* `lib/wasm.ml`
230+
`instr`/`value_type` (i32 core + locals) with a stack-arity soundness theorem;
231+
`formal/RealCompile.v` (R1) gives the **first real `⟦compile p⟧ = ⟦p⟧`** —
232+
resolved int/bool/`let`/binary source, reference `eval`, `compile` to real
233+
wasm, `compile_correct` (env↔locals agreement) + `compile_program_correct`,
234+
retiring `K1`/`K1Let` on real objects. R2 (structured control, fuel-indexed) is
235+
next, and is the rung that settles #601 concretely.
231236
* **K-2 — Effect-soundness is *blocked*, not merely unproven.** P-6 cannot be
232237
honestly *stated against the current backend* because #555 drops handler arms
233238
on three of the codegen targets. The obligation must be split: (a) prove
@@ -399,11 +404,14 @@ F-1 (full transformer preservation) is non-trivial rather than a formality.
399404
the **real typed-WASM operational semantics**, then re-prove K-1/F-1/F-2 against
400405
them (this is what makes K-1/F-1 `XL`, and what actually resolves #601). Planned
401406
as a strict-superset *fragment ladder* in `formal/REAL-LIFT.adoc`
402-
(R0→R1→R2→R-mem→R-float→R-str→R-call→R-wrap→R-eff); **R0 landed**
403-
(`formal/RealWasm.v` — real `lib/wasm.ml` IR, i32 core, arity soundness). Then
404-
the still-`prose`/`absent` obligations: P-5 (inference), P-6 (effects — blocked
405-
on #555/K-2, = rung R-eff), P-7 (resolution), P-8 (parser), P-10 (coherence);
406-
faces F-6/F-7; full-language borrowing (P-3) pending #553.
407+
(R0→R1→R2→R-mem→R-float→R-str→R-call→R-wrap→R-eff); **R0 + R1 landed**
408+
(`formal/RealWasm.v` — real `lib/wasm.ml` IR, i32 core + locals;
409+
`formal/RealCompile.v` — the first real `⟦compile p⟧ = ⟦p⟧`, retiring K1/K1Let
410+
on real objects). **R2** (structured control, fuel-indexed) is next and settles
411+
#601 concretely. Then the still-`prose`/`absent` obligations: P-5 (inference),
412+
P-6 (effects — blocked on #555/K-2, = rung R-eff), P-7 (resolution), P-8
413+
(parser), P-10 (coherence); faces F-6/F-7; full-language borrowing (P-3)
414+
pending #553.
407415
| Wave 2; #555; #553
408416
|===
409417

formal/README.adoc

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,14 @@ cannot mistake it. Coq has no `v.mod` manifest, so `vmod_detected` never fires.
9696
| **mechanized**, axiom-free
9797

9898
| `RealWasm.v`
99-
| **Real lift R0** (`REAL-LIFT.adoc`) — the real `lib/wasm.ml` IR (i32 core):
100-
`wexec` + arity checker + soundness (`wexec_sound`: checked code never traps)
99+
| **Real lift R0+R1** (`REAL-LIFT.adoc`) — the real `lib/wasm.ml` IR: i32 core +
100+
locals (`LocalGet`/`LocalSet`) + comparisons; `wexec`, `wexec_sound`
101+
| **mechanized**, axiom-free
102+
103+
| `RealCompile.v`
104+
| **Real lift R1** — the first *real* `⟦compile p⟧ = ⟦p⟧`: resolved int/bool/`let`
105+
source, reference `eval`, `compile` to real wasm, `compile_correct` (retires
106+
K1/K1Let on real objects)
101107
| **mechanized**, axiom-free
102108
|===
103109

@@ -220,17 +226,28 @@ the same canonical AST emit identical code). The observational version for the
220226
trailing-statement/tail split is mechanized separately in
221227
`invariant-path/proofs/SameCube.agda` (F-2).
222228

223-
== The real lift (R0)
224-
225-
`RealWasm.v` is the first foundation stone of the *real lift* — replacing the
226-
toy stack machine with the actual `lib/wasm.ml` IR. R0 covers the pure i32
227-
numeric core (real `instr` / `value_type` names), a stack-machine `wexec`, an
228-
arity checker `wcheck`, and the soundness theorem `wexec_sound`: arity-checked
229-
code never gets stuck — the target-side invariant the real K-1 preservation
230-
proof rests on. The full plan — the fragment ladder R0 → R1 → R2 → R-mem →
231-
R-float → R-str → R-call → R-wrap → R-eff, the Coq module structure, and the
232-
strategy for each hard part (loops/termination, linear memory, floats, the
233-
elaboration nodes, effects) — is `formal/REAL-LIFT.adoc`.
229+
== The real lift (R0 → R1)
230+
231+
The *real lift* replaces the toy stack machine with the actual `lib/wasm.ml` IR
232+
and the toy source with the real (resolved) AST, climbing a strict-superset
233+
fragment ladder. Two rungs are down:
234+
235+
* **R0** (`RealWasm.v`) — the pure i32 numeric core (real `instr` / `value_type`
236+
names), a stack-machine `wexec`, an arity checker `wcheck`, and `wexec_sound`:
237+
arity-checked, local-free code never gets stuck.
238+
* **R1** (`RealWasm.v` grown with **locals** + comparisons; `RealCompile.v`) —
239+
the **first real `⟦compile p⟧ = ⟦p⟧`**. The source is the resolved
240+
(de Bruijn-level) int/bool/`let`/binary core of `lib/ast.ml`; `eval` is the
241+
reference semantics; `compile` lowers `let` to `LocalSet` into a pre-sized
242+
locals array. `compile_correct` carries an env↔locals *agreement* invariant
243+
with fresh-slot allocation and low-slot preservation through the induction;
244+
`compile_program_correct` runs a closed program from the zero-initialised
245+
array. This **retires `K1`/`K1Let` on real objects**.
246+
247+
The full plan — the rest of the ladder (R2 → R-mem → R-float → R-str → R-call →
248+
R-wrap → R-eff), the Coq module structure, and the strategy for each hard part
249+
(loops/termination via fuel, linear memory, floats, the elaboration nodes,
250+
effects) — is `formal/REAL-LIFT.adoc`.
234251

235252
== Scope (honest)
236253

formal/REAL-LIFT.adoc

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -126,16 +126,19 @@ arity-checked code never gets stuck). That is the first real foundation stone.
126126

127127
| `RealAst.v`
128128
| The real source `expr`/`stmt`/`program` inductives (faithful to `lib/ast.ml`),
129-
per-fragment.
130-
| planned (S0)
129+
per-fragment. (R1's `rexpr` core lives inline in `RealCompile.v`; split out as
130+
the fragment grows.)
131+
| R1 inline
131132

132133
| `RealAstSem.v`
133134
| The reference dynamic semantics (mirrors `lib/interp.ml`), per-fragment.
134-
| planned (S0)
135+
(R1's `eval` lives inline in `RealCompile.v`.)
136+
| R1 inline
135137

136138
| `RealCompile.v`
137-
| The real `compile` (port of `lib/codegen.ml`), per-fragment.
138-
| planned (K-1/R1)
139+
| The real `compile` (port of `lib/codegen.ml`) + (for R1) the source `rexpr`
140+
core, reference `eval`, and the preservation theorem `compile_correct`.
141+
| **R1 landed**
139142

140143
| `RealPreservation.v`
141144
| `⟦compile p⟧ = ⟦p⟧` — the real K-1, per-fragment; then re-chains
@@ -157,11 +160,14 @@ next. The rung names are stable references the `PROOF-NEEDS.adoc` rows will cite
157160
`wexec` + arity soundness. (`RealWasm.v`)
158161
| The toy stack machine's *target* — real names now.
159162

160-
| **R1**
161-
| Source side S0+S1: `ExprLit`/`ExprVar`/`ExprLet`/`ExprBinary` (int/bool) →
162-
`RealAst.v` + `RealAstSem.v`; `compile` for them (`RealCompile.v`); locals
163-
(`LocalGet/Set/Tee`) + i64 in `RealWasm.v`. First real `⟦compile p⟧=⟦p⟧`.
164-
| Subsumes **K1** and **K1Let** on real objects.
163+
| **R1** ✅
164+
| Source `ExprLit`/`ExprVar`/`ExprLet`/`ExprBinary` (int/bool), resolved to
165+
de Bruijn levels — `rexpr` + reference `eval` + `compile` + the preservation
166+
theorem `compile_correct` (`RealCompile.v`); **locals** (`LocalGet`/`LocalSet`)
167+
+ comparison ops added to `RealWasm.v`. First real `⟦compile p⟧=⟦p⟧`, run from
168+
the zero-initialised locals array (`compile_program_correct`). (`LocalTee`/i64
169+
fold in with R2/R-wrap.)
170+
| **Subsumes K1 and K1Let on real objects.**
165171

166172
| **R2**
167173
| Structured control: `Block/Loop/If/Br/BrIf/Return`; `ExprIf`/`ExprMatch`/
@@ -269,9 +275,15 @@ already has the front-end scaffolding (`F1_TransformerPreservation.v`,
269275
== 8. Status
270276

271277
* **R0 landed** — `RealWasm.v`: real `instr`/`value_type` names, `wexec`,
272-
`wcheck`, `wexec_sound` (arity-checked code never traps), axiom-free.
273-
* Next: **R1** — stand up `RealAst.v` + `RealAstSem.v` + `RealCompile.v` for the
274-
int/bool/let/binary fragment and prove the first real `⟦compile p⟧ = ⟦p⟧`,
275-
adding locals + i64 to `RealWasm.v`.
278+
`wcheck`, `wexec_sound` (arity-checked, local-free code never traps), axiom-free.
279+
* **R1 landed** — `RealWasm.v` grown with **locals** (`LocalGet`/`LocalSet`) +
280+
comparison ops; `RealCompile.v` gives the resolved int/bool/`let`/binary source
281+
`rexpr`, the reference `eval`, the `compile` to real wasm, and the **first real
282+
`⟦compile p⟧ = ⟦p⟧`** — `compile_correct` (env↔locals agreement, fresh-slot
283+
allocation, low-slot preservation) + `compile_program_correct`. Axiom-free.
284+
Retires `K1`/`K1Let` on real objects.
285+
* Next: **R2** — structured control (`Block`/`Loop`/`If`/`Br`/`Return`,
286+
`ExprIf`/`ExprMatch`/`while`) on a fuel-indexed `wexec`; this is the rung that
287+
settles #601 concretely.
276288

277289
Tracked against `docs/PROOF-NEEDS.adoc` §6 Wave 3 and #513.

formal/RealCompile.v

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
(* SPDX-License-Identifier: MPL-2.0 *)
2+
(* SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath) *)
3+
4+
(*
5+
RealCompile.v
6+
═════════════
7+
REAL-LIFT rung R1 (see formal/REAL-LIFT.adoc): the first **real**
8+
⟦compile p⟧ = ⟦p⟧, on the real RealWasm.v target IR.
9+
10+
Source: the resolved (de Bruijn LEVEL) core of lib/ast.ml — integer/bool
11+
literals (ExprLit), variables (ExprVar), let (ExprLet), and binary operators
12+
(ExprBinary: + - * & | == <). de Bruijn levels are how lib/codegen.ml sees the
13+
AST after lib/resolve.ml — name resolution itself is obligation P-7. Bool ≔
14+
0/1, Int ≔ Z, so the single observable is Z.
15+
16+
`eval` (mirrors lib/interp.ml) is the reference dynamic semantics. `compile`
17+
(mirrors lib/codegen.ml) lowers `let` to `LocalSet d` into a pre-sized locals
18+
array (slot = binding depth d; siblings reuse slots). The theorem
19+
`compile_correct`:
20+
21+
eval env e = Some v → agree env locals → d = |env| → enough slots →
22+
wexec (compile d e) locals st = Some (locals', v :: st)
23+
∧ |locals'| = |locals| ∧ (low slots < d unchanged)
24+
25+
and the closed-program corollary `compile_program_correct` runs it from the
26+
zero-initialised locals array. This RETIRES the toy K1 / K1Let on REAL objects
27+
— the target is RealWasm's actual lib/wasm.ml instruction names with mutable
28+
locals, not an ad-hoc machine. Axiom-free, no Admitted.
29+
30+
`.v` is Coq, not V-lang — see formal/README.adoc and .hypatia-ignore.
31+
*)
32+
33+
Require Import List.
34+
Require Import ZArith.
35+
Require Import PeanoNat.
36+
Require Import Lia.
37+
Require Import ASFormal.RealWasm.
38+
Import ListNotations.
39+
40+
(* ── source: the resolved R1 core of lib/ast.ml ───────────────────────────── *)
41+
Inductive lit := LInt (z : Z) | LBool (b : bool).
42+
Inductive bop := BAdd | BSub | BMul | BAnd | BOr | BEq | BLt.
43+
Inductive rexpr :=
44+
| RLit (l : lit)
45+
| RVar (i : nat) (* de Bruijn LEVEL: outermost binder = 0 *)
46+
| RLet (e1 e2 : rexpr) (* let _ = e1 in e2 *)
47+
| RBin (b : bop) (e1 e2 : rexpr).
48+
49+
Definition lit_val (l : lit) : Z :=
50+
match l with LInt z => z | LBool b => if b then 1 else 0 end.
51+
52+
Definition bop_val (b : bop) (a c : Z) : Z :=
53+
match b with
54+
| BAdd => Z.add a c | BSub => Z.sub a c | BMul => Z.mul a c
55+
| BAnd => Z.land a c | BOr => Z.lor a c
56+
| BEq => if Z.eqb a c then 1 else 0
57+
| BLt => if Z.ltb a c then 1 else 0
58+
end.
59+
60+
(* ── reference semantics (mirrors lib/interp.ml): env = level-indexed list ── *)
61+
Fixpoint eval (env : list Z) (e : rexpr) : option Z :=
62+
match e with
63+
| RLit l => Some (lit_val l)
64+
| RVar i => nth_error env i
65+
| RLet e1 e2 =>
66+
match eval env e1 with
67+
| Some v1 => eval (env ++ [v1]) e2
68+
| None => None
69+
end
70+
| RBin b e1 e2 =>
71+
match eval env e1, eval env e2 with
72+
| Some a, Some c => Some (bop_val b a c)
73+
| _, _ => None
74+
end
75+
end.
76+
77+
(* ── compiler (mirrors lib/codegen.ml): d = binding depth = next free slot ── *)
78+
Definition bop_instr (b : bop) : instr :=
79+
match b with
80+
| BAdd => I32Add | BSub => I32Sub | BMul => I32Mul
81+
| BAnd => I32And | BOr => I32Or
82+
| BEq => I32Eq | BLt => I32LtS
83+
end.
84+
85+
Fixpoint compile (d : nat) (e : rexpr) : list instr :=
86+
match e with
87+
| RLit l => [I32Const (lit_val l)]
88+
| RVar i => [LocalGet i]
89+
| RLet e1 e2 => compile d e1 ++ [LocalSet d] ++ compile (S d) e2
90+
| RBin b e1 e2 => compile d e1 ++ compile d e2 ++ [bop_instr b]
91+
end.
92+
93+
(* max extra local slots e needs above its starting depth. *)
94+
Fixpoint depth (e : rexpr) : nat :=
95+
match e with
96+
| RLit _ | RVar _ => 0
97+
| RLet e1 e2 => Nat.max (depth e1) (S (depth e2))
98+
| RBin _ e1 e2 => Nat.max (depth e1) (depth e2)
99+
end.
100+
101+
(* the binop instruction realises bop_val on the (reversed) operand stack. *)
102+
Lemma step1_bop : forall b lo v1 v2 t,
103+
step1 (bop_instr b) lo (v2 :: v1 :: t) = Some (lo, bop_val b v1 v2 :: t).
104+
Proof. destruct b; reflexivity. Qed.
105+
106+
(* ── agreement: locals' first |env| slots hold env ────────────────────────── *)
107+
Definition agree (env locals : list Z) : Prop :=
108+
forall i, i < length env -> nth_error locals i = nth_error env i.
109+
110+
(* ── the preservation theorem ─────────────────────────────────────────────── *)
111+
Lemma compile_correct : forall e env d locals st v,
112+
eval env e = Some v ->
113+
length env = d ->
114+
agree env locals ->
115+
d + depth e <= length locals ->
116+
exists locals',
117+
wexec (compile d e) locals st = Some (locals', v :: st) /\
118+
length locals' = length locals /\
119+
(forall i, i < d -> nth_error locals' i = nth_error locals i).
120+
Proof.
121+
induction e as [ l | i | e1 IH1 e2 IH2 | b e1 IH1 e2 IH2 ];
122+
intros env d locals st v Heval Hlen Hagree Hbound; cbn [depth] in Hbound.
123+
- (* RLit *)
124+
cbn in Heval. injection Heval as Hv; subst v.
125+
exists locals. cbn [compile wexec step1].
126+
split; [reflexivity | split; [reflexivity | intros; reflexivity]].
127+
- (* RVar i *)
128+
cbn in Heval.
129+
assert (Hi : i < length env) by (apply nth_error_Some; rewrite Heval; discriminate).
130+
exists locals. cbn [compile wexec step1].
131+
rewrite (Hagree i Hi), Heval. cbn.
132+
split; [reflexivity | split; [reflexivity | intros; reflexivity]].
133+
- (* RLet e1 e2 *)
134+
cbn in Heval.
135+
destruct (eval env e1) as [v1|] eqn:Hev1; cbn in Heval; [| discriminate].
136+
destruct (IH1 env d locals st v1 Hev1 Hlen Hagree ltac:(lia))
137+
as [locals1 [Hw1 [Hlen1 Hlow1]]].
138+
assert (Hd : d < length locals1) by lia.
139+
assert (Hag2 : agree (env ++ [v1]) (set_nth d v1 locals1)).
140+
{ intros j Hj. rewrite app_length in Hj; cbn in Hj.
141+
destruct (Nat.eq_dec j d) as [->|Hjd].
142+
- rewrite set_nth_eq by lia.
143+
rewrite nth_error_app2 by lia. rewrite Hlen, Nat.sub_diag. reflexivity.
144+
- rewrite set_nth_neq by lia. rewrite Hlow1 by lia.
145+
rewrite (Hagree j) by lia. rewrite nth_error_app1 by lia. reflexivity. }
146+
destruct (IH2 (env ++ [v1]) (S d) (set_nth d v1 locals1) st v Heval
147+
ltac:(rewrite app_length; cbn; lia) Hag2
148+
ltac:(rewrite set_nth_length; lia))
149+
as [locals2 [Hw2 [Hlen2 Hlow2]]].
150+
exists locals2. split; [| split].
151+
+ apply wexec_seq with (lo1:=locals1) (st1:=v1::st); [exact Hw1|].
152+
apply wexec_seq with (lo1:=set_nth d v1 locals1) (st1:=st); [| exact Hw2].
153+
cbn [wexec step1].
154+
assert (Hltb : Nat.ltb d (length locals1) = true) by (apply Nat.ltb_lt; lia).
155+
rewrite Hltb. reflexivity.
156+
+ rewrite Hlen2, set_nth_length. exact Hlen1.
157+
+ intros j Hj. rewrite Hlow2 by lia. rewrite set_nth_neq by lia. apply Hlow1; lia.
158+
- (* RBin b e1 e2 *)
159+
cbn in Heval.
160+
destruct (eval env e1) as [v1|] eqn:Hev1; cbn in Heval; [| discriminate].
161+
destruct (eval env e2) as [v2|] eqn:Hev2; cbn in Heval; [| discriminate].
162+
injection Heval as Hv; subst v.
163+
destruct (IH1 env d locals st v1 Hev1 Hlen Hagree ltac:(lia))
164+
as [locals1 [Hw1 [Hlen1 Hlow1]]].
165+
assert (Hag1 : agree env locals1).
166+
{ intros j Hj. rewrite Hlow1 by lia. apply Hagree; lia. }
167+
destruct (IH2 env d locals1 (v1 :: st) v2 Hev2 Hlen Hag1
168+
ltac:(rewrite Hlen1; lia))
169+
as [locals2 [Hw2 [Hlen2 Hlow2]]].
170+
exists locals2. split; [| split].
171+
+ apply wexec_seq with (lo1:=locals1) (st1:=v1::st); [exact Hw1|].
172+
apply wexec_seq with (lo1:=locals2) (st1:=v2::v1::st); [exact Hw2|].
173+
cbn [wexec]. rewrite step1_bop. reflexivity.
174+
+ rewrite Hlen2. exact Hlen1.
175+
+ intros j Hj. rewrite Hlow2 by lia. apply Hlow1; lia.
176+
Qed.
177+
178+
(* ── closed-program corollary ─────────────────────────────────────────────── *)
179+
Corollary compile_program_correct : forall e v,
180+
eval [] e = Some v ->
181+
exists locals',
182+
wexec (compile 0 e) (repeat 0%Z (depth e)) [] = Some (locals', [v]).
183+
Proof.
184+
intros e v Heval.
185+
destruct (compile_correct e [] 0 (repeat 0%Z (depth e)) [] v Heval)
186+
as [locals' [Hw _]].
187+
- reflexivity.
188+
- intros i Hi; cbn in Hi; lia.
189+
- rewrite repeat_length; lia.
190+
- exists locals'; exact Hw.
191+
Qed.
192+
193+
(* concrete: let x = 2+3 in x*4 ⇒ 20, end-to-end. *)
194+
Example r1_eval_demo :
195+
eval [] (RLet (RBin BAdd (RLit (LInt 2)) (RLit (LInt 3)))
196+
(RBin BMul (RVar 0) (RLit (LInt 4)))) = Some 20%Z.
197+
Proof. reflexivity. Qed.
198+
199+
Example r1_exec_demo :
200+
wexec (compile 0 (RLet (RBin BAdd (RLit (LInt 2)) (RLit (LInt 3)))
201+
(RBin BMul (RVar 0) (RLit (LInt 4))))) [0%Z] []
202+
= Some ([5%Z], [20%Z]).
203+
Proof. reflexivity. Qed.
204+
205+
Print Assumptions compile_correct.
206+
Print Assumptions compile_program_correct.

0 commit comments

Comments
 (0)