You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
semantics-2023/theories/program_logics/hoare_lib.v

726 lines
19 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

From iris.prelude Require Import options.
From iris.proofmode Require Import tactics.
From iris.heap_lang Require Import lang notation.
From iris.base_logic Require Export invariants.
From semantics.pl.heap_lang Require Export primitive_laws_nolater.
From semantics.pl.heap_lang Require Import adequacy proofmode.
Module hoare.
(* We make "ghost_state" an axiom for now to simplify the Coq development.
In the future, we will quantify over it. *)
Axiom ghost_state: heapGS heapΣ.
#[export] Existing Instance ghost_state.
(* the type of preconditions and postconditions *)
Notation iProp := (iProp heapΣ).
Implicit Types
(P Q R: iProp)
(φ ψ: Prop)
(e: expr)
(v: val).
Lemma ent_equiv P Q :
(P Q) (P Q) (Q P).
Proof. apply bi.equiv_entails. Qed.
(* Rules for entailment *)
Lemma ent_refl P:
P P.
Proof. eauto. Qed.
Lemma ent_trans P Q R:
(P Q) (Q R) (P R).
Proof. by intros ->. Qed.
(* NOTE: True = ⌜True⌝ *)
(* NOTE: False = ⌜False⌝ *)
Lemma ent_prove_pure P φ:
φ P φ.
Proof. eauto. Qed.
Lemma ent_assume_pure P Q φ:
(P φ)
(φ P Q)
P Q.
Proof.
iIntros (Hent Hpost) "P". iPoseProof (Hent with "P") as "%".
by iApply Hpost.
Qed.
Lemma ent_and_elim_r P Q :
P Q Q.
Proof.
iIntros "[_ $]".
Qed.
Lemma ent_and_elim_l P Q :
P Q P.
Proof.
iIntros "[$ _ ]".
Qed.
Lemma ent_and_intro P Q R :
(P Q)
(P R)
P Q R.
Proof.
iIntros (HQ HR) "HP". iSplit.
+ by iApply HQ.
+ by iApply HR.
Qed.
Lemma ent_or_introl P Q :
P P Q.
Proof. eauto. Qed.
Lemma ent_or_intror P Q :
Q P Q.
Proof. eauto. Qed.
Lemma ent_or_elim P Q R :
(P R)
(Q R)
P Q R.
Proof.
iIntros (HP HQ) "[HP | HQ]"; [by iApply HP | by iApply HQ].
Qed.
Lemma ent_all_intro {X} P Φ :
( x : X, P Φ x)
P x : X, Φ x.
Proof.
iIntros (Hx) "HP". iIntros(x).
by iApply Hx.
Qed.
Lemma ent_all_elim {X} (x : X) (Φ : _ iProp) :
( x, Φ x) Φ x.
Proof. eauto. Qed.
Lemma ent_exist_intro {X} (x : X) P Φ :
(P Φ x)
P x, Φ x.
Proof.
iIntros (HP) "HP". iExists x. by iApply HP.
Qed.
Lemma ent_exist_elim {X} Φ Q :
( x : X, (Φ x Q))
( x, Φ x) Q.
Proof.
iIntros (HQ) "(%x & Hx)". by iApply HQ.
Qed.
(** Separating conjunction rules *)
Lemma ent_sep_comm P Q:
P Q Q P.
Proof. iSplit; iIntros "[$ $]". Qed.
Lemma ent_sep_assoc P1 P2 P3:
P1 (P2 P3) (P1 P2) P3.
Proof.
iSplit.
- iIntros "[$ [$ $]]".
- iIntros "[[$ $] $]".
Qed.
Lemma ent_sep_split P P' Q Q':
(P Q) (P' Q') (P P') Q Q'.
Proof.
by intros -> ->.
Qed.
Lemma ent_sep_true P :
P True P.
Proof.
iIntros "$".
Qed.
Lemma ent_sep_weaken P Q:
P Q P.
Proof.
iIntros "[$ _]".
Qed.
Lemma ent_pointsto_sep l v w :
l v l w False.
Proof.
iIntros "[Ha Hb]".
iPoseProof (mapsto_ne with "Ha Hb") as "%Hneq".
done.
Qed.
Lemma ent_exists_sep {X} (Φ : X iProp) P :
( x : X, Φ x) P ( x : X, Φ x P).
Proof.
iIntros "((%x & Hx) & Hp)". eauto with iFrame.
Qed.
(** Magic wand rules *)
Lemma ent_wand_intro P Q R :
(P Q R)
(P Q - R).
Proof.
iIntros (Hr) "HP HQ". iApply Hr. iFrame.
Qed.
Lemma ent_wand_elim P Q R :
(P Q - R)
P Q R.
Proof.
iIntros (Hr) "[HP HQ]". iApply (Hr with "HP HQ").
Qed.
(** Hoare rules *)
Implicit Types
(Φ Ψ: val iProp).
Definition hoare P (e: expr) Φ := P WP e {{ Φ }}.
Global Notation "{{ P } } e {{ Φ } }" := (hoare P%I e%E Φ%I)
(at level 20, P, e, Φ at level 200,
format "{{ P } } e {{ Φ } }") : stdpp_scope.
Global Notation "{{ P } } e {{ v , Q } }" := (hoare P%I e%E (λ v, Q)%I)
(at level 20, P, e, Q at level 200,
format "{{ P } } e {{ v , Q } }") : stdpp_scope.
(* Rules for Hoare triples *)
Lemma hoare_value v Φ:
{{ Φ v }} v {{ Φ }}.
Proof.
iIntros "H". by iApply wp_value.
Qed.
Lemma hoare_con P Q Φ Ψ e:
(P Q)
( v, Ψ v Φ v)
{{ Q }} e {{ Ψ }}
{{ P }} e {{ Φ }}.
Proof.
iIntros (Hpre Hpost Hhoare) "P".
iApply wp_mono; eauto.
iApply Hhoare. by iApply Hpre.
Qed.
Lemma hoare_bind K P Φ Ψ e:
{{ P }} e {{ Ψ }}
( v, {{ Ψ v }} fill K (Val v) {{ Φ }})
{{ P }} (fill K e) {{ Φ }}.
Proof.
iIntros (Hexpr Hctx) "P".
iApply wp_bind'.
iApply wp_mono; eauto.
by iApply Hexpr.
Qed.
Lemma hoare_pure P φ Φ e:
(P φ)
(φ {{ P }} e {{ Φ }})
{{ P }} e {{ Φ }}.
Proof.
intros Hent Hhoare.
iIntros "P". iPoseProof (Hent with "P") as "%".
by iApply Hhoare.
Qed.
Lemma hoare_exist_pre {X} (Φ : X _) Ψ e :
( x : X, {{ Φ x }} e {{ Ψ }})
{{ x : X, Φ x }} e {{ Ψ }}.
Proof.
iIntros (Hs) "(%x & Hx)". by iApply Hs.
Qed.
Lemma hoare_pure_step P Ψ e1 e2 :
pure_step e1 e2
{{ P }} e2 {{ Ψ }}
{{ P }} e1 {{ Ψ }}.
Proof.
iIntros (Hpure He2) "HP".
iApply (lifting.wp_pure_step_later _ _ _ _ _ True).
- intros _. econstructor 2; [done | econstructor].
- done.
- iNext. iApply He2. done.
Qed.
Lemma hoare_pure_steps P Ψ e1 e2 :
rtc pure_step e1 e2
{{ P }} e2 {{ Ψ }}
{{ P }} e1 {{ Ψ }}.
Proof.
induction 1; eauto using hoare_pure_step.
Qed.
(** Pure step rules *)
Lemma PureExec_1_equiv e1 e2 :
( ϕ, ϕ PureExec ϕ 1 e1 e2)
pure_step e1 e2.
Proof.
split.
- intros (ϕ & H & Hp). by specialize (Hp H) as ?%nsteps_once_inv.
- intros Hp. exists True. split; first done.
intros _. econstructor; first done. constructor.
Qed.
Lemma PureExec_1_elim (ϕ : Prop) e1 e2 :
ϕ PureExec ϕ 1 e1 e2
pure_step e1 e2.
Proof.
intros H Hp. apply PureExec_1_equiv. eauto.
Qed.
Lemma pure_step_fill K (e1 e2: expr):
pure_step e1 e2 pure_step (fill K e1) (fill K e2).
Proof.
intros Hp. apply PureExec_1_equiv.
exists True. split; first done.
apply pure_exec_ctx; first apply _.
intros _. apply nsteps_once. done.
Qed.
Lemma rtc_pure_step_fill K (e1 e2: expr):
rtc pure_step e1 e2 rtc pure_step (fill K e1) (fill K e2).
Proof.
induction 1; first reflexivity.
econstructor; last done. by eapply pure_step_fill.
Qed.
Lemma pure_step_add (n m : Z) :
pure_step (#n + #m) (#(n + m)).
Proof.
eapply PureExec_1_elim; last apply _. done.
Qed.
Lemma pure_step_sub (n m : Z) :
pure_step (#n - #m) (#(n - m)).
Proof.
eapply PureExec_1_elim; last apply _. done.
Qed.
Lemma pure_step_mul (n m : Z) :
pure_step (#n * #m) (#(n * m)).
Proof.
eapply PureExec_1_elim; last apply _. done.
Qed.
Lemma pure_step_quot (n m : Z) :
m 0 pure_step (#n `quot` #m) (#(Z.quot n m)).
Proof.
intros _. eapply PureExec_1_elim; last apply _. done.
Qed.
Lemma pure_step_eq (n m : Z) :
n = m pure_step (#n = #m) #true.
Proof.
intros. eapply PureExec_1_elim; last apply _.
unfold bin_op_eval. simpl.
rewrite bool_decide_eq_true_2; subst; done.
Qed.
Lemma pure_step_neq (n m : Z) :
n m pure_step (#n = #m) #false.
Proof.
intros. eapply PureExec_1_elim; last apply _.
unfold bin_op_eval. simpl.
rewrite bool_decide_eq_false_2; first done.
intros [= Heq]; done.
Qed.
Lemma pure_step_beta f x e v :
pure_step ((rec: f x := e)%V v) (subst' x v (subst' f (rec: f x := e) e)).
Proof.
eapply PureExec_1_elim; last apply _. done.
Qed.
Lemma pure_step_if_true e1 e2 :
pure_step (if: #true then e1 else e2) e1.
Proof.
eapply PureExec_1_elim; last apply _. done.
Qed.
Lemma pure_step_if_false e1 e2 :
pure_step (if: #false then e1 else e2) e2.
Proof.
eapply PureExec_1_elim; last apply _. done.
Qed.
Lemma pure_step_match_injl v x1 x2 e1 e2 :
pure_step (match: InjLV v with InjL x1 => e1 | InjR x2 => e2 end) ((λ: x1, e1) v).
Proof.
by eapply PureExec_1_elim; last apply _.
Qed.
Lemma pure_step_match_injr v x1 x2 e1 e2 :
pure_step (match: InjRV v with InjL x1 => e1 | InjR x2 => e2 end) ((λ: x2, e2) v).
Proof.
by eapply PureExec_1_elim; last apply _.
Qed.
Lemma pure_step_fst v1 v2 :
pure_step (Fst (v1, v2)%V) v1.
Proof.
by eapply PureExec_1_elim; last apply _.
Qed.
Lemma pure_step_snd v1 v2 :
pure_step (Snd (v1, v2)%V) v2.
Proof.
by eapply PureExec_1_elim; last apply _.
Qed.
(** In our Coq formalization, a bit of an additional effort arises due to the handling of values:
values are always represented using the [Val] constructor, and fully reduced expressions take an additional
step of computation to get to a [Val].
See for instance the constructors [PairS], [InjLS], [InjRS].
Therefore, we need an additional rule, [pure_step_val].
*)
Lemma pure_step_val_fun f x e :
rtc pure_step (rec: f x := e)%E (rec: f x := e)%V.
Proof.
eapply rtc_nsteps. exists 1. eapply pure_exec. done.
Qed.
Lemma pure_step_val_pair e1 e2 v1 v2 :
rtc pure_step e1 v1
rtc pure_step e2 v2
rtc pure_step (e1, e2)%E (v1, v2)%V.
Proof.
etransitivity; first etransitivity.
+ by eapply rtc_pure_step_fill with (K := [PairRCtx _]).
+ by eapply rtc_pure_step_fill with (K := [PairLCtx _]).
+ simpl. eapply rtc_nsteps. exists 1. eapply pure_exec. done.
Qed.
Lemma pure_step_val_injl e1 v1 :
rtc pure_step e1 v1
rtc pure_step (InjL e1) (InjLV v1).
Proof.
etransitivity.
+ by eapply rtc_pure_step_fill with (K := [InjLCtx]).
+ simpl. eapply rtc_nsteps. exists 1. eapply pure_exec. done.
Qed.
Lemma pure_step_val_injr e1 v1 :
rtc pure_step e1 v1
rtc pure_step (InjR e1) (InjRV v1).
Proof.
etransitivity.
+ by eapply rtc_pure_step_fill with (K := [InjRCtx]).
+ simpl. eapply rtc_nsteps. exists 1. eapply pure_exec. done.
Qed.
Local Hint Resolve pure_step_val_fun pure_step_val_pair pure_step_val_injl pure_step_val_injr : core.
(** For convenience: a lemma to cover all of these cases, with a precondition that
can be easily solved by eatuto *)
Inductive expr_is_val : expr val Prop :=
| expr_is_val_base v:
expr_is_val (Val v) v
| expr_is_val_fun f x e:
expr_is_val (rec: f x := e)%E (rec: f x := e)%V
| expr_is_val_pair e1 e2 v1 v2:
expr_is_val e1 v1
expr_is_val e2 v2
expr_is_val (e1, e2)%E (v1, v2)%V
| expr_is_val_inj_l e v:
expr_is_val e v
expr_is_val (InjL e)%E (InjLV v)%V
| expr_is_val_inj_r e v:
expr_is_val e v
expr_is_val (InjR e)%E (InjRV v)%V.
#[export]
Hint Constructors expr_is_val : core.
Lemma expr_is_val_of_val v :
expr_is_val (of_val v) v.
Proof.
destruct v; simpl; constructor.
Qed.
#[export]
Hint Resolve expr_is_val_of_val : core.
Lemma pure_step_val e v:
expr_is_val e v
rtc pure_step e v.
Proof.
intros Hexpr. induction Hexpr; eauto. econstructor.
Qed.
Lemma hoare_new v :
{{ True }} ref v {{ w, l : loc, w = #l l v }}.
Proof.
iIntros "_". wp_alloc l as "Hl". iApply wp_value. eauto with iFrame.
Qed.
Lemma hoare_load l v:
{{ l v }} ! #l {{ w, w = v l v }}.
Proof.
iIntros "Hl". wp_load. iApply wp_value. iFrame. done.
Qed.
Lemma hoare_store l (v w: val):
{{ l v }} #l <- w {{ _, l w }}.
Proof.
iIntros "Hl". wp_store. iApply wp_value. iFrame.
Qed.
Lemma hoare_frame P F Φ e:
{{ P }} e {{ Φ }}
{{ P F }} e {{ v, Φ v F }}.
Proof.
iIntros (Hhoare) "[P $]". by iApply Hhoare.
Qed.
(* Prevent printing of magic wands *)
Notation "P - Q" := (bi_entails P Q) (only parsing) : stdpp_scope.
(** Weakest precondition rules *)
Lemma ent_wp_value Φ v :
Φ v WP of_val v {{ w, Φ w }}.
Proof.
iIntros "Hv". by iApply wp_value.
Qed.
Lemma ent_wp_wand' Φ Ψ e :
( v, Φ v - Ψ v) - WP e {{ Φ }} - WP e {{ Ψ }}.
Proof.
iIntros "Hp Hwp". iApply (wp_wand with "Hwp Hp").
Qed.
Lemma ent_wp_wand Φ Ψ e :
( v, Φ v - Ψ v) WP e {{ Φ }} WP e {{ Ψ }}.
Proof.
iIntros "[Hp Hwp]". iApply (wp_wand with "Hwp Hp").
Qed.
Lemma ent_wp_bind e K Φ :
WP e {{ v, WP fill K (Val v) {{ Φ }} }} WP fill K e {{ Φ }}.
Proof.
iApply wp_bind.
Qed.
Lemma ent_wp_pure_step e e' Φ :
pure_step e e'
WP e' {{ Φ }} WP e {{ Φ }}.
Proof.
iIntros (Hpure) "Hwp". iApply (lifting.wp_pure_step_later _ _ _ _ _ True 1); last iApply "Hwp"; last done.
intros _. apply nsteps_once. apply Hpure.
Qed.
Lemma ent_wp_new v Φ :
( l : loc, l v - Φ #l) WP ref (Val v) {{ Φ }}.
Proof.
iIntros "Hs". wp_alloc l as "Hl". wp_value_head. by iApply "Hs".
Qed.
Lemma ent_wp_load l v Φ :
l v (l v - Φ v) WP !#l {{ Φ }}.
Proof.
iIntros "(Hl & Hp)". wp_load. wp_value_head. by iApply "Hp".
Qed.
Lemma ent_wp_store l v w Φ :
l w (l v - Φ #()) WP #l <- Val v {{ Φ }}.
Proof.
iIntros "(Hl & Hp)". wp_store. wp_value_head. by iApply "Hp".
Qed.
(** Persistency *)
Lemma ent_pers_dup P :
P ( P) ( P).
Proof.
iIntros "#HP". eauto.
Qed.
Lemma ent_pers_elim P :
P P.
Proof.
iIntros "#$".
Qed.
Lemma ent_pers_mono P Q :
(P Q)
P Q.
Proof.
iIntros (HPQ) "#HP !>". by iApply HPQ.
Qed.
Lemma ent_pers_pure (ϕ : Prop) :
ϕ ( ϕ : iProp).
Proof.
iIntros "#$".
Qed.
Lemma ent_pers_and_sep P Q :
( P) Q ( P) Q.
Proof.
iIntros "(#$ & $)".
Qed.
Lemma ent_pers_idemp P :
P P.
Proof.
iIntros "#$".
Qed.
Lemma ent_pers_all {X} (Φ : X iProp) :
( x : X, Φ x) x : X, Φ x.
Proof.
iIntros "#Hx" (x). iApply "Hx".
Qed.
Lemma ent_pers_exists {X} (Φ : X iProp) :
( x : X, Φ x) x : X, Φ x.
Proof.
iIntros "(%x & #Hx)". iExists x. done.
Qed.
(** Invariants *)
Implicit Type
(F : iProp)
(N : namespace)
(E : coPset)
.
Lemma ent_inv_pers F N :
inv N F inv N F.
Proof.
iIntros "#$".
Qed.
Lemma ent_inv_alloc F P N E e Φ :
(P inv N F WP e @ E {{ Φ }})
(P F WP e @ E {{ Φ }}).
Proof.
iIntros (Ha) "[HP HF]".
iMod (inv_alloc N with "HF") as "#Hinv".
iApply Ha. iFrame "Hinv ".
Qed.
Lemma inv_alloc N F E e Φ :
F -
(inv N F - WP e @ E {{ Φ }}) -
WP e @ E {{ Φ }}.
Proof.
iIntros "HF Hs".
iMod (inv_alloc N with "HF") as "#Hinv".
by iApply "Hs".
Qed.
(** We require a sidecondition here, namely that [F] is "timeless". All propositions we have seen up to now are in fact timeless.
We will see propositions that do not satisfy this requirement and which need a stronger rule for invariants soon.
*)
Lemma ent_inv_open `{!Timeless F} P N E e Φ :
(P F WP e @ (E N) {{ v, F Φ v }})
N E
(P inv N F WP e @ E {{ Φ }}).
Proof.
iIntros (Ha Hincl) "(HP & #Hinv)".
iMod (inv_acc_timeless with "Hinv") as "(HF & Hcl)"; first done.
iApply wp_fupd'. iApply wp_wand_r.
iSplitR "Hcl". { iApply Ha. iFrame. }
iIntros (v) "[HF Hphi]". iMod ("Hcl" with "HF"). done.
Qed.
Lemma inv_open `{!Timeless F} N E e Φ :
N E
inv N F -
(F - WP e @ (E N) {{ v, F Φ v }})%I -
WP e @ E {{ Φ }}.
Proof.
iIntros (Hincl) "#Hinv Hs".
iMod (inv_acc_timeless with "Hinv") as "(HF & Hcl)"; first done.
iApply wp_fupd'. iApply wp_wand_r.
iSplitR "Hcl". { iApply "Hs". iFrame. }
iIntros (v) "[HF Hphi]". iMod ("Hcl" with "HF"). done.
Qed.
(** Later *)
Lemma ent_later_intro P :
P P.
Proof.
iIntros "$".
Qed.
Lemma ent_later_mono P Q :
(P Q)
( P Q).
Proof.
iIntros (Hs) "HP!>". by iApply Hs.
Qed.
Lemma ent_löb P :
( P P)
True P.
Proof.
iIntros (Hs) "_".
iLöb as "IH". by iApply Hs.
Qed.
Lemma ent_later_sep P Q :
(P Q) ( P) ( Q).
Proof.
iSplit; iIntros "[$ $]".
Qed.
Lemma ent_later_exists `{Inhabited X} (Φ : X iProp) :
( ( x : X, Φ x)) x : X, Φ x.
Proof. apply bi.later_exist. Qed.
Lemma ent_later_all {X} (Φ : X iProp) :
( x : X, Φ x) x : X, Φ x.
Proof. apply bi.later_forall. Qed.
Lemma ent_later_pers P :
P P.
Proof.
iSplit; iIntros "#H !> !>"; done.
Qed.
Lemma ent_later_wp_pure_step e e' Φ :
pure_step e e'
WP e' {{ Φ }} WP e {{ Φ }}.
Proof.
iIntros (Hpure%PureExec_1_equiv) "Hwp".
destruct Hpure as (ϕ & Hphi & Hpure).
iApply (lifting.wp_pure_step_later _ _ _ _ _ _ 1); done.
Qed.
Lemma ent_later_wp_new v Φ :
( l : loc, l v - Φ #l) WP ref v {{ Φ }}.
Proof.
iIntros "Hp". wp_alloc l as "Hl". iApply wp_value. by iApply "Hp".
Qed.
Lemma ent_later_wp_load l v Φ :
l v (l v - Φ v) WP ! #l {{ Φ }}.
Proof.
iIntros "[Hl Hp]". wp_load. iApply wp_value. by iApply "Hp".
Qed.
Lemma ent_later_wp_store l v w Φ :
l v (l w - Φ #()) WP #l <- w {{ Φ }}.
Proof.
iIntros "[Hl Hp]". wp_store. iApply wp_value. by iApply "Hp".
Qed.
End hoare.
Module impred_invariants.
Import hoare.
Implicit Type
(F : iProp)
(N : namespace)
(E : coPset)
.
Lemma ent_inv_open P F N E e Φ :
(P F WP e @ (E N) {{ v, F Φ v }})
N E
(P inv N F WP e @ E {{ Φ }}).
Proof.
iIntros (Ha Hincl) "(HP & #Hinv)".
iMod (inv_acc with "Hinv") as "(HF & Hcl)"; first done.
iApply wp_fupd'. iApply wp_wand_r.
iSplitR "Hcl". { iApply Ha. iFrame. }
iIntros (v) "[HF Hphi]". iMod ("Hcl" with "HF"). done.
Qed.
Lemma inv_open N E F e Φ :
N E
inv N F -
( F - WP e @ (E N) {{ v, F Φ v }})%I -
WP e @ E {{ Φ }}.
Proof.
iIntros (Hincl) "#Hinv Hs".
iMod (inv_acc with "Hinv") as "(HF & Hcl)"; first done.
iApply wp_fupd'. iApply wp_wand_r.
iSplitR "Hcl". { iApply "Hs". iFrame. }
iIntros (v) "[HF Hphi]". iMod ("Hcl" with "HF"). done.
Qed.
End impred_invariants.