Add proof of reaching definition analysis

This requires a few pieces:

* Make node tags use `Fin n` intead of natural numbers. This makes
  it possible to build a finite lattice over AST nodes, and also
  ensure automatic, total indexing from CFG nodes into the AST that
  created them. For this, use the elaborator to derive the ordering
  statements etc. where possible.
* Adjust the forward framework to enable proofs that don't just state
  correctness on the environment, but also on an arbitrary additional
  state accumulated from traversing the trace.
* State the reaching definition analysis's correctness in terms
  of this new framework.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-27 16:29:16 -05:00
parent 5737805125
commit b6b30958aa
20 changed files with 678 additions and 197 deletions

View File

@@ -14,14 +14,14 @@ lemma updateVariablesFromExpression_mono (k : String) (e : Expr) :
Monotone (updateVariablesFromExpression (L := L) (prog := prog) k e) :=
FiniteMap.generalizedUpdate_monotone monotone_id (fun _ => E.eval_mono e)
def evalBasicStmt (_ : prog.State) (bs : BasicStmt)
def evalBasicStmt (s : prog.State) (bs : BasicStmt) (_h : prog.code s = some bs)
(vs : VariableValues L prog) : VariableValues L prog :=
match bs with
| .assign k e => updateVariablesFromExpression k e vs
| .noop => vs
lemma evalBasicStmt_mono (s : prog.State) (bs : BasicStmt) :
Monotone (evalBasicStmt (L := L) (prog := prog) s bs) := by
lemma evalBasicStmt_mono (s : prog.State) (bs : BasicStmt) (h : prog.code s = some bs) :
Monotone (evalBasicStmt (L := L) (prog := prog) s bs h) := by
cases bs with
| assign k e => exact updateVariablesFromExpression_mono k e
| noop => exact monotone_id
@@ -32,7 +32,7 @@ instance ExprEvaluator.toStmtEvaluator : StmtEvaluator L prog :=
instance ExprEvaluator.toStmtEvaluator_valid [LatticeInterpretation L]
[ValidExprEvaluator L prog] : ValidStmtEvaluator L prog := by
constructor
intro s vs ρ₁ ρ₂ bs hbs hvs
intro s vs ρ₁ ρ₂ bs hcode hbs hvs
cases hbs with
| noop => exact hvs
| assign k e v hev =>

View File

@@ -7,8 +7,9 @@ namespace Forward
variable (L : Type) [Lattice L] (prog : Program)
class StmtEvaluator where
eval : prog.State BasicStmt VariableValues L prog VariableValues L prog
eval_mono : s bs, Monotone (eval s bs)
eval : (s : prog.State) (bs : BasicStmt) prog.code s = some bs
VariableValues L prog VariableValues L prog
eval_mono : s bs h, Monotone (eval s bs h)
class ExprEvaluator where
eval : Expr VariableValues L prog L
@@ -17,13 +18,13 @@ class ExprEvaluator where
class ValidExprEvaluator [ExprEvaluator L prog] [I : LatticeInterpretation L] :
Prop where
valid : {vs : VariableValues L prog} {ρ : Env} {e : Expr} {v : Value},
EvalExpr ρ e v vs ρ I.interp (ExprEvaluator.eval e vs) v
EvalExpr ρ e v vs ρ () I.interp (ExprEvaluator.eval e vs) v
class ValidStmtEvaluator [E : StmtEvaluator L prog] [LatticeInterpretation L] :
Prop where
valid : {s : prog.State} {vs : VariableValues L prog} {ρ₁ ρ₂ : Env}
{bs : BasicStmt},
EvalBasicStmt ρ₁ bs ρ₂ vs ρ₁ E.eval s bs vs ρ₂
{bs : BasicStmt} (hcode : prog.code s = some bs),
EvalBasicStmt ρ₁ bs ρ₂ vs ρ₁ () E.eval s bs hcode vs ρ₂ ()
end Forward

View File

@@ -64,39 +64,47 @@ lemma variablesAt_joinAll (s : prog.State) (sv : StateVariables L prog) :
variablesAt s (joinAll sv) = joinForKey s sv :=
joinAll_mem_eq (variablesAt_mem s (joinAll sv))
/-! ### Lifting an interpretation to variable maps -/
class StateInterp (L : Type) [Lattice L] (prog : Program) where
St : Env Type
init : St []
interp : VariableValues L prog (ρ : Env) St ρ Prop
interp_sup : {vs₁ vs₂ : VariableValues L prog} {ρ : Env} {st : St ρ},
interp vs₁ ρ st interp vs₂ ρ st interp (vs₁ vs₂) ρ st
interp_inf : {vs₁ vs₂ : VariableValues L prog} {ρ : Env} {st : St ρ},
interp vs₁ ρ st interp vs₂ ρ st interp (vs₁ vs₂) ρ st
variable [I : LatticeInterpretation L]
instance [S : StateInterp L prog] :
Interp (VariableValues L prog) ((ρ : Env) S.St ρ Prop) :=
S.interp
omit [FiniteHeightLattice L] in
instance : Interp (VariableValues L prog) (Env Prop) where
interp (vs : VariableValues L prog) (ρ : Env) : Prop :=
(k : String) (l : L), (k, l) vs
(v : Value), Env.Mem (k, v) ρ I.interp l v
lemma interp_botV_nil : botV L prog [] := by
intro k l _ v hmem
cases hmem
omit [FiniteHeightLattice L] in
lemma interp_sup {vs₁ vs₂ : VariableValues L prog} {ρ : Env}
(h : vs₁ ρ vs₂ ρ) : vs₁ vs₂ ρ := by
intro k l hmem v hv
obtain l₁, l₂, rfl, h₁, h₂ := FiniteMap.mem_sup hmem
rcases h with h | h
· exact I.interp_sup v (Or.inl (h _ _ h₁ _ hv))
· exact I.interp_sup v (Or.inr (h _ _ h₂ _ hv))
lemma interp_foldr {vs : VariableValues L prog}
{vss : List (VariableValues L prog)} {ρ : Env}
(hvs : vs ρ) (hmem : vs vss) :
vss.foldr (· ·) (botV L prog) ρ := by
lemma interp_foldr [S : StateInterp L prog]
{vs : VariableValues L prog} {vss : List (VariableValues L prog)}
{ρ : Env} {st : S.St ρ} (hvs : vs ρ st) (hmem : vs vss) :
vss.foldr (· ·) (botV L prog) ρ st := by
induction vss with
| nil => cases hmem
| cons vs' vss' ih =>
rcases List.mem_cons.mp hmem with rfl | hmem'
· exact interp_sup (Or.inl hvs)
· exact interp_sup (Or.inr (ih hmem'))
· exact S.interp_sup (Or.inl hvs)
· exact S.interp_sup (Or.inr (ih hmem'))
variable [I : LatticeInterpretation L]
instance : StateInterp L prog where
St := fun _ => PUnit
init := PUnit.unit
interp vs ρ _ := (k : String) (l : L), (k, l) vs
(v : Value), Env.Mem (k, v) ρ I.interp l v
interp_sup := by
intro vs₁ vs₂ ρ st h k l hmem v hv
obtain l₁, l₂, rfl, h₁, h₂ := FiniteMap.mem_sup hmem
rcases h with h | h
· exact I.interp_sup v (Or.inl (h _ _ h₁ _ hv))
· exact I.interp_sup v (Or.inr (h _ _ h₂ _ hv))
interp_inf := by
intro vs₁ vs₂ ρ st h k l hmem v hv
obtain l₁, l₂, rfl, h₁, h₂ := FiniteMap.mem_inf hmem
exact I.interp_inf v h.1 _ _ h₁ _ hv, h.2 _ _ h₂ _ hv
end Forward