@@ -90,75 +90,54 @@ lemma stepTrace {s₁ s₂ : prog.State} {ρ₁ ρ₂ : Env}
rw [ variablesAt_joinAll ]
exact hjoin
/-- Soundness at *every* visited node: if the analysis result over - approximates the
incoming environment at the start of the trace , then a t each node reached along the
way it over - approximates both the environment entering that node (via `joinForKey`)
and the environment leaving it (via `variablesAt`). The intermediate `variablesAt`
evidence used to be computed and discarded inside `walkTrace`; here it is returned. -/
lemma walkTrace_reaches { s₁ s₂ s₃ : prog . State } { ρ ₁ ρ ₂ ρ ₃ : Env }
{ s : prog . State } { ρ in ρ out : Env }
{ tr : Trace prog . cfg s₂ s₃ ρ₂ ρ₃ }
( hr : Reaches tr s ρ in ρ out)
( trₗ : Traceₗ prog . cfg s₁ s₂ ρ₁ ρ₂ )
( hjoin : ⟦ joinForKey s₂ ( result L prog ) ⟧ ( S . Pre trₗ ) ) :
⟦ joinForKey s ( result L prog ) ⟧ ( S . Pre ( trₗ + + hr . pre ) )
∧ ⟦ variablesAt s ( result L prog ) ⟧ ( S . Post ( trₗ + + hr . post ) ) : = by
induction hr with
| s ingle_here hnode = >
simp [ Reaches . p re, Reaches . post ]
refine ⟨ ? _ , ? _ ⟩ < ; > try simpa [ HAppend . hAppend ]
exact stepTrace trₗ hjoin hnode
| edge_here hnode hedge rest = >
simp [ Reaches . pre , Reaches . post ]
refine ⟨ ? _ , ? _ ⟩ < ; > try simpa [ HAppend . hAppend ]
exact stepTrace trₗ hjoin hnode
| edge_there hnode hedge rest hr' ih = >
have hstep : = stepTrace trₗ hjoin hnode
have hmem : = FiniteMap . mem_valuesAt prog . states_nodup
( prog . mem_incoming_of_edge hedge ) ( variablesAt_mem _ ( result L prog ) )
simpa [ Reaches . pre , Reaches . post , HAppend . hAppend ] using
ih ( ( trₗ + + hnode ) . addEdge hedge )
( interp_foldr ( S . post_pre ( trₗ + + hnode ) hedge hstep ) hmem )
/-- Soundness propagates along an execution prefix: if the analysis is sound at
`s₂` for the run so far (`trₗ`) , then i t is sound wherever the further prefix
`mid` ends up. -/
lemma walkPrefix : ∀ { s₂ s : prog . State } { ρ₂ ρ in : Env }
( mid : Traceₗ prog . cfg s₂ s ρ₂ ρ in) { s₁ : prog . State } { ρ₁ : Env }
( trₗ : Traceₗ prog . cfg s ₁ s ₂ ρ ₁ ρ₂ ) ,
⟦ joinForKey s₂ ( result L prog ) ⟧ ( S . Pre trₗ ) →
⟦ joinForKey s ( result L prog ) ⟧ ( S . Pre ( trₗ + + mid ) ) : = by
intro s₂ s ρ₂ ρ in mid
induction mid with
| nil = > intro s₁ ρ₁ trₗ hjoin ; simpa [ HAppend . hAppend , Traceₗ . append ] using hjoin
| cons hnode hedge rest ih = >
intro s₁ ρ₁ trₗ hjoin
have hstep : = stepTrace trₗ hjoin hnode
have hmem : = F initeMap . mem_valuesAt prog . states_nodup
( prog . mem_incoming_of_edge hedge ) ( variablesAt_mem _ ( result L prog ) )
simpa [ HAppend . hAppend , Traceₗ . append ] using
ih ( ( trₗ + + hnode ) . addEdge hedge )
( interp_foldr ( S . post_pre ( trₗ + + hnode ) hedge hstep ) hmem )
omit [ DecidableEq L ] in
/-- The final node of a trace is always reached, with the environment / state the trace
ends in. Used to recover the final - state soundness theorem from `walkTrace_reaches `. -/
def reaches_final { s₁ s₂ : prog . State } { ρ ₁ ρ₂ : Env }
( tr : Trace prog . cfg s₁ s₂ ρ₁ ρ₂ ) :
Σ ρ in, Reaches tr s₂ ρ in ρ₂ : =
match tr with
| . single hnode = > ⟨ _ , . single_here hnode ⟩
| . edge hnode hedge rest = >
let ⟨ ρ in, r' ⟩ : = reaches_final rest ; ⟨ ρ in, . edge_there hnode hedge _ r' ⟩
ends in. Used to recover the final - state soundness theorem from `walkPrefix `. -/
def reaches_final { s : prog . State } { ρ : Env }
( tr : Trace prog . cfg prog . initialState s [ ] ρ ) : Σ ρ in, Reaches s ρ in ρ : =
⟨ _ , ⟨ tr . split . 2 . 1 , tr . split . 2 . 2 ⟩ ⟩
omit [ DecidableEq L ] in
/-- Reaching the final node covers the whole trace. -/
@[ simp ] lemma reaches_final_post { s₁ s₂ : prog . State } { ρ₁ ρ ₂ : Env }
( tr : Trace prog . cfg s₁ s₂ ρ₁ ρ₂ ) :
( reaches_final tr ) . 2 . post = tr : = by
induction tr with
| single hnode = > rfl
| edge hnode hedge rest ih = > simp [ reaches_final , Reaches . post , ih ]
@[ simp ] lemma reaches_final_post { s : prog . State } { ρ : Env }
( tr : Trace prog . cfg prog . initialState s [ ] ρ ) :
( reaches_final tr ) . 2 . post = tr : = Trace . split_append tr
variable ( L prog ) in
/-- Soundness at every program point reached during execution: for any node `s` visited
by the run `hrun` (witnessed by `hr`), the analysis result over - approximates both the
environment entering `s` and the one leaving it. The final - state theorem
` analyze_correct_state` is the special case where `s` is `prog.finalState`. -/
theorem analyze_correct_at { ρ f : Env } ( hrun : EvalStmt [ ] prog . rootStmt ρ f)
{ s : prog . State } { ρ in ρ out : Env }
( hr : Reaches ( prog . trace hrun ) s ρ in ρ out) :
/-- Soundness at every program point an execution actually visits: the analysis
over - approximates both the environment entering that point and the one leaving
it. -/
theorem analyze_correct_at { s : prog . State } { ρ in ρ out : Env } ( hr : Reaches s ρ in ρ out) :
⟦ joinForKey s ( result L prog ) ⟧ ( S . Pre hr . pre )
∧ ⟦ variablesAt s ( result L prog ) ⟧ ( S . Post hr . post ) : = by
refine walkTrace_reaches hr ( Traceₗ . single _ _ [ ] ) ? _
rw [ joinForKey_initialState ]
exact ValidStateEvaluator . botV_init
∧ ⟦ variablesAt s ( result L prog ) ⟧ ( S . Post hr . post ) : =
have hpre : = walkPrefix hr . pre Traceₗ . nil
( by rw [ joinForKey_initialState ] ; exact ValidStateEvaluator . botV_init )
⟨ hpre , stepTrace hr . pre hpre hr . step ⟩
variable ( L prog ) in
theorem analyze_correct'
{ ρ : Env } ( hrun : EvalStmt [ ] prog . rootStmt ρ ) :
⟦ variablesAt prog . finalState ( result L prog ) ⟧ ( S . Post ( prog . trace hrun ) ) : = by
have h : = ( analyze_correct_at L prog hrun ( reaches_final ( prog . trace hrun ) ) . 2 ) . 2
have h : = ( analyze_correct_at L prog ( reaches_final ( prog . trace hrun ) ) . 2 ) . 2
rwa [ reaches_final_post ] at h
end