Add more documentation
This commit is contained in:
@@ -1,5 +1,24 @@
|
|||||||
import Spa.Language.Traces
|
import Spa.Language.Traces
|
||||||
|
|
||||||
|
/-!
|
||||||
|
|
||||||
|
# Properties of the Object Language, CFGs, and Traces
|
||||||
|
|
||||||
|
This module encodes some properties of the language, mostly those having to do
|
||||||
|
with connecting the computational view (the `Spa.Graph`s, on which static
|
||||||
|
analyses are executed) to the semantic view (such as `EvalStmt`, which
|
||||||
|
encodes the expected formal behavior of the language). In particular,
|
||||||
|
to prove that our computationally-implemented static analyses are correct,
|
||||||
|
we need to show that our computational model of their execution (the CFG)
|
||||||
|
matches the formal description. Thus, the key result `cfg_sufficient`.
|
||||||
|
|
||||||
|
Many lemmas and definitions here aim are used to prove that result,
|
||||||
|
by allowing inductive proofs on the construction of the CFG:
|
||||||
|
the bits where we _build up_ the trace corresponding to each
|
||||||
|
proof tree are exactly those when we have two graphs (through
|
||||||
|
which traces exist) and we want to combine these graphs, while
|
||||||
|
showing also that a combined trace exists as well. -/
|
||||||
|
|
||||||
namespace Spa
|
namespace Spa
|
||||||
|
|
||||||
open Graph
|
open Graph
|
||||||
@@ -11,12 +30,12 @@ lemma Fin.castAdd_ne_natAdd {n m : ℕ} (i : Fin n) (j : Fin m) :
|
|||||||
simp only [Fin.coe_castAdd, Fin.coe_natAdd] at this
|
simp only [Fin.coe_castAdd, Fin.coe_natAdd] at this
|
||||||
omega
|
omega
|
||||||
|
|
||||||
/-! ### Trace embeddings -/
|
|
||||||
|
|
||||||
section Embeddings
|
section Embeddings
|
||||||
|
|
||||||
variable {g₁ g₂ : Graph} {ρ₁ ρ₂ : Env}
|
variable {g₁ g₂ : Graph} {ρ₁ ρ₂ : Env}
|
||||||
|
|
||||||
|
/-- When two graphs are overlaid, for each trace in the left graph,
|
||||||
|
a corresponding trace exists in the combined graph. -/
|
||||||
noncomputable def Trace.overlay_left {idx₁ idx₂ : g₁.Index}
|
noncomputable def Trace.overlay_left {idx₁ idx₂ : g₁.Index}
|
||||||
(tr : Trace g₁ idx₁ idx₂ ρ₁ ρ₂) :
|
(tr : Trace g₁ idx₁ idx₂ ρ₁ ρ₂) :
|
||||||
Trace (g₁ ∙ g₂) (idx₁.castAdd g₂.size) (idx₂.castAdd g₂.size) ρ₁ ρ₂ := by
|
Trace (g₁ ∙ g₂) (idx₁.castAdd g₂.size) (idx₂.castAdd g₂.size) ρ₁ ρ₂ := by
|
||||||
@@ -29,6 +48,8 @@ noncomputable def Trace.overlay_left {idx₁ idx₂ : g₁.Index}
|
|||||||
· rwa [show (g₁ ∙ g₂).nodes = Fin.append g₁.nodes g₂.nodes from rfl, Fin.append_left]
|
· rwa [show (g₁ ∙ g₂).nodes = Fin.append g₁.nodes g₂.nodes from rfl, Fin.append_left]
|
||||||
· exact List.mem_append_left _ (List.mem_map_of_mem _ he)
|
· exact List.mem_append_left _ (List.mem_map_of_mem _ he)
|
||||||
|
|
||||||
|
/-- When two graphs are overlaid, for each trace in the right graph,
|
||||||
|
a corresponding trace exists in the combined graph. -/
|
||||||
noncomputable def Trace.overlay_right {idx₁ idx₂ : g₂.Index}
|
noncomputable def Trace.overlay_right {idx₁ idx₂ : g₂.Index}
|
||||||
(tr : Trace g₂ idx₁ idx₂ ρ₁ ρ₂) :
|
(tr : Trace g₂ idx₁ idx₂ ρ₁ ρ₂) :
|
||||||
Trace (g₁ ∙ g₂) (idx₁.natAdd g₁.size) (idx₂.natAdd g₁.size) ρ₁ ρ₂ := by
|
Trace (g₁ ∙ g₂) (idx₁.natAdd g₁.size) (idx₂.natAdd g₁.size) ρ₁ ρ₂ := by
|
||||||
@@ -41,6 +62,8 @@ noncomputable def Trace.overlay_right {idx₁ idx₂ : g₂.Index}
|
|||||||
· rwa [show (g₁ ∙ g₂).nodes = Fin.append g₁.nodes g₂.nodes from rfl, Fin.append_right]
|
· rwa [show (g₁ ∙ g₂).nodes = Fin.append g₁.nodes g₂.nodes from rfl, Fin.append_right]
|
||||||
· exact List.mem_append_right _ (List.mem_map_of_mem _ he)
|
· exact List.mem_append_right _ (List.mem_map_of_mem _ he)
|
||||||
|
|
||||||
|
/-- When two graphs are sequenced, for each trace in the first graph,
|
||||||
|
a corresponding trace exists in the combined graph. -/
|
||||||
noncomputable def Trace.sequence_left {idx₁ idx₂ : g₁.Index}
|
noncomputable def Trace.sequence_left {idx₁ idx₂ : g₁.Index}
|
||||||
(tr : Trace g₁ idx₁ idx₂ ρ₁ ρ₂) :
|
(tr : Trace g₁ idx₁ idx₂ ρ₁ ρ₂) :
|
||||||
Trace (g₁ ⤳ g₂) (idx₁.castAdd g₂.size) (idx₂.castAdd g₂.size) ρ₁ ρ₂ := by
|
Trace (g₁ ⤳ g₂) (idx₁.castAdd g₂.size) (idx₂.castAdd g₂.size) ρ₁ ρ₂ := by
|
||||||
@@ -53,6 +76,8 @@ noncomputable def Trace.sequence_left {idx₁ idx₂ : g₁.Index}
|
|||||||
· rwa [show (g₁ ⤳ g₂).nodes = Fin.append g₁.nodes g₂.nodes from rfl, Fin.append_left]
|
· rwa [show (g₁ ⤳ g₂).nodes = Fin.append g₁.nodes g₂.nodes from rfl, Fin.append_left]
|
||||||
· exact List.mem_append_left _ (List.mem_append_left _ (List.mem_map_of_mem _ he))
|
· exact List.mem_append_left _ (List.mem_append_left _ (List.mem_map_of_mem _ he))
|
||||||
|
|
||||||
|
/-- When two graphs are sequenced, for each trace in the second graph,
|
||||||
|
a corresponding trace exists in the combined graph. -/
|
||||||
noncomputable def Trace.sequence_right {idx₁ idx₂ : g₂.Index}
|
noncomputable def Trace.sequence_right {idx₁ idx₂ : g₂.Index}
|
||||||
(tr : Trace g₂ idx₁ idx₂ ρ₁ ρ₂) :
|
(tr : Trace g₂ idx₁ idx₂ ρ₁ ρ₂) :
|
||||||
Trace (g₁ ⤳ g₂) (idx₁.natAdd g₁.size) (idx₂.natAdd g₁.size) ρ₁ ρ₂ := by
|
Trace (g₁ ⤳ g₂) (idx₁.natAdd g₁.size) (idx₂.natAdd g₁.size) ρ₁ ρ₂ := by
|
||||||
@@ -66,6 +91,7 @@ noncomputable def Trace.sequence_right {idx₁ idx₂ : g₂.Index}
|
|||||||
· exact List.mem_append_left _
|
· exact List.mem_append_left _
|
||||||
(List.mem_append_right _ (List.mem_map_of_mem _ he))
|
(List.mem_append_right _ (List.mem_map_of_mem _ he))
|
||||||
|
|
||||||
|
/-- Equivalent of `Trace.overlay_left` for end-to-end traces. -/
|
||||||
noncomputable def EndToEndTrace.overlay_left (etr : EndToEndTrace g₁ ρ₁ ρ₂) :
|
noncomputable def EndToEndTrace.overlay_left (etr : EndToEndTrace g₁ ρ₁ ρ₂) :
|
||||||
EndToEndTrace (g₁ ∙ g₂) ρ₁ ρ₂ := by
|
EndToEndTrace (g₁ ∙ g₂) ρ₁ ρ₂ := by
|
||||||
obtain ⟨i₁, h₁, i₂, h₂, tr⟩ := etr
|
obtain ⟨i₁, h₁, i₂, h₂, tr⟩ := etr
|
||||||
@@ -73,6 +99,7 @@ noncomputable def EndToEndTrace.overlay_left (etr : EndToEndTrace g₁ ρ₁ ρ
|
|||||||
i₂.castAdd g₂.size, List.mem_append_left _ (List.mem_map_of_mem _ h₂),
|
i₂.castAdd g₂.size, List.mem_append_left _ (List.mem_map_of_mem _ h₂),
|
||||||
tr.overlay_left⟩
|
tr.overlay_left⟩
|
||||||
|
|
||||||
|
/-- Equivalent of `Trace.overlay_right` for end-to-end traces. -/
|
||||||
noncomputable def EndToEndTrace.overlay_right (etr : EndToEndTrace g₂ ρ₁ ρ₂) :
|
noncomputable def EndToEndTrace.overlay_right (etr : EndToEndTrace g₂ ρ₁ ρ₂) :
|
||||||
EndToEndTrace (g₁ ∙ g₂) ρ₁ ρ₂ := by
|
EndToEndTrace (g₁ ∙ g₂) ρ₁ ρ₂ := by
|
||||||
obtain ⟨i₁, h₁, i₂, h₂, tr⟩ := etr
|
obtain ⟨i₁, h₁, i₂, h₂, tr⟩ := etr
|
||||||
@@ -80,6 +107,13 @@ noncomputable def EndToEndTrace.overlay_right (etr : EndToEndTrace g₂ ρ₁ ρ
|
|||||||
i₂.natAdd g₁.size, List.mem_append_right _ (List.mem_map_of_mem _ h₂),
|
i₂.natAdd g₁.size, List.mem_append_right _ (List.mem_map_of_mem _ h₂),
|
||||||
tr.overlay_right⟩
|
tr.overlay_right⟩
|
||||||
|
|
||||||
|
/-- When two graphs are sequenced, two end-to-end traces through the respective
|
||||||
|
graphs can be sequenced to create an end-to-end trace in the combined
|
||||||
|
graph. This is only possible for end-to-end traces and not for general
|
||||||
|
`Trace`s, because sequencing only introduces edges from the output nodes
|
||||||
|
of one graph to the input nodes of another graph. A non-end-to-end trace
|
||||||
|
need to conclude at the output node, so it cannot necessarily be sequenced
|
||||||
|
with a trace in another graph. -/
|
||||||
noncomputable def EndToEndTrace.concat {ρ₃ : Env} (etr₁ : EndToEndTrace g₁ ρ₁ ρ₂)
|
noncomputable def EndToEndTrace.concat {ρ₃ : Env} (etr₁ : EndToEndTrace g₁ ρ₁ ρ₂)
|
||||||
(etr₂ : EndToEndTrace g₂ ρ₂ ρ₃) : EndToEndTrace (g₁ ⤳ g₂) ρ₁ ρ₃ := by
|
(etr₂ : EndToEndTrace g₂ ρ₂ ρ₃) : EndToEndTrace (g₁ ⤳ g₂) ρ₁ ρ₃ := by
|
||||||
obtain ⟨i₁, h₁, i₂, h₂, tr₁⟩ := etr₁
|
obtain ⟨i₁, h₁, i₂, h₂, tr₁⟩ := etr₁
|
||||||
@@ -92,12 +126,11 @@ noncomputable def EndToEndTrace.concat {ρ₃ : Env} (etr₁ : EndToEndTrace g
|
|||||||
|
|
||||||
end Embeddings
|
end Embeddings
|
||||||
|
|
||||||
/-! ### Loops -/
|
|
||||||
|
|
||||||
section Loop
|
section Loop
|
||||||
|
|
||||||
variable {g : Graph} {ρ₁ ρ₂ ρ₃ : Env}
|
variable {g : Graph} {ρ₁ ρ₂ ρ₃ : Env}
|
||||||
|
|
||||||
|
/-- A trace through a body CFG still exists (up to reindexing) in a zero-or-more loop CFG. -/
|
||||||
noncomputable def Trace.loop {idx₁ idx₂ : g.Index} (tr : Trace g idx₁ idx₂ ρ₁ ρ₂) :
|
noncomputable def Trace.loop {idx₁ idx₂ : g.Index} (tr : Trace g idx₁ idx₂ ρ₁ ρ₂) :
|
||||||
Trace (Graph.loop g) (idx₁.natAdd 2) (idx₂.natAdd 2) ρ₁ ρ₂ := by
|
Trace (Graph.loop g) (idx₁.natAdd 2) (idx₂.natAdd 2) ρ₁ ρ₂ := by
|
||||||
induction tr with
|
induction tr with
|
||||||
@@ -112,14 +145,17 @@ noncomputable def Trace.loop {idx₁ idx₂ : g.Index} (tr : Trace g idx₁ idx
|
|||||||
· exact List.mem_append_left _ (List.mem_append_left _
|
· exact List.mem_append_left _ (List.mem_append_left _
|
||||||
(List.mem_append_left _ (List.mem_map_of_mem _ he)))
|
(List.mem_append_left _ (List.mem_map_of_mem _ he)))
|
||||||
|
|
||||||
|
/-- The beginning node of a loop graph is empty. -/
|
||||||
private lemma loop_nodes_at_in :
|
private lemma loop_nodes_at_in :
|
||||||
(Graph.loop g).nodes g.loopIn = none :=
|
(Graph.loop g).nodes g.loopIn = none :=
|
||||||
Fin.append_left (fun _ : Fin 2 => none) g.nodes 0
|
Fin.append_left (fun _ : Fin 2 => none) g.nodes 0
|
||||||
|
|
||||||
|
/-- The ending node of a loop graph is empty. -/
|
||||||
private lemma loop_nodes_at_out :
|
private lemma loop_nodes_at_out :
|
||||||
(Graph.loop g).nodes g.loopOut = none :=
|
(Graph.loop g).nodes g.loopOut = none :=
|
||||||
Fin.append_left (fun _ : Fin 2 => none) g.nodes 1
|
Fin.append_left (fun _ : Fin 2 => none) g.nodes 1
|
||||||
|
|
||||||
|
/-- Equivlaent of `Trace.loop` for end-to-end traces. -/
|
||||||
noncomputable def EndToEndTrace.loop (etr : EndToEndTrace g ρ₁ ρ₂) :
|
noncomputable def EndToEndTrace.loop (etr : EndToEndTrace g ρ₁ ρ₂) :
|
||||||
EndToEndTrace (Graph.loop g) ρ₁ ρ₂ := by
|
EndToEndTrace (Graph.loop g) ρ₁ ρ₂ := by
|
||||||
obtain ⟨i₁, h₁, i₂, h₂, tr⟩ := etr
|
obtain ⟨i₁, h₁, i₂, h₂, tr⟩ := etr
|
||||||
@@ -135,11 +171,13 @@ noncomputable def EndToEndTrace.loop (etr : EndToEndTrace g ρ₁ ρ₂) :
|
|||||||
exact Trace.concat (Trace.single (loop_nodes_at_in ▸ EvalBasicStmtOpt.none)) hin
|
exact Trace.concat (Trace.single (loop_nodes_at_in ▸ EvalBasicStmtOpt.none)) hin
|
||||||
(Trace.concat tr.loop hout (Trace.single (loop_nodes_at_out ▸ EvalBasicStmtOpt.none)))
|
(Trace.concat tr.loop hout (Trace.single (loop_nodes_at_out ▸ EvalBasicStmtOpt.none)))
|
||||||
|
|
||||||
|
/-- The zero-or-more times loop has an edge to return back to the top, to continue after an iteration. -/
|
||||||
private lemma loop_edge_out_in :
|
private lemma loop_edge_out_in :
|
||||||
((g.loopOut, g.loopIn) : (Graph.loop g).Edge) ∈ (Graph.loop g).edges := by
|
((g.loopOut, g.loopIn) : (Graph.loop g).Edge) ∈ (Graph.loop g).edges := by
|
||||||
refine List.mem_append_right _ ?_
|
refine List.mem_append_right _ ?_
|
||||||
exact List.mem_cons_self _ _
|
exact List.mem_cons_self _ _
|
||||||
|
|
||||||
|
/-- Two traces through a loop can be combined, since a loop can be executed any number of times. -/
|
||||||
noncomputable def EndToEndTrace.loop_concat (etr₁ : EndToEndTrace (Graph.loop g) ρ₁ ρ₂)
|
noncomputable def EndToEndTrace.loop_concat (etr₁ : EndToEndTrace (Graph.loop g) ρ₁ ρ₂)
|
||||||
(etr₂ : EndToEndTrace (Graph.loop g) ρ₂ ρ₃) :
|
(etr₂ : EndToEndTrace (Graph.loop g) ρ₂ ρ₃) :
|
||||||
EndToEndTrace (Graph.loop g) ρ₁ ρ₃ := by
|
EndToEndTrace (Graph.loop g) ρ₁ ρ₃ := by
|
||||||
@@ -150,6 +188,7 @@ noncomputable def EndToEndTrace.loop_concat (etr₁ : EndToEndTrace (Graph.loop
|
|||||||
exact ⟨g.loopIn, List.mem_singleton_self _, g.loopOut, List.mem_singleton_self _,
|
exact ⟨g.loopIn, List.mem_singleton_self _, g.loopOut, List.mem_singleton_self _,
|
||||||
Trace.concat tr₁ loop_edge_out_in tr₂⟩
|
Trace.concat tr₁ loop_edge_out_in tr₂⟩
|
||||||
|
|
||||||
|
/-- A loop can be executed zero times. -/
|
||||||
noncomputable def EndToEndTrace.loop_empty {ρ : Env} : EndToEndTrace (Graph.loop g) ρ ρ := by
|
noncomputable def EndToEndTrace.loop_empty {ρ : Env} : EndToEndTrace (Graph.loop g) ρ ρ := by
|
||||||
have hedge : ((g.loopIn, g.loopOut) : (Graph.loop g).Edge) ∈ (Graph.loop g).edges :=
|
have hedge : ((g.loopIn, g.loopOut) : (Graph.loop g).Edge) ∈ (Graph.loop g).edges :=
|
||||||
List.mem_append_right _ (List.mem_cons_of_mem _ (List.mem_cons_self _ _))
|
List.mem_append_right _ (List.mem_cons_of_mem _ (List.mem_cons_self _ _))
|
||||||
|
|||||||
Reference in New Issue
Block a user