Library HoareTut.totalhoarelogic
Generation of Hoare proof obligations in total correctness
Global Set Asymmetric Patterns.
Set Implicit Arguments.
Require Export hoarelogicsemantics.
Require Wf.
Module TotalHoareLogic (HD: HoareLogicDefs).
Export HD.
Module HLD:=HD.
Definition sem_wp := wp.
Export Wf.
Syntactic definition of the weakest precondition.
Fixpoint synt_wp (prog: ImpProg) : Pred → Pred
:= fun post e ⇒
match prog with
| Iskip ⇒ post e
| (Iset A x expr) ⇒ post (E.upd x (E.eval expr e) e)
| (Iif cond p1 p2) ⇒
((E.eval cond e)=true → (synt_wp p1 post e))
∧ ((E.eval cond e)=false → (synt_wp p2 post e))
| (Iseq p1 p2) ⇒ synt_wp p1 (synt_wp p2 post) e
| (Iwhile cond p) ⇒
∃ inv:Pred,
∃ R:E.Env → E.Env → Prop,
(well_founded R)
∧ (inv e)
∧ (∀ e', (inv e')
→ (E.eval cond e')=false → post e')
∧ (∀ e', (inv e')
→ (E.eval cond e')=true → synt_wp p inv e')
∧ (∀ e0, (inv e0)
→ (E.eval cond e0)=true → synt_wp p (fun e1 ⇒ R e1 e0) e0)
end.
:= fun post e ⇒
match prog with
| Iskip ⇒ post e
| (Iset A x expr) ⇒ post (E.upd x (E.eval expr e) e)
| (Iif cond p1 p2) ⇒
((E.eval cond e)=true → (synt_wp p1 post e))
∧ ((E.eval cond e)=false → (synt_wp p2 post e))
| (Iseq p1 p2) ⇒ synt_wp p1 (synt_wp p2 post) e
| (Iwhile cond p) ⇒
∃ inv:Pred,
∃ R:E.Env → E.Env → Prop,
(well_founded R)
∧ (inv e)
∧ (∀ e', (inv e')
→ (E.eval cond e')=false → post e')
∧ (∀ e', (inv e')
→ (E.eval cond e')=true → synt_wp p inv e')
∧ (∀ e0, (inv e0)
→ (E.eval cond e0)=true → synt_wp p (fun e1 ⇒ R e1 e0) e0)
end.
Lemma synt_wp_monotonic:
∀ (p: ImpProg) (post1 post2: Pred),
(∀ e, post1 e → post2 e)
→ ∀ e, (synt_wp p post1 e) → (synt_wp p post2 e).
Proof.
induction p; simpl; firstorder eauto.
Qed.
Hint Resolve synt_wp_monotonic: hoare.
∀ (p: ImpProg) (post1 post2: Pred),
(∀ e, post1 e → post2 e)
→ ∀ e, (synt_wp p post1 e) → (synt_wp p post2 e).
Proof.
induction p; simpl; firstorder eauto.
Qed.
Hint Resolve synt_wp_monotonic: hoare.
Below, a little tactic to decompose a pair in hypothesis H
by giving the name n to the first component.
Ltac dec2 n H := case H; clear H; intros n H.
The property below is also satisfied by wp (using the fact that
the language is deterministic).
We need it here to prove the soundness.
Lemma synt_wp_conj:
∀ (p: ImpProg) (post1 post2: Pred) e,
(synt_wp p post1 e) → (synt_wp p post2 e)
→ (synt_wp p (fun e ⇒ post1 e ∧ post2 e) e).
Proof.
induction p; simpl; try ((intuition auto); fail).
intros post1 post2 e H1 H2.
intros; eapply synt_wp_monotonic.
2: apply (IHp1 _ _ _ H1 H2).
simpl; intuition auto.
intros post1 post2 e H1 H2.
dec2 inv1 H1.
dec2 R1 H1.
dec2 inv2 H2.
intros;
constructor 1 with (x:=fun e ⇒ inv1 e ∧ inv2 e).
constructor 1 with (x:=R1).
firstorder auto.
Qed.
∀ (p: ImpProg) (post1 post2: Pred) e,
(synt_wp p post1 e) → (synt_wp p post2 e)
→ (synt_wp p (fun e ⇒ post1 e ∧ post2 e) e).
Proof.
induction p; simpl; try ((intuition auto); fail).
intros post1 post2 e H1 H2.
intros; eapply synt_wp_monotonic.
2: apply (IHp1 _ _ _ H1 H2).
simpl; intuition auto.
intros post1 post2 e H1 H2.
dec2 inv1 H1.
dec2 R1 H1.
dec2 inv2 H2.
intros;
constructor 1 with (x:=fun e ⇒ inv1 e ∧ inv2 e).
constructor 1 with (x:=R1).
firstorder auto.
Qed.
The proof of soundness proceeds by induction over prog.
Lemma wp_sound: ∀ prog post, synt_wp prog post |= prog [=post=].
Proof.
unfold wp.
induction prog; simpl; try ((intuition eauto with hoare); fail).
intros post e.
set (b:=E.eval cond e).
cut (E.eval cond e=b); auto.
case b; firstorder eauto with hoare.
intros post e H; case (IHprog1 _ _ H).
intros e0 H0; case (IHprog2 post e0); firstorder eauto with hoare.
intros post e H.
dec2 inv H.
dec2 R H.
dec2 Rwf H.
dec2 Hinv H.
dec2 H1 H.
dec2 H2 H.
generalize Hinv.
pattern e.
eapply well_founded_ind; eauto.
clear Hinv e.
intros e' X H'.
set (b:=E.eval cond e').
cut (E.eval cond e'=b); auto.
case b; [ idtac | firstorder eauto with hoare ].
intros H5.
case (IHprog (wp (Iwhile cond prog) post) e');
[ idtac | (unfold wp; firstorder eauto with hoare) ].
eapply synt_wp_monotonic.
2:apply (synt_wp_conj _ _ _ _ (H2 _ H' H5) (H _ H' H5)).
simpl; unfold wp; intuition auto.
Qed.
Proof.
unfold wp.
induction prog; simpl; try ((intuition eauto with hoare); fail).
intros post e.
set (b:=E.eval cond e).
cut (E.eval cond e=b); auto.
case b; firstorder eauto with hoare.
intros post e H; case (IHprog1 _ _ H).
intros e0 H0; case (IHprog2 post e0); firstorder eauto with hoare.
intros post e H.
dec2 inv H.
dec2 R H.
dec2 Rwf H.
dec2 Hinv H.
dec2 H1 H.
dec2 H2 H.
generalize Hinv.
pattern e.
eapply well_founded_ind; eauto.
clear Hinv e.
intros e' X H'.
set (b:=E.eval cond e').
cut (E.eval cond e'=b); auto.
case b; [ idtac | firstorder eauto with hoare ].
intros H5.
case (IHprog (wp (Iwhile cond prog) post) e');
[ idtac | (unfold wp; firstorder eauto with hoare) ].
eapply synt_wp_monotonic.
2:apply (synt_wp_conj _ _ _ _ (H2 _ H' H5) (H _ H' H5)).
simpl; unfold wp; intuition auto.
Qed.
Auxiliary lemmas for completeness
A technical issue: the inversion of exec
Definition aux_wlp (prog: ImpProg) : Pred → Pred
:= fun post e ⇒
match prog with
| Iskip ⇒ post e
| (Iset A x expr) ⇒ post (E.upd x (E.eval expr e) e)
| (Iif cond p1 p2) ⇒
∀ e', exec e (if E.eval cond e then p1 else p2) e'
→ post e'
| (Iseq p1 p2) ⇒ ∀ e1 e2, exec e p1 e1 → exec e1 p2 e2 → post e2
| (Iwhile cond p) ⇒ ∀ e', exec e (Iif cond (Iseq p (Iwhile cond p)) Iskip) e' → post e'
end.
:= fun post e ⇒
match prog with
| Iskip ⇒ post e
| (Iset A x expr) ⇒ post (E.upd x (E.eval expr e) e)
| (Iif cond p1 p2) ⇒
∀ e', exec e (if E.eval cond e then p1 else p2) e'
→ post e'
| (Iseq p1 p2) ⇒ ∀ e1 e2, exec e p1 e1 → exec e1 p2 e2 → post e2
| (Iwhile cond p) ⇒ ∀ e', exec e (Iif cond (Iseq p (Iwhile cond p)) Iskip) e' → post e'
end.
Lemma exec_inversion:
∀ prog e e', (exec e prog e') → ∀ post, (aux_wlp prog post e) → post e'.
Proof.
induction 1; simpl;
try ((firstorder eauto with hoare); fail).
Qed.
∀ prog e e', (exec e prog e') → ∀ post, (aux_wlp prog post e) → post e'.
Proof.
induction 1; simpl;
try ((firstorder eauto with hoare); fail).
Qed.
Here is the case, where the previous lemma is better than the standard inversion of Coq.
Lemma exec_test_inversion:
∀ A (x:E.Var A) expr e e',
(exec e (Iset x expr) e') → e'=(E.upd x (E.eval expr e) e).
Proof.
intros A x expr e e' H.
∀ A (x:E.Var A) expr e e',
(exec e (Iset x expr) e') → e'=(E.upd x (E.eval expr e) e).
Proof.
intros A x expr e e' H.
Here, try "inversion H" instead the tactic below.
The generated goal is not directly provable.
Below, a little tactic to helps in applying exec_inversion.
Ltac exec_inversion H :=
match type of H with
| (exec ?e ?p ?e') ⇒ pattern e'; apply (exec_inversion H); simpl; clear H
end.
match type of H with
| (exec ?e ?p ?e') ⇒ pattern e'; apply (exec_inversion H); simpl; clear H
end.
The programming language is deterministic
Lemma exec_deterministic: ∀ ei p ef,
(exec ei p ef) → ∀ ef', (exec ei p ef') → ef=ef'.
Proof.
induction 1; intros ef' X; exec_inversion X; eauto.
intros e1 e2 X1 X2; assert (X3: e'=e1); auto.
subst; auto.
Qed.
(exec ei p ef) → ∀ ef', (exec ei p ef') → ef=ef'.
Proof.
induction 1; intros ef' X; exec_inversion X; eauto.
intros e1 e2 X1 X2; assert (X3: e'=e1); auto.
subst; auto.
Qed.
Definition of the variant
Given a program p and a boolean expression cond, the relation on environment "reduces cond p" is the variant required by "synt_wp (Iwhile cond p)".
Definition reduces cond p e1 e0 :=
(E.eval cond e0)=true ∧ (exec e0 p e1) ∧ ∃ ef, (exec e1 (Iwhile cond p) ef).
(E.eval cond e0)=true ∧ (exec e0 p e1) ∧ ∃ ef, (exec e1 (Iwhile cond p) ef).
To prove that "reduces cond p" is well-founded, I want to count
the number of execution of p in the computation of "Iwhile cond p".
Indeed, as the language is deterministic, this number is unique.
Hence, "execn n e (Iwhile cond p) e'" means that "exec e (Iwhile cond p) e'"
in a sequence of n execution of p.
Inductive execn: nat → E.Env → ImpProg → E.Env → Prop :=
| execn_Iskip:
∀ e, (execn 0 e Iskip e)
| execn_Iset:
∀ (A:Type) e x (expr: E.Expr A),
(execn 0 e (Iset x expr) (E.upd x (E.eval expr e) e))
| execn_Iif:
∀ n e (cond: E.Expr bool) p1 p2 e',
(execn n e (if (E.eval cond e) then p1 else p2) e')
→ (execn n e (Iif cond p1 p2) e')
| execn_Iseq:
∀ n e p1 p2 e' e'',
(exec e p1 e')
→ (execn n e' p2 e'')
→ (execn n e (Iseq p1 p2) e'')
| execn_Iwhile:
∀ n e cond p e',
(execn n e (Iif cond (Iseq p (Iwhile cond p)) Iskip) e')
→ (execn (S n) e (Iwhile cond p) e').
Hint Resolve execn_Iskip execn_Iset execn_Iif execn_Iseq execn_Iwhile: hoare.
Lemma exec_execn: ∀ ei p ef,
(exec ei p ef) → (∃ n, execn n ei p ef).
Proof.
induction 1; firstorder (eauto with hoare).
Qed.
| execn_Iskip:
∀ e, (execn 0 e Iskip e)
| execn_Iset:
∀ (A:Type) e x (expr: E.Expr A),
(execn 0 e (Iset x expr) (E.upd x (E.eval expr e) e))
| execn_Iif:
∀ n e (cond: E.Expr bool) p1 p2 e',
(execn n e (if (E.eval cond e) then p1 else p2) e')
→ (execn n e (Iif cond p1 p2) e')
| execn_Iseq:
∀ n e p1 p2 e' e'',
(exec e p1 e')
→ (execn n e' p2 e'')
→ (execn n e (Iseq p1 p2) e'')
| execn_Iwhile:
∀ n e cond p e',
(execn n e (Iif cond (Iseq p (Iwhile cond p)) Iskip) e')
→ (execn (S n) e (Iwhile cond p) e').
Hint Resolve execn_Iskip execn_Iset execn_Iif execn_Iseq execn_Iwhile: hoare.
Lemma exec_execn: ∀ ei p ef,
(exec ei p ef) → (∃ n, execn n ei p ef).
Proof.
induction 1; firstorder (eauto with hoare).
Qed.
In the proof below, I mainly use that "reduces cond p e1 e0"
implies that there exists n and ef such that "execn (S n) e0 (Iwhile cond p) ef"
and "execn n e1 (Iwhile cond p) ef".
Lemma reduces_wf: ∀ cond p, well_founded (reduces cond p).
Proof.
unfold well_founded.
intros cond p e0; apply Acc_intro.
intros e1 H; unfold reduces in H.
decompose [ex and] H; clear H.
clear H2 H0 e0.
case (exec_execn H1).
intros n.
generalize cond p e1 x; clear cond p e1 x H1.
elim n.
intros cond p e0 e1 H; inversion_clear H.
clear n; intros n HR cond p e0 e1 H.
inversion_clear H.
inversion_clear H0.
set (b:=E.eval cond e0) in × |-.
cut (E.eval cond e0=b); auto.
generalize H; clear H; case b; simpl.
intros H;
inversion_clear H.
intros;
apply Acc_intro.
intros e2 H3; unfold reduces in H3.
intuition.
rewrite (exec_deterministic H3 H0); eauto.
intros H H0; apply Acc_intro.
unfold reduces; rewrite H0.
intuition.
discriminate.
Qed.
Hint Resolve reduces_wf: hoare.
Proof.
unfold well_founded.
intros cond p e0; apply Acc_intro.
intros e1 H; unfold reduces in H.
decompose [ex and] H; clear H.
clear H2 H0 e0.
case (exec_execn H1).
intros n.
generalize cond p e1 x; clear cond p e1 x H1.
elim n.
intros cond p e0 e1 H; inversion_clear H.
clear n; intros n HR cond p e0 e1 H.
inversion_clear H.
inversion_clear H0.
set (b:=E.eval cond e0) in × |-.
cut (E.eval cond e0=b); auto.
generalize H; clear H; case b; simpl.
intros H;
inversion_clear H.
intros;
apply Acc_intro.
intros e2 H3; unfold reduces in H3.
intuition.
rewrite (exec_deterministic H3 H0); eauto.
intros H H0; apply Acc_intro.
unfold reduces; rewrite H0.
intuition.
discriminate.
Qed.
Hint Resolve reduces_wf: hoare.
Lemma wp_complete: ∀ prog post, prog [= post =] |= (synt_wp prog post).
Proof.
unfold wp.
intros prog post e H; case H; clear H.
intros e' H; case H; clear H.
generalize post e e'; clear post e e'; elim prog; clear prog; simpl.
intros post e e' H; exec_inversion H; auto.
intros A v expr post e e' H; exec_inversion H; auto.
intros cond p1 Hp1 p2 Hp2 post e e' H; exec_inversion H.
case (E.eval cond e); simpl; firstorder auto || discriminate.
intros p1 Hp1 p2 Hp2 post e e' H.
exec_inversion H.
eauto.
intros cond p Hp post e e' H H0.
constructor 1 with (x:=wp (Iwhile cond p) post).
constructor 1 with (x:=reduces cond p).
unfold wp; (intuition eauto with hoare);
dec2 e1 H1;
case H1; clear H1; intros H1;
exec_inversion H1;
intros e2 H1; exec_inversion H1;
rewrite H2; intros e3 H1; exec_inversion H1;
unfold reduces; eauto with hoare.
Qed.
Proof.
unfold wp.
intros prog post e H; case H; clear H.
intros e' H; case H; clear H.
generalize post e e'; clear post e e'; elim prog; clear prog; simpl.
intros post e e' H; exec_inversion H; auto.
intros A v expr post e e' H; exec_inversion H; auto.
intros cond p1 Hp1 p2 Hp2 post e e' H; exec_inversion H.
case (E.eval cond e); simpl; firstorder auto || discriminate.
intros p1 Hp1 p2 Hp2 post e e' H.
exec_inversion H.
eauto.
intros cond p Hp post e e' H H0.
constructor 1 with (x:=wp (Iwhile cond p) post).
constructor 1 with (x:=reduces cond p).
unfold wp; (intuition eauto with hoare);
dec2 e1 H1;
case H1; clear H1; intros H1;
exec_inversion H1;
intros e2 H1; exec_inversion H1;
rewrite H2; intros e3 H1; exec_inversion H1;
unfold reduces; eauto with hoare.
Qed.
Hint Resolve wp_complete wp_sound: hoare.
Theorem soundness: ∀ pre p post, pre |= (synt_wp p post) → pre |= p [=post=].
Proof.
auto with hoare.
Qed.
Theorem completeness: ∀ pre p post, pre |= p [=post=] → pre |= (synt_wp p post).
Proof.
intuition auto with hoare.
Qed.
End TotalHoareLogic.
"Tutorial on Hoare Logic" Library. Copyright 2007 Sylvain Boulme.
This file is distributed under the terms of the
"GNU LESSER GENERAL PUBLIC LICENSE" version 3.