ai-free / bcpl /BCPL /Correct.lean
SNAPKITTYWEST's picture
October 2026 main drop: mirror from GitHub
f5deffb verified
Raw History Blame Contribute Delete
14.6 kB
import BCPL.ExprCorrect
/-!
# BCPL.Correct
Translation correctness of the BCPL lowering.
`compile_correct`: whenever the reference semantics derives result `r` for statement `s`
from memory `m`, the machine, started at the statement's address with memory `m`, either
* reaches (via `Steps`) the end of the statement's code with the data stack unchanged and
memory equal to the BCPL result, or
* reaches a state whose next step traps with exactly the trap BCPL reports.
`compileProgram_correct` lifts this to a whole program run from the initial machine state.
-/
namespace WordDialect
namespace BCPL
open IR
theorem assignCode_ok {n : Nat} (addr : Var → Word n) (l : LVal) (e : Expr) (s : State n)
(m' : Memory n) (h : assignSem addr l e s.mem = .ok m') :
execSeq (assignCode addr l e) s =
.next { s with pc := s.pc + (assignCode addr l e).length, mem := m' } := by
cases l with
| var x =>
simp only [assignSem] at h
split at h
· cases h
· rename_i v hv
simp only [writeW] at h
split at h
· rename_i hw; cases h
rw [assignCode, execSeq_append, compileE_ok addr e s v hv]
simp [Outcome.andThen, execSeq, exec, State.fall, hw, Nat.add_assoc]
· cases h
| rv a =>
simp only [assignSem] at h
split at h
· cases h
· rename_i v hv
split at h
· cases h
· rename_i a' ha
simp only [writeW] at h
split at h
· rename_i hw; cases h
rw [assignCode, execSeq_append, execSeq_append, compileE_ok addr e s v hv]
simp only [Outcome.andThen]
rw [compileE_ok addr a { s with pc := s.pc + (compileE addr e).length, dstack := v :: s.dstack } a' ha]
simp [Outcome.andThen, execSeq, exec, State.fall, hw, Nat.add_assoc]
· cases h
theorem assignCode_err {n : Nat} (addr : Var → Word n) (l : LVal) (e : Expr) (s : State n)
(t : Trap) (h : assignSem addr l e s.mem = .error t) :
execSeq (assignCode addr l e) s = .trapped t := by
cases l with
| var x =>
simp only [assignSem] at h
split at h
· rename_i _ ht; cases h
rw [assignCode, execSeq_append, compileE_err addr e s _ ht] <;> rfl
· rename_i v hv
simp only [writeW] at h
split at h
· cases h
· rename_i hw; cases h
rw [assignCode, execSeq_append, compileE_ok addr e s v hv]
simp [Outcome.andThen, execSeq, exec, State.fall, hw]
| rv a =>
simp only [assignSem] at h
split at h
· rename_i _ ht; cases h
rw [assignCode, execSeq_append, execSeq_append, compileE_err addr e s _ ht] <;> rfl
· rename_i v hv
split at h
· rename_i _ ht; cases h
rw [assignCode, execSeq_append, execSeq_append, compileE_ok addr e s v hv]
simp only [Outcome.andThen]
rw [compileE_err addr a { s with pc := s.pc + (compileE addr e).length, dstack := v :: s.dstack } _ ht] <;> rfl
· rename_i a' ha
simp only [writeW] at h
split at h
· cases h
· rename_i hw; cases h
rw [assignCode, execSeq_append, execSeq_append, compileE_ok addr e s v hv]
simp only [Outcome.andThen]
rw [compileE_ok addr a { s with pc := s.pc + (compileE addr e).length, dstack := v :: s.dstack } a' ha]
simp [Outcome.andThen, execSeq, exec, State.fall, hw]
/-- The machine realizes result `r`, ending at address `e` with data stack `d` on success. -/
def Reaches {n : Nat} (p : Prog n) (s : State n) (d : List (Word n)) (e : Nat) :
Except Trap (Memory n) → Prop
| .ok m' => ∃ s', Steps p s s' ∧ s'.pc = e ∧ s'.dstack = d ∧ s'.mem = m'
| .error t => ∃ s1, Steps p s s1 ∧ step p s1 = .trapped t
theorem reaches_trans {n : Nat} {p : Prog n} {s s1 : State n} {d : List (Word n)} {e : Nat}
{r : Except Trap (Memory n)} (h1 : Steps p s s1) (h2 : Reaches p s1 d e r) :
Reaches p s d e r := by
cases r with
| ok m' =>
obtain ⟨s', hs, hpc, hd, hm⟩ := h2
exact ⟨s', h1.trans hs, hpc, hd, hm⟩
| error t =>
obtain ⟨s2, hs, ht⟩ := h2
exact ⟨s2, h1.trans hs, ht⟩
theorem reaches_cast {n : Nat} {p : Prog n} {s : State n} {d : List (Word n)} {e e' : Nat}
{r : Except Trap (Memory n)} (h : e = e') (hr : Reaches p s d e r) : Reaches p s d e' r := by
subst h; exact hr
theorem reaches_dstack {n : Nat} {p : Prog n} {s : State n} {d d' : List (Word n)} {e : Nat}
{r : Except Trap (Memory n)} (h : d = d') (hr : Reaches p s d e r) : Reaches p s d' e r := by
subst h; exact hr
theorem size_assign {n : Nat} (addr : Var → Word n) (l : LVal) (e : Expr) :
(compile addr (.assign l e)).size = (assignCode addr l e).length := rfl
theorem size_seq {n : Nat} (addr : Var → Word n) (a b : Stmt) :
(compile addr (.seq a b)).size = (compile addr a).size + (compile addr b).size := rfl
theorem size_test {n : Nat} (addr : Var → Word n) (c : Expr) (t e : Stmt) :
(compile addr (.test c t e)).size =
(compileE addr c).length + ((compile addr e).size + (compile addr t).size + 2) := rfl
theorem size_while {n : Nat} (addr : Var → Word n) (c : Expr) (body : Stmt) :
(compile addr (.while c body)).size =
(compileE addr c).length + (compile addr body).size + 3 := rfl
theorem compile_correct {n : Nat} {addr : Var → Word n} {s : Stmt} {m : Memory n}
{r : Except Trap (Memory n)} (h : Exec addr s m r) :
∀ (p : Prog n) (base : Nat) (st : State n),
At p base ((compile addr s).emit base) → st.pc = base → st.mem = m →
Reaches p st st.dstack (base + (compile addr s).size) r := by
induction h with
| skip =>
intro p base st _ hpc hm
exact ⟨st, .refl, by simp [compile, Frag.empty, Frag.ofCode, hpc], rfl, hm⟩
| @assign l e m r hr =>
intro p base st hat hpc hm
subst hr
have hcode : At p base (assignCode addr l e) := by simpa [compile, Frag.ofCode] using hat
cases hres : assignSem addr l e m with
| ok m' =>
have hx := assignCode_ok addr l e st m' (by rw [hm]; exact hres)
obtain ⟨hst, hpc'⟩ :=
execSeq_steps (assignCode_straight addr l e) (by rw [hpc]; exact hcode) hx
exact ⟨_, hst, by rw [size_assign]; simp [hpc], rfl, rfl⟩
| error t =>
have hx := assignCode_err addr l e st t (by rw [hm]; exact hres)
exact execSeq_trap (assignCode_straight addr l e) (by rw [hpc]; exact hcode) hx
| @seqOk a b m m1 r _ _ iha ihb =>
intro p base st hat hpc hm
have hat' := Frag.seq_at (by simpa [compile] using hat)
obtain ⟨s1, hs1, hpc1, hd1, hm1⟩ := iha p base st hat'.1 hpc hm
have ih2 := ihb p (base + (compile addr a).size) s1 hat'.2 hpc1 hm1
rw [hd1] at ih2
refine reaches_cast ?_ (reaches_trans hs1 ih2)
rw [size_seq]; omega
| @seqErr a b m t _ iha =>
intro p base st hat hpc hm
have hat' := Frag.seq_at (by simpa [compile] using hat)
exact iha p base st hat'.1 hpc hm
| @testErr c t e m x hc =>
intro p base st hat hpc hm
have hat' := Frag.seq_at (by simpa [compile] using hat)
have hcode : At p base (compileE addr c) := by simpa [Frag.ofCode] using hat'.1
exact execSeq_trap (compileE_straight addr c) (by rw [hpc]; exact hcode)
(compileE_err addr c st x (by rw [hm]; exact hc))
| @testTrue c t e m v r hc ht _ iht =>
intro p base st hat hpc hm
have hat' := Frag.seq_at (by simpa [compile] using hat)
have hcode : At p base (compileE addr c) := by simpa [Frag.ofCode] using hat'.1
have hx := compileE_ok addr c st v (by rw [hm]; exact hc)
obtain ⟨hst, hpc1⟩ := execSeq_steps (compileE_straight addr c) (by rw [hpc]; exact hcode) hx
have hite : At p (base + (compileE addr c).length)
((Frag.ite (compile addr t) (compile addr e)).emit (base + (compileE addr c).length)) :=
hat'.2
obtain ⟨_, _, _, ht'⟩ := ite_decode hite
have h1 := ite_true_rule hite
(s := { st with pc := st.pc + (compileE addr c).length, dstack := v :: st.dstack })
(by simp [hpc]) rfl ht
have ih2 := iht p (base + (compileE addr c).length + 2 + (compile addr e).size)
{ st with pc := base + (compileE addr c).length + 2 + (compile addr e).size, dstack := st.dstack }
ht' (by simp [hpc]) hm
refine reaches_cast ?_ (reaches_trans (hst.trans (by simpa [hpc] using h1)) ih2)
rw [size_test]; omega
| @testFalse c t e m v r hc hf _ ihe =>
intro p base st hat hpc hm
have hat' := Frag.seq_at (by simpa [compile] using hat)
have hcode : At p base (compileE addr c) := by simpa [Frag.ofCode] using hat'.1
have hx := compileE_ok addr c st v (by rw [hm]; exact hc)
obtain ⟨hst, hpc1⟩ := execSeq_steps (compileE_straight addr c) (by rw [hpc]; exact hcode) hx
have hite : At p (base + (compileE addr c).length)
((Frag.ite (compile addr t) (compile addr e)).emit (base + (compileE addr c).length)) :=
hat'.2
obtain ⟨h0, he, _, _⟩ := ite_decode hite
cases r with
| ok m1 =>
obtain ⟨s2, hs2, hpc2, hd2, hm2⟩ := ihe p (base + (compileE addr c).length + 1)
{ st with pc := base + (compileE addr c).length + 1, dstack := st.dstack }
he (by simp [hpc]) hm
have h1 := ite_false_rule hite
(s := { st with pc := st.pc + (compileE addr c).length, dstack := v :: st.dstack })
(by simp [hpc]) rfl hf
(s1 := s2) (by simpa [hpc] using hs2) (by simpa using hpc2)
refine ⟨_, hst.trans h1, ?_, hd2, hm2⟩
show base + (compileE addr c).length + 2 + (compile addr e).size + (compile addr t).size = _
rw [size_test]; omega
| error t' =>
obtain ⟨s2, hs2, ht2⟩ := ihe p (base + (compileE addr c).length + 1)
{ st with pc := base + (compileE addr c).length + 1, dstack := st.dstack }
he (by simp [hpc]) hm
have hfall := step_branch_fall
(s := { st with pc := st.pc + (compileE addr c).length, dstack := v :: st.dstack })
(by simp [hpc]; exact h0) rfl hf
exact ⟨s2, hst.trans (.cons (by simpa [hpc] using hfall) hs2), ht2⟩
| @whileErr c body m x hc =>
intro p base st hat hpc hm
obtain ⟨hc_at, _, _, _, _⟩ := while_decode (by simpa [compile] using hat)
have hcode : At p base (compileE addr c) := by simpa [Frag.ofCode] using hc_at
exact execSeq_trap (compileE_straight addr c) (by rw [hpc]; exact hcode)
(compileE_err addr c st x (by rw [hm]; exact hc))
| @whileExit c body m v hc hf =>
intro p base st hat hpc hm
have hat' : At p base ((Frag.whileLoop (Frag.ofCode (compileE addr c)) (compile addr body)).emit base) := by
simpa [compile] using hat
obtain ⟨hc_at, _, _, _, _⟩ := while_decode hat'
have hcode : At p base (compileE addr c) := by simpa [Frag.ofCode] using hc_at
have hx := compileE_ok addr c st v (by rw [hm]; exact hc)
obtain ⟨hst, hpc1⟩ := execSeq_steps (compileE_straight addr c) (by rw [hpc]; exact hcode) hx
have h1 := while_exit_rule hat'
(s1 := { st with pc := st.pc + (compileE addr c).length, dstack := v :: st.dstack })
(by simp [hpc, Frag.ofCode]) rfl hf
refine ⟨_, hst.trans h1, ?_, rfl, hm⟩
rw [size_while]; simp [Frag.ofCode]; omega
| @whileBodyErr c body m v x hc ht _ ihb =>
intro p base st hat hpc hm
have hat' : At p base ((Frag.whileLoop (Frag.ofCode (compileE addr c)) (compile addr body)).emit base) := by
simpa [compile] using hat
obtain ⟨hc_at, _, _, hb_at, _⟩ := while_decode hat'
have hcode : At p base (compileE addr c) := by simpa [Frag.ofCode] using hc_at
have hx := compileE_ok addr c st v (by rw [hm]; exact hc)
obtain ⟨hst, hpc1⟩ := execSeq_steps (compileE_straight addr c) (by rw [hpc]; exact hcode) hx
have h1 := while_enter_rule hat'
(s1 := { st with pc := st.pc + (compileE addr c).length, dstack := v :: st.dstack })
(by simp [hpc, Frag.ofCode]) rfl ht
have ih2 := ihb p (base + (Frag.ofCode (compileE addr c)).size + 2)
{ st with pc := base + (Frag.ofCode (compileE addr c)).size + 2, dstack := st.dstack }
hb_at (by simp [hpc]) hm
exact reaches_trans (hst.trans h1) ih2
| @whileAgain c body m v m1 r hc ht _ _ ihb ihl =>
intro p base st hat hpc hm
have hat' : At p base ((Frag.whileLoop (Frag.ofCode (compileE addr c)) (compile addr body)).emit base) := by
simpa [compile] using hat
obtain ⟨hc_at, _, _, hb_at, _⟩ := while_decode hat'
have hcode : At p base (compileE addr c) := by simpa [Frag.ofCode] using hc_at
have hx := compileE_ok addr c st v (by rw [hm]; exact hc)
obtain ⟨hst, hpc1⟩ := execSeq_steps (compileE_straight addr c) (by rw [hpc]; exact hcode) hx
have h1 := while_enter_rule hat'
(s1 := { st with pc := st.pc + (compileE addr c).length, dstack := v :: st.dstack })
(by simp [hpc, Frag.ofCode]) rfl ht
obtain ⟨s3, hs3, hpc3, hd3, hm3⟩ := ihb p (base + (Frag.ofCode (compileE addr c)).size + 2)
{ st with pc := base + (Frag.ofCode (compileE addr c)).size + 2, dstack := st.dstack }
hb_at (by simp [hpc]) hm
have h2 := while_back_rule hat' (s1 := s3) hpc3
have hd3' : s3.dstack = st.dstack := hd3
have ih4 : Reaches p { s3 with pc := base } st.dstack
(base + (compile addr (Stmt.while c body)).size) r :=
reaches_dstack hd3' (ihl p base { s3 with pc := base } hat rfl hm3)
exact reaches_trans (((hst.trans h1).trans hs3).trans h2) ih4
/-- Whole-program correctness from the initial machine state (empty stacks, given memory). -/
theorem compileProgram_correct {n : Nat} {addr : Var → Word n} {s : Stmt} {mem : Memory n}
{r : Except Trap (Memory n)} (h : Exec addr s mem r) :
match r with
| .ok m' =>
∃ s', WordDialect.Exec (compileProgram addr s) (State.init mem) (.halted s') ∧
s'.dstack = [] ∧ s'.mem = m'
| .error t => WordDialect.Exec (compileProgram addr s) (State.init mem) (.trapped t) := by
have hat : At (compileProgram addr s) 0 ((compile addr s).emit 0) := by
have := at_embed ([] : Prog n) ((compile addr s).emit 0) [.halt]
simpa [compileProgram] using this
have key := compile_correct h (compileProgram addr s) 0 (State.init mem) hat rfl rfl
cases r with
| ok m' =>
obtain ⟨s', hs, hpc, hd, hm⟩ := key
have hfetch : (compileProgram addr s)[s'.pc]? = some .halt := by
rw [hpc]
simp [compileProgram, (compile addr s).len]
refine ⟨s', WordDialect.Exec.of_steps hs (.halt ?_), hd, hm⟩
rw [step_of_fetch hfetch]; rfl
| error t =>
obtain ⟨s1, hs, ht⟩ := key
exact WordDialect.Exec.of_steps hs (.trap ht)
end BCPL
end WordDialect