WiSE.implem.bugfinder_proof
From Coq Require Import List Lia String ZArith Program.Equality.
From WiSE Require Import streams lang.imp symbolic.symex implem.bugfinder.
Import ListNotations.
Import LTL.
From WiSE Require Import streams lang.imp symbolic.symex implem.bugfinder.
Import ListNotations.
Import LTL.
Lemma map_in:
forall A B (f : A -> B) (l : list A) b,
In b (List.map f l) -> exists a, In a l /\ b = f a.
Proof.
intros. induction l; try easy.
destruct H as [<- | H].
- repeat econstructor.
- specialize (IHl H) as (a' & Ha' & ->).
exists a'. split; auto. now right.
Qed.
Theorem expand_sound:
forall path env prog t,
In t (expand path env prog) -> sym_step ((path, env), prog) t.
Proof.
intros path env prog [[path' env'] prog'] Hin.
induction prog in path', env', prog', Hin |-*; subst; try easy.
- destruct Hin as [[=<-<-<-] | [ [=<-<-<-] | [] ]]; constructor.
- destruct prog1; try easy.
+ destruct Hin as [[=<-<-<-] | []]. now econstructor.
+ pose proof (map_in _ _ _ _ _ Hin) as ([[path2 env2] prog3] & [H1 [=->->->]]).
specialize (IHprog1 _ _ _ H1).
now constructor.
+ pose proof (map_in _ _ _ _ _ Hin) as ([[path2 env2] prog3] & [H1 [=->->->]]).
specialize (IHprog1 _ _ _ H1).
now constructor.
+ pose proof (map_in _ _ _ _ _ Hin) as ([[path2 env2] prog3] & [H1 [=->->->]]).
specialize (IHprog1 _ _ _ H1).
now constructor.
+ pose proof (map_in _ _ _ _ _ Hin) as ([[path2 env2] prog3] & [H1 [=->->->]]).
specialize (IHprog1 _ _ _ H1).
now constructor.
- destruct Hin as [[=<-<-<-] | []]. now constructor.
- destruct Hin as [[=<-<-<-] | [[=<-<-<-]| []]]; now constructor.
Qed.
forall path env prog t,
In t (expand path env prog) -> sym_step ((path, env), prog) t.
Proof.
intros path env prog [[path' env'] prog'] Hin.
induction prog in path', env', prog', Hin |-*; subst; try easy.
- destruct Hin as [[=<-<-<-] | [ [=<-<-<-] | [] ]]; constructor.
- destruct prog1; try easy.
+ destruct Hin as [[=<-<-<-] | []]. now econstructor.
+ pose proof (map_in _ _ _ _ _ Hin) as ([[path2 env2] prog3] & [H1 [=->->->]]).
specialize (IHprog1 _ _ _ H1).
now constructor.
+ pose proof (map_in _ _ _ _ _ Hin) as ([[path2 env2] prog3] & [H1 [=->->->]]).
specialize (IHprog1 _ _ _ H1).
now constructor.
+ pose proof (map_in _ _ _ _ _ Hin) as ([[path2 env2] prog3] & [H1 [=->->->]]).
specialize (IHprog1 _ _ _ H1).
now constructor.
+ pose proof (map_in _ _ _ _ _ Hin) as ([[path2 env2] prog3] & [H1 [=->->->]]).
specialize (IHprog1 _ _ _ H1).
now constructor.
- destruct Hin as [[=<-<-<-] | []]. now constructor.
- destruct Hin as [[=<-<-<-] | [[=<-<-<-]| []]]; now constructor.
Qed.
Theorem expand_complete:
forall path env prog t,
sym_step ((path, env), prog) t ->
In t (expand path env prog).
Proof.
intros * Ht.
dependent induction Ht; intros; simpl.
- now econstructor.
- change (π2, s2, Seq c2 c3) with (then_do c3 (π2, s2, c2)).
destruct c1; try easy.
all: eapply in_map, IHHt; eauto.
- now left.
- now left.
- now (right; left).
- now left.
- now (right; left).
Qed.
forall path env prog t,
sym_step ((path, env), prog) t ->
In t (expand path env prog).
Proof.
intros * Ht.
dependent induction Ht; intros; simpl.
- now econstructor.
- change (π2, s2, Seq c2 c3) with (then_do c3 (π2, s2, c2)).
destruct c1; try easy.
all: eapply in_map, IHHt; eauto.
- now left.
- now left.
- now (right; left).
- now left.
- now (right; left).
Qed.
expand spawns 0, 1 or 2 tasks
Theorem expand_inv:
forall path env prog,
(expand path env prog = []) \/
(exists s, expand path env prog = [s]) \/
(exists s1 s2, expand path env prog = [s1; s2]).
Proof.
intros. induction prog.
- now left.
- now right; right; repeat econstructor.
- destruct IHprog1 as [IH | [(s & Hs) | (s1 & s2 & Hs)]].
+ destruct prog1; simpl in *; try easy.
now right; left; repeat econstructor. rewrite IH.
now left.
now left.
+ destruct prog1; simpl in *; try easy.
right. repeat econstructor. now rewrite Hs.
right. repeat econstructor.
+ destruct prog1; simpl in *; try easy.
now right; right; repeat econstructor.
now right; right; repeat econstructor; rewrite Hs.
right; right; repeat econstructor; rewrite Hs.
- now right; left; repeat econstructor.
- now left.
- now right; right; repeat econstructor.
Qed.
forall path env prog,
(expand path env prog = []) \/
(exists s, expand path env prog = [s]) \/
(exists s1 s2, expand path env prog = [s1; s2]).
Proof.
intros. induction prog.
- now left.
- now right; right; repeat econstructor.
- destruct IHprog1 as [IH | [(s & Hs) | (s1 & s2 & Hs)]].
+ destruct prog1; simpl in *; try easy.
now right; left; repeat econstructor. rewrite IH.
now left.
now left.
+ destruct prog1; simpl in *; try easy.
right. repeat econstructor. now rewrite Hs.
right. repeat econstructor.
+ destruct prog1; simpl in *; try easy.
now right; right; repeat econstructor.
now right; right; repeat econstructor; rewrite Hs.
right; right; repeat econstructor; rewrite Hs.
- now right; left; repeat econstructor.
- now left.
- now right; right; repeat econstructor.
Qed.
Eager model of run
The run function that executes the main loop of the bugfinder generates a lazy stream. Lazy streams are defined co-inductively which make them somwhat hard to reason about. To ease the proofs, we provide an eager implementation run_n of run that simulates the behavior of run for n steps of computation. We then relate the 2 functions by a theorem run_run_n.
Theorem run_eq:
forall l path env prog,
run ((path, env, prog)::l) = scons (Some (path, env, prog)) (run (l ++ expand path env prog)).
Proof.
intros. now rewrite <- force_id at 1.
Qed.
forall l path env prog,
run ((path, env, prog)::l) = scons (Some (path, env, prog)) (run (l ++ expand path env prog)).
Proof.
intros. now rewrite <- force_id at 1.
Qed.
the run [] is a fixpoint for shift i.e.
once the task list is empty, run loops indefinitely
in a state where the task list remains empty
Relation between run and its eager model run_n.
run_n n l computes the task list after n iterations run l
Lemma run_run_n:
forall n l, shift n (run l) = run (run_n n l).
Proof.
intros. induction n in l |-*; try easy.
destruct l as [| [[path env] prog] l] .
- apply run_nil.
- rewrite run_eq. simpl. now rewrite IHn.
Qed.
Lemma run_n_nil:
forall n, run_n n [] = [].
Proof.
now induction n.
Qed.
forall n l, shift n (run l) = run (run_n n l).
Proof.
intros. induction n in l |-*; try easy.
destruct l as [| [[path env] prog] l] .
- apply run_nil.
- rewrite run_eq. simpl. now rewrite IHn.
Qed.
Lemma run_n_nil:
forall n, run_n n [] = [].
Proof.
now induction n.
Qed.
Theorem run_n_length:
forall l1 l2,
exists l3,
run_n (List.length l1) (l1 ++ l2) = l2 ++ l3.
Proof.
intros. induction l1 as [|[[path env] prog] l1 IH] in l2 |-*.
- simpl. exists []. now rewrite app_nil_r.
- simpl. rewrite <- List.app_assoc.
specialize (IH (l2 ++ expand path env prog)) as [l3 Hl3].
rewrite Hl3. rewrite <- List.app_assoc. now eexists.
Qed.
forall l1 l2,
exists l3,
run_n (List.length l1) (l1 ++ l2) = l2 ++ l3.
Proof.
intros. induction l1 as [|[[path env] prog] l1 IH] in l2 |-*.
- simpl. exists []. now rewrite app_nil_r.
- simpl. rewrite <- List.app_assoc.
specialize (IH (l2 ++ expand path env prog)) as [l3 Hl3].
rewrite Hl3. rewrite <- List.app_assoc. now eexists.
Qed.
After 1 + List.length l1 iterations, the task list of run (t::l1 ++ l2)
starts with l2 followed with the task spawed by executing t
Theorem run_n_S_length:
forall l1 l2 path env prog,
exists l3,
run_n (S (List.length l1)) ((path, env, prog)::l1 ++ l2) = l2 ++ (expand path env prog) ++ l3.
Proof.
intros. induction l1 as [|[[path1 env1] prog1] l1 IH] in l2, path, env, prog |-*.
- simpl. exists []. now rewrite app_nil_r.
- simpl. do 2 rewrite <- List.app_assoc. simpl in IH.
specialize (IH (l2 ++ expand path env prog) path1 env1 prog1).
simpl in IH. edestruct IH as [l3 Hl3]. eexists.
repeat rewrite <- List.app_assoc in Hl3. apply Hl3.
Qed.
forall l1 l2 path env prog,
exists l3,
run_n (S (List.length l1)) ((path, env, prog)::l1 ++ l2) = l2 ++ (expand path env prog) ++ l3.
Proof.
intros. induction l1 as [|[[path1 env1] prog1] l1 IH] in l2, path, env, prog |-*.
- simpl. exists []. now rewrite app_nil_r.
- simpl. do 2 rewrite <- List.app_assoc. simpl in IH.
specialize (IH (l2 ++ expand path env prog) path1 env1 prog1).
simpl in IH. edestruct IH as [l3 Hl3]. eexists.
repeat rewrite <- List.app_assoc in Hl3. apply Hl3.
Qed.
LTL Specification Predicates
Definition reachable_from (tasks : list sym_state) : LTL.t :=
now (fun s =>
match s with
| None => True
| Some s => exists s0, In s0 tasks /\ sym_steps s0 s
end
).
now (fun s =>
match s with
| None => True
| Some s => exists s0, In s0 tasks /\ sym_steps s0 s
end
).
The current state in the stream is s
Definition here (s : sym_state) : LTL.t :=
now (fun st =>
match st with
| Some s' => s' = s
| None => False
end
).
Notation "! x" := (here x).
Definition bug_found s : LTL.t :=
now (fun (st : status) =>
match st with
| BugFound s' => s' = s
| _ => False
end
).
Notation "!! x" := (bug_found x).
Definition potential_bug p : LTL.t :=
now (fun (st : status) =>
match st with
| BugFound s' => potential_bug (Bcst true, id, p) s'
| _ => True
end
).
Definition none : LTL.t :=
now (fun (st : option sym_state) =>
match st with
| None => True
| _ => False
end
).
Definition done : LTL.t :=
now (fun (st : status) =>
match st with
| Finished => True
| _ => False
end
).
now (fun st =>
match st with
| Some s' => s' = s
| None => False
end
).
Notation "! x" := (here x).
Definition bug_found s : LTL.t :=
now (fun (st : status) =>
match st with
| BugFound s' => s' = s
| _ => False
end
).
Notation "!! x" := (bug_found x).
Definition potential_bug p : LTL.t :=
now (fun (st : status) =>
match st with
| BugFound s' => potential_bug (Bcst true, id, p) s'
| _ => True
end
).
Definition none : LTL.t :=
now (fun (st : option sym_state) =>
match st with
| None => True
| _ => False
end
).
Definition done : LTL.t :=
now (fun (st : status) =>
match st with
| Finished => True
| _ => False
end
).
Soundess of run
Theorem run_sound:
forall l,
run l ⊨ □ reachable_from l.
Proof.
intros l n. rewrite run_run_n.
induction n in l |-*.
- simpl in *. destruct l as [| [[path env] prog]]; try easy.
repeat econstructor.
- destruct l as [| [[path env] prog] l]; try easy.
specialize (IHn (l ++ expand path env prog)).
simpl. destruct run_n as [| [[path1 env1] prog1]] eqn:Heq; try easy.
destruct IHn as [[[path2 env2] prog2] [[H | H]%in_app_iff Hsteps]].
+ eexists. split; eauto. now right.
+ pose proof (expand_sound _ _ _ _ H).
eexists. split. now left.
econstructor; eauto.
Qed.
forall l,
run l ⊨ □ reachable_from l.
Proof.
intros l n. rewrite run_run_n.
induction n in l |-*.
- simpl in *. destruct l as [| [[path env] prog]]; try easy.
repeat econstructor.
- destruct l as [| [[path env] prog] l]; try easy.
specialize (IHn (l ++ expand path env prog)).
simpl. destruct run_n as [| [[path1 env1] prog1]] eqn:Heq; try easy.
destruct IHn as [[[path2 env2] prog2] [[H | H]%in_app_iff Hsteps]].
+ eexists. split; eauto. now right.
+ pose proof (expand_sound _ _ _ _ H).
eexists. split. now left.
econstructor; eauto.
Qed.
Completeness of run
Theorem run_step_complete:
forall l s s',
sym_step s s' ->
run l ⊨ (!s → ◊!s').
Proof.
intros * Hstep H.
destruct l as [| [[path env] prog]]; try easy.
cbn in H. subst.
apply expand_complete in Hstep.
destruct (expand_inv path env prog) as [Htask | [[s Htask] | (s1 & s2 & Htask)]].
- now rewrite Htask in Hstep.
- rewrite run_eq, Htask in *. inversion Hstep; subst; try easy.
exists (S (List.length l)). simpl.
rewrite run_run_n.
pose proof (run_n_length l [s']) as [l3 Hl3].
replace (run_n (Datatypes.length l) (l ++ [s'])) with ([s'] ++ l3) at 1.
now destruct s' as [[a b] c].
- rewrite run_eq, Htask in *. destruct Hstep as [-> | [ -> | []]].
+ exists (S (List.length l)). simpl.
rewrite run_run_n.
pose proof (run_n_length l [s'; s2]) as [l3 Hl3].
replace (run_n (Datatypes.length l) (l ++ [s'; s2])) with ([s'; s2] ++ l3) at 1.
simpl. now destruct s' as [[a b] c].
+ exists (S (S (List.length l))). rewrite shift_eq.
rewrite run_run_n.
replace (l ++ [s1; s']) with ((l ++ [s1]) ++ [s']) at 1 by now rewrite <- List.app_assoc.
pose proof (run_n_length (l ++ [s1]) [s']) as [l3 Hl3].
replace (Datatypes.length (l ++ [s1])) with (S (Datatypes.length l)) in Hl3.
replace (run_n (S (Datatypes.length l)) ((l ++ [s1]) ++ [s'])) with ([s'] ++ l3) at 1.
now destruct s' as [[a b] c].
rewrite List.app_length. simpl. lia.
Qed.
forall l s s',
sym_step s s' ->
run l ⊨ (!s → ◊!s').
Proof.
intros * Hstep H.
destruct l as [| [[path env] prog]]; try easy.
cbn in H. subst.
apply expand_complete in Hstep.
destruct (expand_inv path env prog) as [Htask | [[s Htask] | (s1 & s2 & Htask)]].
- now rewrite Htask in Hstep.
- rewrite run_eq, Htask in *. inversion Hstep; subst; try easy.
exists (S (List.length l)). simpl.
rewrite run_run_n.
pose proof (run_n_length l [s']) as [l3 Hl3].
replace (run_n (Datatypes.length l) (l ++ [s'])) with ([s'] ++ l3) at 1.
now destruct s' as [[a b] c].
- rewrite run_eq, Htask in *. destruct Hstep as [-> | [ -> | []]].
+ exists (S (List.length l)). simpl.
rewrite run_run_n.
pose proof (run_n_length l [s'; s2]) as [l3 Hl3].
replace (run_n (Datatypes.length l) (l ++ [s'; s2])) with ([s'; s2] ++ l3) at 1.
simpl. now destruct s' as [[a b] c].
+ exists (S (S (List.length l))). rewrite shift_eq.
rewrite run_run_n.
replace (l ++ [s1; s']) with ((l ++ [s1]) ++ [s']) at 1 by now rewrite <- List.app_assoc.
pose proof (run_n_length (l ++ [s1]) [s']) as [l3 Hl3].
replace (Datatypes.length (l ++ [s1])) with (S (Datatypes.length l)) in Hl3.
replace (run_n (S (Datatypes.length l)) ((l ++ [s1]) ++ [s'])) with ([s'] ++ l3) at 1.
now destruct s' as [[a b] c].
rewrite List.app_length. simpl. lia.
Qed.
run is complete for sym_steps:
At any point in time, if s is generated by run l, then
all the sym_steps sucessors of s are eventually generated
Theorem run_steps_complete:
forall s s',
sym_steps s s' ->
forall l,
run l ⊨ □ (!s → ◊!s').
Proof.
intros s s' H.
dependent induction H.
- intros l n Hn. now exists 0.
- intros l n Hn. rewrite run_run_n in *.
pose proof (run_step_complete (run_n n l) _ _ H Hn) as [m Hm]. simpl in Hm.
specialize (IHstar _ _ Hm) as [k Hk].
rewrite shift_shift, run_run_n in Hk.
eexists (k + m). now rewrite run_run_n.
Qed.
forall s s',
sym_steps s s' ->
forall l,
run l ⊨ □ (!s → ◊!s').
Proof.
intros s s' H.
dependent induction H.
- intros l n Hn. now exists 0.
- intros l n Hn. rewrite run_run_n in *.
pose proof (run_step_complete (run_n n l) _ _ H Hn) as [m Hm]. simpl in Hm.
specialize (IHstar _ _ Hm) as [k Hk].
rewrite shift_shift, run_run_n in Hk.
eexists (k + m). now rewrite run_run_n.
Qed.
run is a complete wau to compute the sym_steps sucessors
of any state s:
starting with the task [s], run [s] eventually generates
all sym_steps sucessors of s
Theorem run_complete:
forall s s',
sym_steps s s' -> run [s] ⊨ ◊!s'.
Proof.
intros.
now pose proof (run_steps_complete _ _ H [s] 0 (run_here s)).
Qed.
Theorem run_finished_nil:
forall l,
run l ⊨ none -> l = [].
Proof.
now intros [|[[path env] prog]].
Qed.
Theorem run_finished:
forall l,
run l ⊨ □ (none → □ none).
Proof.
intros l n Hn m.
rewrite run_run_n in Hn.
apply run_finished_nil in Hn.
rewrite run_run_n, Hn.
now rewrite run_nil.
Qed.
forall s s',
sym_steps s s' -> run [s] ⊨ ◊!s'.
Proof.
intros.
now pose proof (run_steps_complete _ _ H [s] 0 (run_here s)).
Qed.
Theorem run_finished_nil:
forall l,
run l ⊨ none -> l = [].
Proof.
now intros [|[[path env] prog]].
Qed.
Theorem run_finished:
forall l,
run l ⊨ □ (none → □ none).
Proof.
intros l n Hn m.
rewrite run_run_n in Hn.
apply run_finished_nil in Hn.
rewrite run_run_n, Hn.
now rewrite run_nil.
Qed.
Theorem find_bugs_sound:
forall p,
find_bugs p ⊨ □ (potential_bug p).
Proof.
intros. unfold find_bugs.
pose proof (run_sound (init p)).
intros n. specialize (H n).
unfold potential_bug, reachable_from, now in *.
rewrite get_shift in *. simpl get in *.
rewrite get_map in *. destruct display eqn:Heq1; try easy.
destruct get as [[[path env] prog]|] eqn:Heq2; simpl in Heq1; try easy.
destruct (is_error prog) eqn:Heq3.
apply is_error_correct in Heq3. injection Heq1 as <-.
now destruct H as [[[path0 env0] prog0] [[[=->->->]|] H2]].
now destruct is_skip.
Qed.
forall p,
find_bugs p ⊨ □ (potential_bug p).
Proof.
intros. unfold find_bugs.
pose proof (run_sound (init p)).
intros n. specialize (H n).
unfold potential_bug, reachable_from, now in *.
rewrite get_shift in *. simpl get in *.
rewrite get_map in *. destruct display eqn:Heq1; try easy.
destruct get as [[[path env] prog]|] eqn:Heq2; simpl in Heq1; try easy.
destruct (is_error prog) eqn:Heq3.
apply is_error_correct in Heq3. injection Heq1 as <-.
now destruct H as [[[path0 env0] prog0] [[[=->->->]|] H2]].
now destruct is_skip.
Qed.
Theorem find_bugs_complete:
forall p s',
symex.potential_bug (Bcst true, id, p) s' ->
find_bugs p ⊨ ◊ (bug_found s').
Proof.
intros p [[path env] prog] [H1 H2].
pose proof (run_complete _ _ H1) as [n Hn].
exists n. unfold find_bugs, bug_found, here, now in *.
rewrite get_shift in *. simpl in *.
rewrite get_map. unfold init.
destruct get eqn:Heq1; subst; try easy.
unfold display.
destruct is_error eqn:Heq2; try easy.
destruct prog; try easy.
apply is_error_correct in H2.
now rewrite H2 in Heq2.
Qed.
forall p s',
symex.potential_bug (Bcst true, id, p) s' ->
find_bugs p ⊨ ◊ (bug_found s').
Proof.
intros p [[path env] prog] [H1 H2].
pose proof (run_complete _ _ H1) as [n Hn].
exists n. unfold find_bugs, bug_found, here, now in *.
rewrite get_shift in *. simpl in *.
rewrite get_map. unfold init.
destruct get eqn:Heq1; subst; try easy.
unfold display.
destruct is_error eqn:Heq2; try easy.
destruct prog; try easy.
apply is_error_correct in H2.
now rewrite H2 in Heq2.
Qed.
A symbolic state denotes a valid bug in
p if all states in its concretization are bugs
A status message is valid wrt prog p
if it is a BugFound message reporting a ValidBug
or any other kind of status message
Definition ValidStatus p :=
now (fun st =>
match st with
| BugFound σ' => ValidBug p σ'
| _ => True
end
).
Definition Symbolic σ :=
now (fun st =>
match st with
| BugFound σ' => Concrete σ' σ
| _ => False
end
).
Theorem relative_completeness:
forall p σ,
imp.IsBug p σ -> find_bugs p ⊨ ◊ Symbolic σ.
Proof.
intros * [(V0 & σ' & [Hsteps Hequiv])%Reach_complete H].
pose proof (run_complete _ _ Hsteps) as [n Hn].
exists n.
unfold find_bugs, Symbolic, bug_found, here, now in *.
rewrite get_shift in *. simpl in *.
rewrite get_map. unfold init, display.
destruct get as [[[π senv] p']|]; auto.
rewrite <- Hn in Hequiv. destruct σ as [V ?].
destruct (is_error p') eqn:Herr.
- now exists V0.
- destruct Hequiv as [_ [-> ->]].
apply is_error_Stuck in H.
now rewrite H in Herr.
Qed.
Theorem relative_soundness:
forall p,
find_bugs p ⊨ □ ValidStatus p.
Proof.
intros p n.
pose proof (find_bugs_sound p n).
unfold ValidStatus, potential_bug, now, get in *.
destruct (shift) eqn:Heq.
destruct x as [ [[φ senv] p'] | | ]; auto.
intros [V p''] (V0 & HV0).
destruct H as [H2 H3].
split.
- apply symex.Reach_sound.
now exists V0, (φ, senv, p').
- destruct HV0 as [_ [_ <-]].
now apply is_error_Stuck, is_error_correct.
Qed.
now (fun st =>
match st with
| BugFound σ' => ValidBug p σ'
| _ => True
end
).
Definition Symbolic σ :=
now (fun st =>
match st with
| BugFound σ' => Concrete σ' σ
| _ => False
end
).
Theorem relative_completeness:
forall p σ,
imp.IsBug p σ -> find_bugs p ⊨ ◊ Symbolic σ.
Proof.
intros * [(V0 & σ' & [Hsteps Hequiv])%Reach_complete H].
pose proof (run_complete _ _ Hsteps) as [n Hn].
exists n.
unfold find_bugs, Symbolic, bug_found, here, now in *.
rewrite get_shift in *. simpl in *.
rewrite get_map. unfold init, display.
destruct get as [[[π senv] p']|]; auto.
rewrite <- Hn in Hequiv. destruct σ as [V ?].
destruct (is_error p') eqn:Herr.
- now exists V0.
- destruct Hequiv as [_ [-> ->]].
apply is_error_Stuck in H.
now rewrite H in Herr.
Qed.
Theorem relative_soundness:
forall p,
find_bugs p ⊨ □ ValidStatus p.
Proof.
intros p n.
pose proof (find_bugs_sound p n).
unfold ValidStatus, potential_bug, now, get in *.
destruct (shift) eqn:Heq.
destruct x as [ [[φ senv] p'] | | ]; auto.
intros [V p''] (V0 & HV0).
destruct H as [H2 H3].
split.
- apply symex.Reach_sound.
now exists V0, (φ, senv, p').
- destruct HV0 as [_ [_ <-]].
now apply is_error_Stuck, is_error_correct.
Qed.
"termination" of the bugfinding loop:
If at any point in time find_bugs p emits
a Finished token, then the exploration of p
terminated. We encode this property by
asserting that after the first Finished token,
the only message that the loop will ever send is Finished
(i.e. it cannot find new bugs afterward)
Theorem sound_termination:
forall p,
find_bugs p ⊨ □ (done → □ done).
Proof.
intros p. unfold find_bugs.
pose proof (run_finished (init p)).
intros n Hn m.
assert (Hnone : none (shift n (run (init p)))).
{ unfold none, done, now in Hn |-*. rewrite get_shift in *.
simpl in *. rewrite get_map in Hn.
destruct get as [[[path env] prog]|] eqn:Heq1; try easy.
simpl in Hn. destruct is_error eqn:Heq2; try easy.
}
specialize (H n Hnone m). clear Hn Hnone.
rewrite shift_shift in *.
unfold done, none, now in *.
rewrite get_shift in *. simpl in *.
rewrite get_map. now destruct get eqn:Heq.
Qed.
forall p,
find_bugs p ⊨ □ (done → □ done).
Proof.
intros p. unfold find_bugs.
pose proof (run_finished (init p)).
intros n Hn m.
assert (Hnone : none (shift n (run (init p)))).
{ unfold none, done, now in Hn |-*. rewrite get_shift in *.
simpl in *. rewrite get_map in Hn.
destruct get as [[[path env] prog]|] eqn:Heq1; try easy.
simpl in Hn. destruct is_error eqn:Heq2; try easy.
}
specialize (H n Hnone m). clear Hn Hnone.
rewrite shift_shift in *.
unfold done, none, now in *.
rewrite get_shift in *. simpl in *.
rewrite get_map. now destruct get eqn:Heq.
Qed.