release exercise 8

amethyst
Benjamin Peters 5 months ago
parent 1594ef0bd2
commit 6cc382131f

@ -1,4 +1,5 @@
-Q theories/lib semantics.lib
-Q theories/program_logics semantics.pl
-Q theories/type_systems semantics.ts
# We sometimes want to locally override notation, and there is no good way to do that with scopes.
-arg -w -arg -notation-overridden
@ -76,6 +77,22 @@ theories/type_systems/systemf_mu_state/parallel_subst.v
theories/type_systems/systemf_mu_state/logrel.v
theories/type_systems/systemf_mu_state/logrel_sol.v
# Program logic library
theories/program_logics/program_logic/notation.v
theories/program_logics/program_logic/sequential_wp.v
theories/program_logics/program_logic/lifting.v
theories/program_logics/program_logic/ectx_lifting.v
theories/program_logics/program_logic/adequacy.v
theories/program_logics/heap_lang/primitive_laws.v
theories/program_logics/heap_lang/derived_laws.v
theories/program_logics/heap_lang/proofmode.v
theories/program_logics/heap_lang/adequacy.v
theories/program_logics/heap_lang/primitive_laws_nolater.v
# Program logic chapter
theories/program_logics/hoare_lib.v
theories/program_logics/hoare.v
# By removing the # below, you can add the exercise sheets to make
#theories/type_systems/warmup/warmup.v
#theories/type_systems/warmup/warmup_sol.v

@ -0,0 +1,29 @@
From iris.algebra Require Import auth.
From iris.proofmode Require Import proofmode.
From semantics.pl.program_logic Require Export sequential_wp adequacy.
From iris.heap_lang Require Import notation.
From semantics.pl.heap_lang Require Export proofmode.
From iris.prelude Require Import options.
Class heapGpreS Σ := HeapGpreS {
heapGpreS_iris : invGpreS Σ;
heapGpreS_heap : gen_heapGpreS loc (option val) Σ;
}.
#[export] Existing Instance heapGpreS_iris.
#[export] Existing Instance heapGpreS_heap.
Definition heapΣ : gFunctors :=
#[invΣ; gen_heapΣ loc (option val)].
Global Instance subG_heapGpreS {Σ} : subG heapΣ Σ heapGpreS Σ.
Proof. solve_inG. Qed.
Definition heap_adequacy Σ `{!heapGpreS Σ} s e σ φ :
( `{!heapGS Σ}, WP e @ s; {{ v, φ v }})
adequate s e σ (λ v _, φ v).
Proof.
intros Hwp; eapply (wp_adequacy _ _); iIntros (?).
iMod (gen_heap_init σ.(heap)) as (?) "[Hh _]".
iModIntro. iExists
(λ σ, (gen_heap_interp σ.(heap))%I).
iFrame. iApply (Hwp (HeapGS _ _ _)).
Qed.

@ -0,0 +1,172 @@
(** This file extends the HeapLang program logic with some derived laws (not
using the lifting lemmas) about arrays and prophecies.
For utility functions on arrays (e.g., freeing/copying an array), see
[heap_lang.lib.array]. *)
From stdpp Require Import fin_maps.
From iris.bi Require Import lib.fractional.
From iris.proofmode Require Import proofmode.
From iris.heap_lang Require Import tactics notation.
From semantics.pl.heap_lang Require Export primitive_laws.
From iris.prelude Require Import options.
(** The [array] connective is a version of [mapsto] that works
with lists of values. *)
Definition array `{!heapGS Σ} (l : loc) (dq : dfrac) (vs : list val) : iProp Σ :=
[ list] i v vs, (l + i) {dq} v.
(** FIXME: Refactor these notations using custom entries once Coq bug #13654
has been fixed. *)
Notation "l ↦∗{ dq } vs" := (array l dq vs)
(at level 20, format "l ↦∗{ dq } vs") : bi_scope.
Notation "l ↦∗□ vs" := (array l DfracDiscarded vs)
(at level 20, format "l ↦∗□ vs") : bi_scope.
Notation "l ↦∗{# q } vs" := (array l (DfracOwn q) vs)
(at level 20, format "l ↦∗{# q } vs") : bi_scope.
Notation "l ↦∗ vs" := (array l (DfracOwn 1) vs)
(at level 20, format "l ↦∗ vs") : bi_scope.
(** We have [FromSep] and [IntoSep] instances to split the fraction (via the
[AsFractional] instance below), but not for splitting the list, as that would
lead to overlapping instances. *)
Section lifting.
Context `{!heapGS Σ}.
Implicit Types P Q : iProp Σ.
Implicit Types Φ : val iProp Σ.
Implicit Types σ : state.
Implicit Types v : val.
Implicit Types vs : list val.
Implicit Types l : loc.
Implicit Types sz off : nat.
Global Instance array_timeless l q vs : Timeless (array l q vs) := _.
Global Instance array_fractional l vs : Fractional (λ q, l {#q} vs)%I := _.
Global Instance array_as_fractional l q vs :
AsFractional (l {#q} vs) (λ q, l {#q} vs)%I q.
Proof. split; done || apply _. Qed.
Lemma array_nil l dq : l {dq} [] emp.
Proof. by rewrite /array. Qed.
Lemma array_singleton l dq v : l {dq} [v] l {dq} v.
Proof. by rewrite /array /= right_id Loc.add_0. Qed.
Lemma array_app l dq vs ws :
l {dq} (vs ++ ws) l {dq} vs (l + length vs) {dq} ws.
Proof.
rewrite /array big_sepL_app.
setoid_rewrite Nat2Z.inj_add.
by setoid_rewrite Loc.add_assoc.
Qed.
Lemma array_cons l dq v vs : l {dq} (v :: vs) l {dq} v (l + 1) {dq} vs.
Proof.
rewrite /array big_sepL_cons Loc.add_0.
setoid_rewrite Loc.add_assoc.
setoid_rewrite Nat2Z.inj_succ.
by setoid_rewrite Z.add_1_l.
Qed.
Global Instance array_cons_frame l dq v vs R Q :
Frame false R (l {dq} v (l + 1) {dq} vs) Q
Frame false R (l {dq} (v :: vs)) Q | 2.
Proof. by rewrite /Frame array_cons. Qed.
Lemma update_array l dq vs off v :
vs !! off = Some v
l {dq} vs - ((l + off) {dq} v v', (l + off) {dq} v' - l {dq} <[off:=v']>vs).
Proof.
iIntros (Hlookup) "Hl".
rewrite -[X in (l {_} X)%I](take_drop_middle _ off v); last done.
iDestruct (array_app with "Hl") as "[Hl1 Hl]".
iDestruct (array_cons with "Hl") as "[Hl2 Hl3]".
assert (off < length vs) as H by (apply lookup_lt_is_Some; by eexists).
rewrite take_length min_l; last by lia. iFrame "Hl2".
iIntros (w) "Hl2".
clear Hlookup. assert (<[off:=w]> vs !! off = Some w) as Hlookup.
{ apply list_lookup_insert. lia. }
rewrite -[in (l {_} <[off:=w]> vs)%I](take_drop_middle (<[off:=w]> vs) off w Hlookup).
iApply array_app. rewrite take_insert; last by lia. iFrame.
iApply array_cons. rewrite take_length min_l; last by lia. iFrame.
rewrite drop_insert_gt; last by lia. done.
Qed.
(** * Rules for allocation *)
Lemma mapsto_seq_array l dq v n :
([ list] i seq 0 n, (l + (i : nat)) {dq} v) -
l {dq} replicate n v.
Proof.
rewrite /array. iInduction n as [|n'] "IH" forall (l); simpl.
{ done. }
iIntros "[$ Hl]". rewrite -fmap_S_seq big_sepL_fmap.
setoid_rewrite Nat2Z.inj_succ. setoid_rewrite <-Z.add_1_l.
setoid_rewrite <-Loc.add_assoc. iApply "IH". done.
Qed.
Lemma wp_allocN s E v n Φ :
(0 < n)%Z
( l, l replicate (Z.to_nat n) v - Φ (LitV $ LitLoc l)) -
WP AllocN (Val $ LitV $ LitInt $ n) (Val v) @ s; E; E {{ Φ }}.
Proof.
iIntros (Hzs) "". iApply wp_allocN_seq; [done..|].
iNext. iIntros (l) "Hlm". iApply "".
by iApply mapsto_seq_array.
Qed.
Lemma wp_allocN_vec s E v n Φ :
(0 < n)%Z
( l, l vreplicate (Z.to_nat n) v - Φ (#l)) -
WP AllocN #n v @ s ; E; E {{ Φ }}.
Proof.
iIntros (Hzs) "". iApply wp_allocN; [ lia | .. ].
iNext. iIntros (l) "Hl". iApply "". rewrite vec_to_list_replicate. iFrame.
Qed.
(** * Rules for accessing array elements *)
Lemma wp_load_offset s E l dq off vs v Φ :
vs !! off = Some v
l {dq} vs -
(l {dq} vs - Φ v) -
WP ! #(l + off) @ s; E; E {{ Φ }}.
Proof.
iIntros (Hlookup) "Hl HΦ".
iDestruct (update_array l _ _ _ _ Hlookup with "Hl") as "[Hl1 Hl2]".
iApply (wp_load with "Hl1"). iIntros "!> Hl1". iApply "".
iDestruct ("Hl2" $! v) as "Hl2". rewrite list_insert_id; last done.
iApply "Hl2". iApply "Hl1".
Qed.
Lemma wp_load_offset_vec s E l dq sz (off : fin sz) (vs : vec val sz) Φ :
l {dq} vs -
(l {dq} vs - Φ (vs !!! off)) -
WP ! #(l + off) @ s; E; E {{ Φ }}.
Proof. apply wp_load_offset. by apply vlookup_lookup. Qed.
Lemma wp_store_offset s E l off vs v Φ :
is_Some (vs !! off)
l vs -
(l <[off:=v]> vs - Φ #()) -
WP #(l + off) <- v @ s; E; E {{ Φ }}.
Proof.
iIntros ([w Hlookup]) "Hl HΦ".
iDestruct (update_array l _ _ _ _ Hlookup with "Hl") as "[Hl1 Hl2]".
iApply (wp_store with "Hl1"). iIntros "!> Hl1".
iApply "". iApply "Hl2". iApply "Hl1".
Qed.
Lemma wp_store_offset_vec s E l sz (off : fin sz) (vs : vec val sz) v Φ :
l vs -
(l vinsert off v vs - Φ #()) -
WP #(l + off) <- v @ s; E; E {{ Φ }}.
Proof.
setoid_rewrite vec_to_list_insert. apply wp_store_offset.
eexists. by apply vlookup_lookup.
Qed.
End lifting.
#[global] Typeclasses Opaque array.

@ -0,0 +1,177 @@
(** This file proves the basic laws of the HeapLang program logic by applying
the Iris lifting lemmas. *)
From iris.proofmode Require Import proofmode.
From iris.bi.lib Require Import fractional.
From iris.base_logic.lib Require Export gen_heap.
From semantics.pl.program_logic Require Export sequential_wp.
From semantics.pl.program_logic Require Import ectx_lifting.
From iris.heap_lang Require Export class_instances.
From iris.heap_lang Require Import tactics notation.
From semantics.pl.program_logic Require Export notation.
From iris.prelude Require Import options.
Class heapGS Σ := HeapGS {
heapGS_invGS : invGS_gen HasNoLc Σ;
heapGS_gen_heapGS : gen_heapGS loc (option val) Σ;
}.
#[export] Existing Instance heapGS_gen_heapGS.
Global Instance heapGS_irisGS `{!heapGS Σ} : irisGS heap_lang Σ := {
iris_invGS := heapGS_invGS;
state_interp σ := (gen_heap_interp σ.(heap))%I;
}.
(** Since we use an [option val] instance of [gen_heap], we need to overwrite
the notations. That also helps for scopes and coercions. *)
(** FIXME: Refactor these notations using custom entries once Coq bug #13654
has been fixed. *)
Notation "l ↦{ dq } v" := (mapsto (L:=loc) (V:=option val) l dq (Some v%V))
(at level 20, format "l ↦{ dq } v") : bi_scope.
Notation "l ↦□ v" := (mapsto (L:=loc) (V:=option val) l DfracDiscarded (Some v%V))
(at level 20, format "l ↦□ v") : bi_scope.
Notation "l ↦{# q } v" := (mapsto (L:=loc) (V:=option val) l (DfracOwn q) (Some v%V))
(at level 20, format "l ↦{# q } v") : bi_scope.
Notation "l ↦ v" := (mapsto (L:=loc) (V:=option val) l (DfracOwn 1) (Some v%V))
(at level 20, format "l ↦ v") : bi_scope.
Section lifting.
Context `{!heapGS Σ}.
Implicit Types P Q : iProp Σ.
Implicit Types Φ Ψ : val iProp Σ.
Implicit Types efs : list expr.
Implicit Types σ : state.
Implicit Types v : val.
Implicit Types l : loc.
(** Recursive functions: we do not use this lemmas as it is easier to use Löb
induction directly, but this demonstrates that we can state the expected
reasoning principle for recursive functions, without any visible . *)
Lemma wp_rec_löb s E1 E2 f x e Φ Ψ :
( ( v, Ψ v - WP (rec: f x := e)%V v @ s; E1; E2 {{ Φ }}) -
v, Ψ v - WP (subst' x v (subst' f (rec: f x := e) e)) @ s; E1; E2 {{ Φ }}) -
v, Ψ v - WP (rec: f x := e)%V v @ s; E1; E2 {{ Φ }}.
Proof.
iIntros "#Hrec". iLöb as "IH". iIntros (v) "".
iApply lifting.wp_pure_step_later; first done.
iNext. iApply ("Hrec" with "[] HΨ"). iIntros "!>" (w) "".
iApply ("IH" with "").
Qed.
(** Heap *)
(** We need to adjust the [gen_heap] and [gen_inv_heap] lemmas because of our
value type being [option val]. *)
Lemma mapsto_valid l dq v : l {dq} v - dq.
Proof. apply mapsto_valid. Qed.
Lemma mapsto_valid_2 l dq1 dq2 v1 v2 :
l {dq1} v1 - l {dq2} v2 - (dq1 dq2) v1 = v2.
Proof.
iIntros "H1 H2".
iDestruct (mapsto_valid_2 with "H1 H2") as %[? [=]]. done.
Qed.
Lemma mapsto_agree l dq1 dq2 v1 v2 : l {dq1} v1 - l {dq2} v2 - v1 = v2.
Proof. iIntros "H1 H2". iDestruct (mapsto_agree with "H1 H2") as %[=]. done. Qed.
Lemma mapsto_combine l dq1 dq2 v1 v2 :
l {dq1} v1 - l {dq2} v2 - l {dq1 dq2} v1 v1 = v2.
Proof.
iIntros "Hl1 Hl2". iDestruct (mapsto_combine with "Hl1 Hl2") as "[$ Heq]".
by iDestruct "Heq" as %[= ->].
Qed.
Lemma mapsto_frac_ne l1 l2 dq1 dq2 v1 v2 :
¬ (dq1 dq2) l1 {dq1} v1 - l2 {dq2} v2 - l1 l2.
Proof. apply mapsto_frac_ne. Qed.
Lemma mapsto_ne l1 l2 dq2 v1 v2 : l1 v1 - l2 {dq2} v2 - l1 l2.
Proof. apply mapsto_ne. Qed.
Lemma mapsto_persist l dq v : l {dq} v == l v.
Proof. apply mapsto_persist. Qed.
Lemma heap_array_to_seq_mapsto l v (n : nat) :
([ map] l' ov heap_array l (replicate n v), gen_heap.mapsto l' (DfracOwn 1) ov) -
[ list] i seq 0 n, (l + (i : nat)) v.
Proof.
iIntros "Hvs". iInduction n as [|n] "IH" forall (l); simpl.
{ done. }
rewrite big_opM_union; last first.
{ apply map_disjoint_spec=> l' v1 v2 /lookup_singleton_Some [-> _].
intros (j&w&?&Hjl&_)%heap_array_lookup.
rewrite Loc.add_assoc -{1}[l']Loc.add_0 in Hjl. simplify_eq; lia. }
rewrite Loc.add_0 -fmap_S_seq big_sepL_fmap.
setoid_rewrite Nat2Z.inj_succ. setoid_rewrite <-Z.add_1_l.
setoid_rewrite <-Loc.add_assoc.
rewrite big_opM_singleton; iDestruct "Hvs" as "[$ Hvs]". by iApply "IH".
Qed.
Lemma wp_allocN_seq s E v n Φ :
(0 < n)%Z
( l, ([ list] i seq 0 (Z.to_nat n), (l + (i : nat)) v) - Φ (LitV $ LitLoc l)) -
WP AllocN (Val $ LitV $ LitInt $ n) (Val v) @ s; E; E {{ Φ }}.
Proof.
iIntros (Hn) "".
iApply wp_lift_head_step_fupd_nomask; first done.
iIntros (σ1) "Hσ !>"; iSplit; first by destruct n; auto with lia head_step.
iIntros (e2 σ2 κ efs Hstep); inv_head_step.
iMod (gen_heap_alloc_big _ (heap_array _ (replicate (Z.to_nat n) v)) with "Hσ")
as "(Hσ & Hl & Hm)".
{ apply heap_array_map_disjoint.
rewrite replicate_length Z2Nat.id; auto with lia. }
iApply step_fupd_intro; first done.
iModIntro. iFrame "Hσ". do 2 (iSplit; first done).
iApply wp_value_fupd. iApply "".
by iApply heap_array_to_seq_mapsto.
Qed.
Lemma wp_alloc s E v Φ :
( l, l v - Φ (LitV $ LitLoc l)) -
WP Alloc (Val v) @ s; E; E {{ Φ }}.
Proof.
iIntros "". iApply wp_allocN_seq; [auto with lia..|].
iIntros "!>" (l) "/= (? & _)". rewrite Loc.add_0. iApply ""; iFrame.
Qed.
Lemma wp_free s E l v Φ :
l v -
( Φ (LitV LitUnit)) -
WP Free (Val $ LitV $ LitLoc l) @ s; E; E {{ Φ }}.
Proof.
iIntros ">Hl HΦ". iApply wp_lift_head_step_fupd_nomask; first done.
iIntros (σ1) "Hσ !>". iDestruct (gen_heap_valid with "Hσ Hl") as %?.
iSplit; first by eauto with head_step.
iIntros (e2 σ2 κ efs Hstep); inv_head_step.
iMod (gen_heap_update with "Hσ Hl") as "[$ Hl]".
iIntros "!>!>!>". iSplit; first done. iSplit; first done.
iApply wp_value. by iApply "".
Qed.
Lemma wp_load s E l dq v Φ :
l {dq} v -
(l {dq} v - Φ v) -
WP Load (Val $ LitV $ LitLoc l) @ s; E; E {{ Φ }}.
Proof.
iIntros ">Hl HΦ". iApply wp_lift_head_step_fupd_nomask; first done.
iIntros (σ1) "Hσ !>". iDestruct (gen_heap_valid with "Hσ Hl") as %?.
iSplit; first by eauto with head_step.
iIntros (e2 σ2 κ efs Hstep); inv_head_step.
iApply step_fupd_intro; first done. iNext. iFrame.
iSplitR; first done. iSplitR; first done. iApply wp_value. by iApply "".
Qed.
Lemma wp_store s E l v' v Φ :
l v' -
(l v - Φ (LitV LitUnit)) -
WP Store (Val $ LitV $ LitLoc l) (Val v) @ s; E; E {{ Φ }}.
Proof.
iIntros ">Hl HΦ". iApply wp_lift_head_step_fupd_nomask; first done.
iIntros (σ1) "Hσ !>". iDestruct (gen_heap_valid with "Hσ Hl") as %?.
iSplit; first by eauto with head_step.
iIntros (e2 σ2 κ efs Hstep); inv_head_step.
iMod (gen_heap_update with "Hσ Hl") as "[$ Hl]".
iApply step_fupd_intro; first done. iNext.
iSplit; first done. iSplit; first done.
iApply wp_value. by iApply "".
Qed.
End lifting.

@ -0,0 +1,112 @@
From stdpp Require Import fin_maps.
From iris.proofmode Require Import proofmode.
From iris.bi.lib Require Import fractional.
From semantics.pl.heap_lang Require Export primitive_laws derived_laws.
From iris.base_logic.lib Require Export gen_heap gen_inv_heap.
From semantics.pl.program_logic Require Export sequential_wp.
From semantics.pl.program_logic Require Import ectx_lifting.
From iris.heap_lang Require Export class_instances.
From iris.heap_lang Require Import tactics notation.
From iris.prelude Require Import options.
Section lifting.
Context `{!heapGS Σ}.
Implicit Types P Q : iProp Σ.
Implicit Types Φ Ψ : val iProp Σ.
Implicit Types efs : list expr.
Implicit Types σ : state.
Implicit Types v : val.
Implicit Types l : loc.
(** Heap *)
Lemma wp_allocN_seq s E v n Φ :
(0 < n)%Z
( l, ([ list] i seq 0 (Z.to_nat n), (l + (i : nat)) v) - Φ (LitV $ LitLoc l)) -
WP AllocN (Val $ LitV $ LitInt $ n) (Val v) @ s; E; E {{ Φ }}.
Proof.
iIntros (Hn) "". iApply wp_allocN_seq; done.
Qed.
Lemma wp_alloc s E v Φ :
( l, l v - Φ (LitV $ LitLoc l)) -
WP Alloc (Val v) @ s; E; E {{ Φ }}.
Proof.
iIntros "". by iApply wp_alloc.
Qed.
Lemma wp_free s E l v Φ :
l v -
(Φ (LitV LitUnit)) -
WP Free (Val $ LitV $ LitLoc l) @ s; E; E {{ Φ }}.
Proof.
iIntros "Hl HΦ". iApply (wp_free with "Hl HΦ").
Qed.
Lemma wp_load s E l dq v Φ :
l {dq} v -
(l {dq} v - Φ v) -
WP Load (Val $ LitV $ LitLoc l) @ s; E; E {{ Φ }}.
Proof.
iIntros "Hl HΦ". iApply (wp_load with "Hl HΦ").
Qed.
Lemma wp_store s E l v' v Φ :
l v' -
(l v - Φ (LitV LitUnit)) -
WP Store (Val $ LitV $ LitLoc l) (Val v) @ s; E; E {{ Φ }}.
Proof.
iIntros "Hl HΦ".
iApply (wp_store with "Hl HΦ").
Qed.
(*** Derived *)
Lemma wp_allocN s E v n Φ :
(0 < n)%Z
( l, l replicate (Z.to_nat n) v - Φ (LitV $ LitLoc l)) -
WP AllocN (Val $ LitV $ LitInt $ n) (Val v) @ s; E; E {{ Φ }}.
Proof.
iIntros. by iApply wp_allocN.
Qed.
Lemma wp_allocN_vec s E v n Φ :
(0 < n)%Z
( l, l vreplicate (Z.to_nat n) v - Φ (#l)) -
WP AllocN #n v @ s ; E; E {{ Φ }}.
Proof.
iIntros. by iApply wp_allocN_vec.
Qed.
(** * Rules for accessing array elements *)
Lemma wp_load_offset s E l dq (off : nat) vs v Φ :
vs !! off = Some v
l {dq} vs -
(l {dq} vs - Φ v) -
WP ! #(l + off) @ s; E; E {{ Φ }}.
Proof.
iIntros (?) "Hl HΦ". by iApply (wp_load_offset with "Hl HΦ").
Qed.
Lemma wp_load_offset_vec s E l dq sz (off : fin sz) (vs : vec val sz) Φ :
l {dq} vs -
(l {dq} vs - Φ (vs !!! off)) -
WP ! #(l + off) @ s; E; E {{ Φ }}.
Proof. apply wp_load_offset. by apply vlookup_lookup. Qed.
Lemma wp_store_offset s E l (off : nat) vs v Φ :
is_Some (vs !! off)
l vs -
(l <[off:=v]> vs - Φ #()) -
WP #(l + off) <- v @ s; E; E {{ Φ }}.
Proof.
iIntros (?) "Hl HΦ". by iApply (wp_store_offset with "Hl HΦ").
Qed.
Lemma wp_store_offset_vec s E l sz (off : fin sz) (vs : vec val sz) v Φ :
l vs -
(l vinsert off v vs - Φ #()) -
WP #(l + off) <- v @ s; E; E {{ Φ }}.
Proof.
iIntros "Hl HΦ". by iApply (wp_store_offset_vec with "Hl HΦ").
Qed.
End lifting.

@ -0,0 +1,387 @@
From iris.proofmode Require Import coq_tactics reduction spec_patterns.
From iris.proofmode Require Export tactics.
From iris.heap_lang Require Export tactics.
From iris.heap_lang Require Import notation.
From semantics.pl.heap_lang Require Export derived_laws.
From semantics.pl.program_logic Require Export notation.
From iris.prelude Require Import options.
Import uPred.
Lemma tac_wp_expr_eval `{!heapGS Σ} Δ s E1 E2 Φ e e' :
( (e'':=e'), e = e'')
envs_entails Δ (WP e' @ s; E1; E2 {{ Φ }}) envs_entails Δ (WP e @ s; E1; E2 {{ Φ }}).
Proof. by intros ->. Qed.
Tactic Notation "wp_expr_eval" tactic3(t) :=
iStartProof;
lazymatch goal with
| |- envs_entails _ (wp ?s ?E1 ?E2 ?e ?Q) =>
notypeclasses refine (tac_wp_expr_eval _ _ _ _ _ e _ _ _);
[let x := fresh in intros x; t; unfold x; notypeclasses refine eq_refl|]
| _ => fail "wp_expr_eval: not a 'wp'"
end.
Ltac wp_expr_simpl := wp_expr_eval simpl.
Lemma tac_wp_pure `{!heapGS Σ} Δ Δ' s E1 E2 K e1 e2 φ n Φ :
PureExec φ n e1 e2
φ
MaybeIntoLaterNEnvs n Δ Δ'
envs_entails Δ' (WP (fill K e2) @ s; E1; E2 {{ Φ }})
envs_entails Δ (WP (fill K e1) @ s; E1; E2 {{ Φ }}).
Proof.
rewrite envs_entails_unseal=> ??? HΔ'. rewrite into_laterN_env_sound /=.
(* We want [pure_exec_fill] to be available to TC search locally. *)
pose proof @pure_exec_fill.
rewrite HΔ' -lifting.wp_pure_step_later //.
Qed.
Lemma tac_wp_value_nofupd `{!heapGS Σ} Δ s E Φ v :
envs_entails Δ (Φ v) envs_entails Δ (WP (Val v) @ s; E; E {{ Φ }}).
Proof. rewrite envs_entails_unseal=> ->. by apply wp_value. Qed.
(** Simplify the goal if it is [WP] of a value.
If the postcondition already allows a fupd, do not add a second one.
But otherwise, *do* add a fupd. This ensures that all the lemmas applied
here are bidirectional, so we never will make a goal unprovable. *)
Ltac wp_value_head :=
lazymatch goal with
| |- envs_entails _ (wp ?s ?E1 ?E2 (Val _) _) =>
eapply tac_wp_value_nofupd
end.
Ltac wp_finish :=
wp_expr_simpl; (* simplify occurences of subst/fill *)
pm_prettify. (* prettify ▷s caused by [MaybeIntoLaterNEnvs] and
λs caused by wp_value *)
Ltac solve_vals_compare_safe :=
(* The first branch is for when we have [vals_compare_safe] in the context.
The other two branches are for when either one of the branches reduces to
[True] or we have it in the context. *)
fast_done || (left; fast_done) || (right; fast_done).
(** The argument [efoc] can be used to specify the construct that should be
reduced. For example, you can write [wp_pure (EIf _ _ _)], which will search
for an [EIf _ _ _] in the expression, and reduce it.
The use of [open_constr] in this tactic is essential. It will convert all holes
(i.e. [_]s) into evars, that later get unified when an occurences is found
(see [unify e' efoc] in the code below). *)
Tactic Notation "wp_pure" open_constr(efoc) :=
iStartProof;
lazymatch goal with
| |- envs_entails _ (wp ?s ?E1 ?E2 ?e ?Q) =>
let e := eval simpl in e in
reshape_expr e ltac:(fun K e' =>
unify e' efoc;
eapply (tac_wp_pure _ _ _ _ _ K e');
[tc_solve (* PureExec *)
|try solve_vals_compare_safe (* The pure condition for PureExec --
handles trivial goals, including [vals_compare_safe] *)
|tc_solve (* IntoLaters *)
|wp_finish (* new goal *)
])
|| fail "wp_pure: cannot find" efoc "in" e "or" efoc "is not a redex"
| _ => fail "wp_pure: not a 'wp'"
end.
Ltac wp_pures :=
iStartProof;
first [ (* The `;[]` makes sure that no side-condition magically spawns. *)
progress repeat (wp_pure _; [])
| wp_finish (* In case wp_pure never ran, make sure we do the usual cleanup. *)
].
(** Unlike [wp_pures], the tactics [wp_rec] and [wp_lam] should also reduce
lambdas/recs that are hidden behind a definition, i.e. they should use
[AsRecV_recv] as a proper instance instead of a [Hint Extern].
We achieve this by putting [AsRecV_recv] in the current environment so that it
can be used as an instance by the typeclass resolution system. We then perform
the reduction, and finally we clear this new hypothesis. *)
Tactic Notation "wp_rec" :=
let H := fresh in
assert (H := AsRecV_recv);
wp_pure (App _ _);
clear H.
Tactic Notation "wp_if" := wp_pure (If _ _ _).
Tactic Notation "wp_if_true" := wp_pure (If (LitV (LitBool true)) _ _).
Tactic Notation "wp_if_false" := wp_pure (If (LitV (LitBool false)) _ _).
Tactic Notation "wp_unop" := wp_pure (UnOp _ _).
Tactic Notation "wp_binop" := wp_pure (BinOp _ _ _).
Tactic Notation "wp_op" := wp_unop || wp_binop.
Tactic Notation "wp_lam" := wp_rec.
Tactic Notation "wp_let" := wp_pure (Rec BAnon (BNamed _) _); wp_lam.
Tactic Notation "wp_seq" := wp_pure (Rec BAnon BAnon _); wp_lam.
Tactic Notation "wp_proj" := wp_pure (Fst _) || wp_pure (Snd _).
Tactic Notation "wp_case" := wp_pure (Case _ _ _).
Tactic Notation "wp_match" := wp_case; wp_pure (Rec _ _ _); wp_lam.
Tactic Notation "wp_inj" := wp_pure (InjL _) || wp_pure (InjR _).
Tactic Notation "wp_pair" := wp_pure (Pair _ _).
Tactic Notation "wp_closure" := wp_pure (Rec _ _ _).
(* will spawn an evar for [E2] *)
Lemma tac_wp_bind `{!heapGS Σ} K Δ s E1 E2 E3 Φ e f :
f = (λ e, fill K e) (* as an eta expanded hypothesis so that we can `simpl` it *)
envs_entails Δ (WP e @ s; E1; E2 {{ v, WP f (Val v) @ s; E2; E3 {{ Φ }} }})%I
envs_entails Δ (WP fill K e @ s; E1; E3 {{ Φ }}).
Proof. rewrite envs_entails_unseal=> -> ->. by apply: wp_bind. Qed.
(* don't change masks for bound expression *)
Lemma tac_wp_bind_nomask `{!heapGS Σ} K Δ s E1 E2 Φ e f :
f = (λ e, fill K e) (* as an eta expanded hypothesis so that we can `simpl` it *)
envs_entails Δ (WP e @ s; E1; E1 {{ v, WP f (Val v) @ s; E1; E2 {{ Φ }} }})%I
envs_entails Δ (WP fill K e @ s; E1; E2 {{ Φ }}).
Proof. rewrite envs_entails_unseal=> -> ->. by apply: wp_bind. Qed.
Ltac wp_bind_core K :=
lazymatch eval hnf in K with
| [] => idtac
| _ => eapply (tac_wp_bind_nomask K); [simpl; reflexivity|reduction.pm_prettify]
end.
Tactic Notation "wp_bind" open_constr(efoc) :=
iStartProof;
lazymatch goal with
| |- envs_entails _ (wp ?s ?E1 ?E2 ?e ?Q) =>
first [ reshape_expr e ltac:(fun K e' => unify e' efoc; wp_bind_core K)
| fail 1 "wp_bind: cannot find" efoc "in" e ]
| _ => fail "wp_bind: not a 'wp'"
end.
Ltac wp_bind_core' K :=
lazymatch eval hnf in K with
| [] => idtac
| _ => eapply (tac_wp_bind K); [simpl; reflexivity|reduction.pm_prettify]
end.
Tactic Notation "wp_bind'" open_constr(efoc) :=
iStartProof;
lazymatch goal with
| |- envs_entails _ (wp ?s ?E1 ?E2 ?e ?Q) =>
first [ reshape_expr e ltac:(fun K e' => unify e' efoc; wp_bind_core' K)
| fail 1 "wp_bind: cannot find" efoc "in" e ]
| _ => fail "wp_bind: not a 'wp'"
end.
(** Heap tactics *)
Section heap.
Context `{!heapGS Σ}.
Implicit Types P Q : iProp Σ.
Implicit Types Φ : val iProp Σ.
Implicit Types Δ : envs (uPredI (iResUR Σ)).
Implicit Types v : val.
Implicit Types z : Z.
Lemma wand_apply' (P R Q : iProp Σ) :
(P R)
(R - Q)
P Q.
Proof.
intros Ha Hb. iIntros "HP". iApply Hb. iApply Ha. done.
Qed.
Lemma tac_wp_allocN Δ Δ' s E1 E2 j K v n Φ :
(0 < n)%Z
MaybeIntoLaterNEnvs 1 Δ Δ'
( l,
match envs_app false (Esnoc Enil j (array l (DfracOwn 1) (replicate (Z.to_nat n) v))) Δ' with
| Some Δ'' =>
envs_entails Δ'' (WP fill K (Val $ LitV $ LitLoc l) @ s; E1; E2 {{ Φ }})
| None => False
end)
envs_entails Δ (WP fill K (AllocN (Val $ LitV $ LitInt n) (Val v)) @ s; E1; E2 {{ Φ }}).
Proof.
rewrite envs_entails_unseal=> ? ? HΔ.
rewrite -wp_bind. eapply wand_apply'; last exact: wp_allocN.
rewrite into_laterN_env_sound; apply later_mono, forall_intro=> l.
specialize (HΔ l).
destruct (envs_app _ _ _) as [Δ''|] eqn:HΔ'; [ | contradiction ].
rewrite envs_app_sound //; simpl.
apply wand_intro_l. by rewrite right_id wand_elim_r.
Qed.
Lemma tac_wp_alloc Δ Δ' s E1 E2 j K v Φ :
MaybeIntoLaterNEnvs 1 Δ Δ'
( l,
match envs_app false (Esnoc Enil j (l v)) Δ' with
| Some Δ'' =>
envs_entails Δ'' (WP fill K (Val $ LitV l) @ s; E1; E2 {{ Φ }})
| None => False
end)
envs_entails Δ (WP fill K (Alloc (Val v)) @ s; E1; E2 {{ Φ }}).
Proof.
rewrite envs_entails_unseal=> ? HΔ.
rewrite -wp_bind. eapply wand_apply'; last exact: wp_alloc.
rewrite into_laterN_env_sound; apply later_mono, forall_intro=> l.
specialize (HΔ l).
destruct (envs_app _ _ _) as [Δ''|] eqn:HΔ'; [ | contradiction ].
rewrite envs_app_sound //; simpl.
apply wand_intro_l. by rewrite right_id wand_elim_r.
Qed.
Lemma tac_wp_free Δ Δ' s E1 E2 i K l v Φ :
MaybeIntoLaterNEnvs 1 Δ Δ'
envs_lookup i Δ' = Some (false, l v)%I
(let Δ'' := envs_delete false i false Δ' in
envs_entails Δ'' (WP fill K (Val $ LitV LitUnit) @ s; E1; E2 {{ Φ }}))
envs_entails Δ (WP fill K (Free (LitV l)) @ s; E1; E2 {{ Φ }}).
Proof.
rewrite envs_entails_unseal=> ? Hlk Hfin.
rewrite -wp_bind. eapply wand_apply; first apply wand_entails, wp_free.
rewrite into_laterN_env_sound -later_sep envs_lookup_split //; simpl.
rewrite -Hfin wand_elim_r (envs_lookup_sound' _ _ _ _ _ Hlk).
by apply later_mono, sep_mono_r.
Qed.
Lemma tac_wp_load Δ Δ' s E1 E2 i K b l q v Φ :
MaybeIntoLaterNEnvs 1 Δ Δ'
envs_lookup i Δ' = Some (b, l {q} v)%I
envs_entails Δ' (WP fill K (Val v) @ s; E1; E2 {{ Φ }})
envs_entails Δ (WP fill K (Load (LitV l)) @ s; E1; E2 {{ Φ }}).
Proof.
rewrite envs_entails_unseal=> ?? Hi.
rewrite -wp_bind. eapply wand_apply; first apply wand_entails, wp_load.
rewrite into_laterN_env_sound -later_sep envs_lookup_split //; simpl.
apply later_mono.
destruct b; simpl.
* iIntros "[#$ He]". iIntros "_". iApply Hi. iApply "He". iFrame "#".
* by apply sep_mono_r, wand_mono.
Qed.
Lemma tac_wp_store Δ Δ' s E1 E2 i K l v v' Φ :
MaybeIntoLaterNEnvs 1 Δ Δ'
envs_lookup i Δ' = Some (false, l v)%I
match envs_simple_replace i false (Esnoc Enil i (l v')) Δ' with
| Some Δ'' => envs_entails Δ'' (WP fill K (Val $ LitV LitUnit) @ s; E1; E2 {{ Φ }})
| None => False
end
envs_entails Δ (WP fill K (Store (LitV l) (Val v')) @ s; E1; E2 {{ Φ }}).
Proof.
rewrite envs_entails_unseal=> ???.
destruct (envs_simple_replace _ _ _) as [Δ''|] eqn:HΔ''; [ | contradiction ].
rewrite -wp_bind. eapply wand_apply; first apply wand_entails, wp_store.
rewrite into_laterN_env_sound -later_sep envs_simple_replace_sound //; simpl.
rewrite right_id. by apply later_mono, sep_mono_r, wand_mono.
Qed.
End heap.
(** The tactic [wp_apply_core lem tac_suc tac_fail] evaluates [lem] to a
hypothesis [H] that can be applied, and then runs [wp_bind_core K; tac_suc H]
for every possible evaluation context [K].
- The tactic [tac_suc] should do [iApplyHyp H] to actually apply the hypothesis,
but can perform other operations in addition (see [wp_apply] and [awp_apply]
below).
- The tactic [tac_fail cont] is called when [tac_suc H] fails for all evaluation
contexts [K], and can perform further operations before invoking [cont] to
try again.
TC resolution of [lem] premises happens *after* [tac_suc H] got executed. *)
Ltac wp_apply_core lem tac_suc tac_fail := first
[iPoseProofCore lem as false (fun H =>
lazymatch goal with
| |- envs_entails _ (wp ?s ?E1 ?E2 ?e ?Q) =>
reshape_expr e ltac:(fun K e' =>
wp_bind_core K; tac_suc H)
| _ => fail 1 "wp_apply: not a 'wp'"
end)
|tac_fail ltac:(fun _ => wp_apply_core lem tac_suc tac_fail)
|let P := type of lem in
fail "wp_apply: cannot apply" lem ":" P ].
Tactic Notation "wp_apply" open_constr(lem) :=
wp_apply_core lem ltac:(fun H => iApplyHyp H; try iNext; try wp_expr_simpl)
ltac:(fun cont => fail).
Tactic Notation "wp_smart_apply" open_constr(lem) :=
wp_apply_core lem ltac:(fun H => iApplyHyp H; try iNext; try wp_expr_simpl)
ltac:(fun cont => wp_pure _; []; cont ()).
Tactic Notation "wp_alloc" ident(l) "as" constr(H) :=
let Htmp := iFresh in
let finish _ :=
first [intros l | fail 1 "wp_alloc:" l "not fresh"];
pm_reduce;
lazymatch goal with
| |- False => fail 1 "wp_alloc:" H "not fresh"
| _ => iDestructHyp Htmp as H; wp_finish
end in
wp_pures;
(** The code first tries to use allocation lemma for a single reference,
ie, [tac_wp_alloc] (respectively, [tac_twp_alloc]).
If that fails, it tries to use the lemma [tac_wp_allocN]
(respectively, [tac_twp_allocN]) for allocating an array.
Notice that we could have used the array allocation lemma also for single
references. However, that would produce the resource l [v] instead of
l v for single references. These are logically equivalent assertions
but are not equal. *)
lazymatch goal with
| |- envs_entails _ (wp ?s ?E1 ?E2 ?e ?Q) =>
let process_single _ :=
first
[reshape_expr e ltac:(fun K e' => eapply (tac_wp_alloc _ _ _ _ _ Htmp K))
|fail 1 "wp_alloc: cannot find 'Alloc' in" e];
[tc_solve
|finish ()]
in
let process_array _ :=
first
[reshape_expr e ltac:(fun K e' => eapply (tac_wp_allocN _ _ _ _ _ Htmp K))
|fail 1 "wp_alloc: cannot find 'Alloc' in" e];
[idtac|tc_solve
|finish ()]
in (process_single ()) || (process_array ())
| _ => fail "wp_alloc: not a 'wp'"
end.
Tactic Notation "wp_alloc" ident(l) :=
wp_alloc l as "?".
Tactic Notation "wp_free" :=
let solve_mapsto _ :=
let l := match goal with |- _ = Some (_, (?l {_} _)%I) => l end in
iAssumptionCore || fail "wp_free: cannot find" l "↦ ?" in
wp_pures;
lazymatch goal with
| |- envs_entails _ (wp ?s ?E1 ?E2 ?e ?Q) =>
first
[reshape_expr e ltac:(fun K e' => eapply (tac_wp_free _ _ _ _ _ _ K))
|fail 1 "wp_free: cannot find 'Free' in" e];
[tc_solve
|solve_mapsto ()
|pm_reduce; wp_finish]
| _ => fail "wp_free: not a 'wp'"
end.
Tactic Notation "wp_load" :=
let solve_mapsto _ :=
let l := match goal with |- _ = Some (_, (?l {_} _)%I) => l end in
iAssumptionCore || fail "wp_load: cannot find" l "↦ ?" in
wp_pures;
lazymatch goal with
| |- envs_entails _ (wp ?s ?E1 ?E2 ?e ?Q) =>
first
[reshape_expr e ltac:(fun K e' => eapply (tac_wp_load _ _ _ _ _ _ K))
|fail 1 "wp_load: cannot find 'Load' in" e];
[tc_solve
|solve_mapsto ()
|wp_finish]
| _ => fail "wp_load: not a 'wp'"
end.
Tactic Notation "wp_store" :=
let solve_mapsto _ :=
let l := match goal with |- _ = Some (_, (?l {_} _)%I) => l end in
iAssumptionCore || fail "wp_store: cannot find" l "↦ ?" in
wp_pures;
lazymatch goal with
| |- envs_entails _ (wp ?s ?E1 ?E2 ?e ?Q) =>
first
[reshape_expr e ltac:(fun K e' => eapply (tac_wp_store _ _ _ _ _ _ K))
|fail 1 "wp_store: cannot find 'Store' in" e];
[tc_solve
|solve_mapsto ()
|pm_reduce; first [wp_seq|wp_finish]]
| _ => fail "wp_store: not a 'wp'"
end.

@ -0,0 +1,751 @@
From iris.prelude Require Import options.
From iris.proofmode Require Import tactics.
From iris.heap_lang Require Import lang notation.
From semantics.pl Require Export hoare_lib.
Import hoare.
Implicit Types
(P Q: iProp)
(φ ψ: Prop)
(e: expr)
(v: val).
(** * Hoare logic *)
(** Entailment rules *)
Check ent_equiv.
Check ent_refl.
Check ent_trans.
(* NOTE: True = ⌜True⌝ *)
(* NOTE: False = ⌜False⌝ *)
Check ent_prove_pure.
Check ent_assume_pure.
Check ent_and_elim_r.
Check ent_and_elim_l.
Check ent_and_intro.
Check ent_or_introl.
Check ent_or_intror.
Check ent_or_elim.
Check ent_all_intro.
Check ent_all_elim.
Check ent_exist_intro.
Check ent_exist_elim.
(** Derived entailment rules *)
Lemma ent_weakening P Q R :
(P R)
P Q R.
Proof.
(* TODO: exercise *)
Admitted.
Lemma ent_true P :
P True.
Proof.
(* TODO: exercise *)
Admitted.
Lemma ent_false P :
False P.
Proof.
(* TODO: exercise *)
Admitted.
Lemma ent_and_comm P Q :
P Q Q P.
Proof.
(* TODO: exercise *)
Admitted.
Lemma ent_or_comm P Q :
P Q Q P.
Proof.
(* TODO: exercise *)
Admitted.
Lemma ent_all_comm {X} (Φ : X X iProp) :
( x y, Φ x y) ( y x, Φ x y).
Proof.
(* TODO: exercise *)
Admitted.
Lemma ent_exist_comm {X} (Φ : X X iProp) :
( x y, Φ x y) ( y x, Φ x y).
Proof.
(* TODO: exercise *)
Admitted.
(** Derived Hoare rules *)
Lemma hoare_con_pre P Q Φ e:
(P Q)
{{ Q }} e {{ Φ }}
{{ P }} e {{ Φ }}.
Proof.
intros ??. eapply hoare_con; eauto.
Qed.
Lemma hoare_con_post P Φ Ψ e:
( v, Ψ v Φ v)
{{ P }} e {{ Ψ }}
{{ P }} e {{ Φ }}.
Proof.
intros ??. eapply hoare_con; last done; eauto.
Qed.
Lemma hoare_value_con P Φ v :
(P Φ v)
{{ P }} v {{ Φ }}.
Proof.
intros H. eapply hoare_con; last apply hoare_value.
- apply H.
- eauto.
Qed.
Lemma hoare_value' P v :
{{ P }} v {{ w, P w = v}}.
Proof.
eapply hoare_con; last apply hoare_value with (Φ := (λ v', P v' = v)%I).
- etrans; first apply ent_sep_true. rewrite ent_sep_comm. apply ent_sep_split; first done.
by apply ent_prove_pure.
- done.
Qed.
Lemma hoare_rec P Φ f x e v:
({{ P }} subst' x v (subst' f (rec: f x := e) e) {{Φ}})
{{ P }} (rec: f x := e)%V v {{Φ}}.
Proof.
(* TODO: exercise *)
Admitted.
Lemma hoare_let P Φ x e v:
({{ P }} subst' x v e {{Φ}})
{{ P }} let: x := v in e {{Φ}}.
Proof.
intros Ha. eapply hoare_pure_steps.
{ eapply (rtc_pure_step_fill [AppLCtx _]).
apply pure_step_val. done.
}
eapply hoare_pure_step; last done.
apply pure_step_beta.
Qed.
Lemma hoare_eq_num (n m: Z):
{{ n = m }} #n = #m {{ u, u = #true }}.
Proof.
eapply hoare_pure; first reflexivity.
intros ->. eapply hoare_pure_step.
{ apply pure_step_eq. done. }
apply hoare_value_con.
by apply ent_prove_pure.
Qed.
Lemma hoare_neq_num (n m: Z):
{{ n m }} #n = #m {{ u, u = #false }}.
Proof.
(* TODO: exercise *)
Admitted.
Lemma hoare_sub (z1 z2: Z):
{{ True }} #z1 - #z2 {{ v, v = #(z1 - z2) }}.
Proof.
(* TODO: exercise *)
Admitted.
Lemma hoare_add (z1 z2: Z):
{{ True }} #z1 + #z2 {{ v, v = #(z1 + z2) }}.
Proof.
(* TODO: exercise *)
Admitted.
Lemma hoare_if_false P e1 e2 Φ:
{{ P }} e2 {{ Φ }}
({{ P }} if: #false then e1 else e2 {{ Φ }}).
Proof.
(* TODO: exercise *)
Admitted.
Lemma hoare_if_true P e1 e2 Φ:
{{ P }} e1 {{ Φ }}
({{ P }} if: #true then e1 else e2 {{ Φ }}).
Proof.
(* TODO: exercise *)
Admitted.
Lemma hoare_pure_pre φ Φ e:
{{ φ }} e {{ Φ }} (φ {{ True }} e {{ Φ }}).
Proof.
(* TODO: exercise *)
Admitted.
(** Example: Fibonacci *)
Definition fib: val :=
rec: "fib" "n" :=
if: "n" = #0 then #0
else if: "n" = #1 then #1
else "fib" ("n" - #1) + "fib" ("n" - #2).
Lemma fib_zero:
{{ True }} fib #0 {{ v, v = #0 }}.
Proof.
(* TODO: exercise *)
Admitted.
Lemma fib_one:
{{ True }} fib #1 {{ v, v = #1 }}.
Proof.
(* TODO: exercise *)
Admitted.
Lemma fib_succ (z n m: Z):
{{ True }} fib #(z - 1)%Z {{ v, v = #n }}
{{ True }} fib #(z - 2)%Z {{ v, v = #m }}
{{ z > 1%Z }} fib #z {{ v, v = #(n + m) }}.
Proof.
intros H1 H2. eapply hoare_pure_pre. intros Hgt.
unfold fib.
eapply hoare_pure_steps.
{ econstructor 2.
{ apply pure_step_beta. }
simpl. econstructor 2. { apply (pure_step_fill [IfCtx _ _]). apply pure_step_neq. lia. }
simpl. econstructor 2. { apply pure_step_if_false. }
econstructor 2. { apply (pure_step_fill [IfCtx _ _]). apply pure_step_neq. lia. }
simpl. econstructor 2. { apply pure_step_if_false. }
fold fib. reflexivity.
}
eapply (hoare_bind [BinOpRCtx _ _]).
{ eapply (hoare_bind [AppRCtx _]). { apply hoare_sub. }
intros v. eapply hoare_pure_pre. intros ->. apply H2.
}
intros v. apply hoare_pure_pre. intros ->. simpl.
eapply (hoare_bind [BinOpLCtx _ _]).
{ eapply (hoare_bind [AppRCtx _]). { apply hoare_sub. }
intros v. eapply hoare_pure_pre. intros ->. apply H1.
}
intros v. apply hoare_pure_pre. intros ->. simpl.
eapply hoare_pure_step. { apply pure_step_add. }
eapply hoare_value_con. by apply ent_prove_pure.
Qed.
Lemma fib_succ_oldschool (z n m: Z):
{{ True }} fib #(z - 1)%Z {{ v, v = #n }}
{{ True }} fib #(z - 2)%Z {{ v, v = #m }}
{{ z > 1%Z }} fib #z {{ v, v = #(n + m) }}.
Proof.
(* TODO: exercise *)
Admitted.
Fixpoint Fib (n: nat) :=
match n with
| 0 => 0
| S n =>
match n with
| 0 => 1
| S m => Fib n + Fib m
end
end.
Lemma fib_computes_Fib (n: nat):
{{ True }} fib #n {{ v, v = #(Fib n) }}.
Proof.
induction (lt_wf n) as [n _ IH].
destruct n as [|[|n]].
- simpl. eapply fib_zero.
- simpl. eapply fib_one.
- replace (Fib (S (S n)): Z) with (Fib (S n) + Fib n)%Z by (simpl; lia).
edestruct (hoare_pure_pre (S (S n) > 1))%Z as [H1 _]; eapply H1; last lia.
eapply fib_succ.
+ replace (S (S n) - 1)%Z with (S n: Z) by lia. eapply IH. lia.
+ replace (S (S n) - 2)%Z with (n: Z) by lia. eapply IH. lia.
Qed.
(** ** Example: gcd *)
Definition mod_val : val :=
λ: "a" "b", "a" - ("a" `quot` "b") * "b".
Definition euclid: val :=
rec: "euclid" "a" "b" :=
if: "b" = #0 then "a" else "euclid" "b" (mod_val "a" "b").
Lemma quot_diff a b :
(0 a)%Z (0 < b)%Z (0 a - a `quot` b * b < b)%Z.
Proof.
intros. split.
- rewrite Z.mul_comm -Z.rem_eq; last lia. apply Z.rem_nonneg; lia.
- rewrite Z.mul_comm -Z.rem_eq; last lia.
specialize (Z.rem_bound_pos_pos a b ltac:(lia) ltac:(lia)). lia.
Qed.
Lemma Z_nonneg_ind (P : Z Prop) :
( x, (0 x)%Z ( y, (0 y < x)%Z P y) P x)
x, (0 x)%Z P x.
Proof.
intros IH x Hle. generalize Hle.
revert x Hle. refine (Z_lt_induction (λ x, (0 x)%Z P x) _).
naive_solver.
Qed.
Lemma mod_spec (a b : Z) :
{{ (b > 0)%Z (a >= 0)%Z }}
mod_val #a #b
{{ cv, (c k : Z), cv = #c (0 <= k)%Z (a = b * k + c)%Z (0 <= c < b)%Z }}.
Proof.
eapply (hoare_pure _ (b > 0 a >= 0)%Z).
{ eapply ent_assume_pure. { eapply ent_and_elim_l. }
intros ?. eapply ent_assume_pure. { eapply ent_and_elim_r. }
intros ?. eapply ent_assume_pure. { eapply ent_and_elim_l. }
intros ?. apply ent_prove_pure. done.
}
intros (? & ?).
unfold mod_val. eapply hoare_pure_step.
{ apply pure_step_fill with (K := [AppLCtx _]). apply pure_step_beta. }
fold mod_val. simpl.
apply hoare_let. simpl.
eapply hoare_pure_step.
{ apply pure_step_fill with (K := [BinOpLCtx _ _; BinOpRCtx _ _]).
apply pure_step_quot. lia.
}
simpl. eapply hoare_pure_step.
{ apply pure_step_fill with (K := [BinOpRCtx _ _]). apply pure_step_mul. }
simpl. eapply hoare_pure_step.
{ apply pure_step_sub. }
eapply hoare_value_con.
eapply ent_exist_intro. apply ent_exist_intro with (x := (a `quot` b)%Z).
(* MATH *)
apply ent_prove_pure. split; last split; last split.
- reflexivity.
- apply Z.quot_pos; lia.
- lia.
- apply quot_diff; lia.
Qed.
Lemma gcd_step (b c k : Z) :
Z.gcd b c = Z.gcd (b * k + c) b.
Proof.
rewrite Z.add_comm (Z.gcd_comm _ b) Z.mul_comm Z.gcd_add_mult_diag_r. done.
Qed.
Lemma euclid_step_gt0 (a b : Z) :
( c : Z,
{{ (0 c < b)%Z}}
euclid #b #c
{{ d, d = #(Z.gcd b c) }})
{{ (b > 0)%Z (a >= 0)%Z}} euclid #a #b {{ c, c = #(Z.gcd a b) }}.
Proof.
intros Ha.
eapply (hoare_pure _ (a >= 0 b > 0)%Z).
{ eapply ent_assume_pure. { eapply ent_and_elim_l. }
intros ?. eapply ent_assume_pure. { eapply ent_and_elim_r. }
intros ?. eapply ent_assume_pure. { eapply ent_and_elim_l. }
intros ?. apply ent_prove_pure. done.
}
intros (? & ?).
unfold euclid. eapply hoare_pure_step.
{ apply (pure_step_fill [AppLCtx _]). apply pure_step_beta. }
fold euclid. simpl. apply hoare_let. simpl.
eapply hoare_pure_step.
{ apply (pure_step_fill [IfCtx _ _]). apply pure_step_neq. lia. }
simpl. apply hoare_if_false.
eapply hoare_bind with (K := [AppRCtx _]).
{ apply mod_spec. }
intros v. simpl.
apply hoare_exist_pre. intros d.
apply hoare_exist_pre. intros k.
apply hoare_pure_pre.
intros (-> & ? & -> & ?).
eapply hoare_con; last apply Ha.
{ apply ent_prove_pure. split_and!; lia. }
{ simpl. intros v. eapply ent_assume_pure; first done. intros ->.
apply ent_prove_pure. f_equiv. f_equiv. apply gcd_step.
}
Qed.
Lemma euclid_step_0 (a : Z) :
{{ True }} euclid #a #0 {{ v, v = #a }}.
Proof.
unfold euclid. eapply hoare_pure_step.
{ apply (pure_step_fill [AppLCtx _]). apply pure_step_beta. }
fold euclid. simpl. apply hoare_let. simpl.
eapply hoare_pure_step.
{ apply (pure_step_fill [IfCtx _ _]). apply pure_step_eq. lia. }
simpl. apply hoare_if_true.
apply hoare_value_con. by apply ent_prove_pure.
Qed.
Lemma euclid_proof (a b : Z) :
{{ (0 a 0 b)%Z }} euclid #a #b {{ c, c = #(Z.gcd a b) }}.
Proof.
eapply hoare_pure_pre. intros (Ha & Hb).
revert b Hb a Ha. refine (Z_nonneg_ind _ _).
intros b Hb IH a Ha.
destruct (decide (b = 0)) as [ -> | Hneq0].
- eapply hoare_con; last apply euclid_step_0.
{ done. }
{ intros v. simpl. eapply ent_assume_pure; first done. intros ->.
apply ent_prove_pure.
rewrite Z.gcd_0_r Z.abs_eq; first done. lia.
}
- (* use a mod b < b *)
eapply hoare_con; last apply euclid_step_gt0.
{ apply ent_and_intro; apply ent_prove_pure; lia. }
{ done. }
intros c. apply hoare_pure_pre. intros.
eapply hoare_con; last eapply IH; [ done | done | lia.. ].
Qed.
(** Exercise: Factorial *)
Definition fac : val :=
rec: "fac" "n" :=
if: "n" = #0 then #1
else "n" * "fac" ("n" - #1).
Fixpoint Fac (n : nat) :=
match n with
| 0 => 1
| S n => (S n) * Fac n
end.
Lemma fac_computes_Fac (n : nat) :
{{ True }} fac #n {{ v, v = #(Fac n) }}.
Proof.
(* TODO: exercise *)
Admitted.
(** * Separation Logic *)
(*Check ent_sep_weaken.*)
(*Check ent_sep_true.*)
(*Check ent_sep_comm.*)
(*Check ent_sep_split.*)
(*Check ent_sep_assoc.*)
(*Check ent_pointsto_sep.*)
(*Check ent_exists_sep.*)
(* Note: The separating conjunction can usually be typed with \ast or \sep *)
Lemma ent_pointsto_disj l l' v w :
l v l' w l l'.
Proof.
(* TODO: exercise *)
Admitted.
Lemma ent_sep_exists {X} (Φ : X iProp) P :
( x : X, Φ x P) ( x : X, Φ x) P.
Proof.
(* TODO: exercise *)
Admitted.
(** ** Example: Chains *)
Fixpoint chain_pre n l r : iProp :=
match n with
| 0 => l = r
| S n => t : loc, l #t chain_pre n t r
end.
Definition chain l r : iProp := n, n > 0 chain_pre n l r.
Lemma chain_single (l r : loc) :
l #r chain l r.
Proof.
(* TODO: exercise *)
Admitted.
Lemma chain_cons (l r t : loc) :
l #r chain r t chain l t.
Proof.
(* TODO: exercise *)
Admitted.
Lemma chain_trans (l r t : loc) :
chain l r chain r t chain l t.
Proof.
(* TODO: exercise *)
Admitted.
Lemma chain_sep_false (l r t : loc) :
chain l r chain l t False.
Proof.
(* TODO: exercise *)
Admitted.
Definition cycle l := chain l l.
Lemma chain_cycle l r :
chain l r chain r l cycle l.
Proof.
apply chain_trans.
Qed.
(** New Hoare rules *)
(*Check hoare_frame.*)
(*Check hoare_new.*)
(*Check hoare_store.*)
(*Check hoare_load.*)
Lemma hoare_pure_pre_sep_l (ϕ : Prop) Q Φ e :
(ϕ {{ Q }} e {{ Φ }})
{{ ϕ Q }} e {{ Φ }}.
Proof.
intros He.
eapply hoare_pure.
{ apply ent_sep_weaken. }
intros ?.
eapply hoare_con; last by apply He.
- rewrite ent_sep_comm. apply ent_sep_weaken.
- done.
Qed.
(* Enables rewriting with equivalences ⊣⊢ in pre/post condition *)
#[export] Instance hoare_proper :
Proper (equiv ==> eq ==> (pointwise_relation val ()) ==> impl) hoare.
Proof.
intros P1 P2 HP%ent_equiv e1 e2 <- Φ1 Φ2 HΦ Hp.
eapply hoare_con; last done.
- apply HP.
- done.
Qed.
Definition assert e := (if: e then #() else #0 #0)%E.
Lemma hoare_assert P e :
{{ P }} e {{ v, v = #true }}
{{ P }} assert e {{ v, v = #() }}.
Proof.
(* TODO: exercise *)
Admitted.
Lemma frame_example (f : val) :
( l l' : loc, {{ l #0 }} f #l #l' {{ _, l #42 }})
{{ True }}
let: "x" := ref #0 in
let: "y" := ref #42 in
(f "x" "y";;
assert (!"x" = !"y"))
{{ _, True }}.
Proof.
intros Hf.
eapply hoare_bind with (K := [AppRCtx _]).
{ apply hoare_new. }
intros v. simpl.
apply hoare_exist_pre. intros l.
apply hoare_pure_pre_sep_l. intros ->.
eapply hoare_let. simpl.
eapply hoare_bind with (K := [AppRCtx _]).
{ eapply hoare_con_pre. { apply ent_sep_true. }
eapply hoare_frame. apply hoare_new. }
intros v. simpl.
rewrite -ent_sep_exists. apply hoare_exist_pre. intros l'.
rewrite -ent_sep_assoc. eapply hoare_pure_pre_sep_l. intros ->.
eapply hoare_let. simpl.
eapply hoare_bind with (K := [AppRCtx _]).
{ rewrite ent_sep_comm. eapply hoare_frame. apply Hf. }
intros v. simpl.
apply hoare_let. simpl.
eapply hoare_con_post; first last.
{ apply hoare_assert.
eapply hoare_bind with (K := [BinOpRCtx _ _]).
{ rewrite ent_sep_comm. apply hoare_frame. apply hoare_load. }
intros v'. simpl. rewrite -ent_sep_assoc. apply hoare_pure_pre_sep_l.
intros ->.
eapply hoare_bind with (K := [BinOpLCtx _ _]).
{ rewrite ent_sep_comm. apply hoare_frame. apply hoare_load. }
intros v'. simpl. rewrite -ent_sep_assoc. apply hoare_pure_pre_sep_l.
intros ->.
eapply hoare_pure_step. { by apply pure_step_eq. }
eapply hoare_value_con. by apply ent_prove_pure.
}
intros. apply ent_true.
Qed.
(** Exercise: swap *)
Definition swap : val :=
λ: "l" "r", let: "t" := ! "r" in "r" <- !"l";; "l" <- "t".
Lemma swap_correct (l r: loc) (v w: val):
{{ l v r w }} swap #l #r {{ _, l w r v }}.
Proof.
(* TODO: exercise *)
Admitted.
(** ** Case study: lists *)
Fixpoint is_ll (xs : list val) (v : val) : iProp :=
match xs with
| [] => v = NONEV
| x :: xs =>
(l : loc) (w : val),
v = SOMEV #l l (x, w) is_ll xs w
end.
Definition new_ll : val :=
λ: <>, NONEV.
Definition cons_ll : val :=
λ: "h" "l", SOME (ref ("h", "l")).
Definition head_ll : val :=
λ: "x", match: "x" with NONE => #() | SOME "r" => Fst (!"r") end.
Definition tail_ll : val :=
λ: "x", match: "x" with NONE => #() | SOME "r" => Snd (!"r") end.
Definition len_ll : val :=
rec: "len" "x" := match: "x" with NONE => #0 | SOME "r" => #1 + "len" (Snd !"r") end.
Definition app_ll : val :=
rec: "app" "x" "y" :=
match: "x" with NONE => "y" | SOME "r" =>
let: "rs" := !"r" in
"r" <- (Fst "rs", "app" (Snd "rs") "y");;
SOME "r"
end.
Lemma app_ll_correct xs ys v w :
{{ is_ll xs v is_ll ys w }} app_ll v w {{ u, is_ll (xs ++ ys) u }}.
Proof.
induction xs as [ | x xs IH] in v |-*.
- simpl. apply hoare_pure_pre_sep_l. intros ->.
eapply hoare_bind with (K := [AppLCtx _]).
{ apply hoare_rec. simpl.
eapply hoare_pure_steps.
{ apply pure_step_val. done. }
eapply hoare_value'.
}
intros v'. simpl. rewrite ent_sep_comm. apply hoare_pure_pre_sep_l. intros ->.
apply hoare_rec. simpl.
eapply hoare_pure_step. { apply pure_step_match_injl. }
apply hoare_let. simpl. apply hoare_value.
- simpl. rewrite -ent_sep_exists. apply hoare_exist_pre.
intros l. rewrite -ent_sep_exists. apply hoare_exist_pre.
intros w'. rewrite -ent_sep_assoc. apply hoare_pure_pre_sep_l. intros ->.
eapply hoare_bind with (K := [AppLCtx _ ]).
{ apply hoare_rec. simpl.
eapply hoare_pure_steps. {apply pure_step_val. done. }
eapply hoare_value'.
}
intros v'. simpl. rewrite ent_sep_comm. apply hoare_pure_pre_sep_l. intros ->.
apply hoare_rec. simpl.
eapply hoare_pure_step. {apply pure_step_match_injr. }
apply hoare_let. simpl.
eapply hoare_bind with (K := [AppRCtx _]).
{ apply hoare_frame. apply hoare_frame. apply hoare_load. }
intros v. simpl.
rewrite -!ent_sep_assoc. apply hoare_pure_pre_sep_l. intros ->.
apply hoare_let. simpl.
eapply hoare_bind with (K := [PairRCtx _; StoreRCtx _; AppRCtx _]).
{ eapply hoare_bind with (K := [AppRCtx _; AppLCtx _]).
{ eapply hoare_pure_step. { apply pure_step_snd. }
apply hoare_value'.
}
intros v. simpl. fold app_ll.
rewrite ent_sep_comm. apply hoare_pure_pre_sep_l. intros ->.
rewrite ent_sep_comm. eapply hoare_frame.
apply IH.
}
intros v. simpl.
eapply hoare_bind with (K := [PairLCtx _; StoreRCtx _; AppRCtx _]).
{ eapply hoare_pure_step. { apply pure_step_fst. }
apply hoare_value'.
}
intros v'. simpl. rewrite ent_sep_comm. apply hoare_pure_pre_sep_l. intros ->.
eapply hoare_bind with (K := [AppRCtx _]).
{ rewrite ent_sep_comm. apply hoare_frame.
eapply hoare_bind with (K := [StoreRCtx _]).
{ eapply hoare_pure_steps.
{ eapply pure_step_val. eauto. }
eapply hoare_value'.
}
intros v'. simpl. rewrite ent_sep_comm. apply hoare_pure_pre_sep_l. intros ->.
apply hoare_store.
}
intros v'. simpl. apply hoare_let. simpl.
eapply hoare_pure_steps.
{ eapply pure_step_val. eauto. }
eapply hoare_value_con.
eapply ent_exist_intro. eapply ent_exist_intro.
etrans. { apply ent_sep_true. }
eapply ent_sep_split.
{ apply ent_prove_pure. done. }
apply ent_sep_split; reflexivity.
Qed.
(** Exercise: linked lists *)
Lemma new_ll_correct :
{{ True }} new_ll #() {{ v, is_ll [] v }}.
Proof.
(* TODO: exercise *)
Admitted.
Lemma cons_ll_correct (v x : val) xs :
{{ is_ll xs v }} cons_ll x v {{ u, is_ll (x :: xs) u }}.
Proof.
(* TODO: exercise *)
Admitted.
Lemma head_ll_correct (v x : val) xs :
{{ is_ll (x :: xs) v }} head_ll v {{ w, w = x }}.
Proof.
(* TODO: exercise *)
Admitted.
Lemma tail_ll_correct v x xs :
{{ is_ll (x :: xs) v }} tail_ll v {{ w, is_ll xs w }}.
Proof.
(* TODO: exercise *)
Admitted.
Lemma len_ll_correct v xs :
{{ is_ll xs v }} len_ll v {{ w, w = #(length xs) is_ll xs v }}.
Proof.
(* TODO: exercise *)
Admitted.
(** Exercise: State and prove a strengthened specification for [tail]. *)
Lemma tail_ll_strengthened v x xs :
{{ is_ll (x :: xs) v }} tail_ll v {{ w, False (* FIXME *) }}.
Proof.
(* FIXME: exercise *)
Abort.

@ -0,0 +1,725 @@
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.

@ -0,0 +1,229 @@
From iris.algebra Require Import gmap auth agree gset coPset.
From iris.proofmode Require Import proofmode.
From iris.base_logic.lib Require Import wsat.
From semantics.pl.program_logic Require Export sequential_wp.
From iris.prelude Require Import options.
Import uPred.
(** This file contains the adequacy statements of the Iris program logic. First
we prove a number of auxilary results. *)
Section adequacy.
Context `{!irisGS Λ Σ}.
Implicit Types e : expr Λ.
Implicit Types P Q : iProp Σ.
Implicit Types Φ : val Λ iProp Σ.
Implicit Types Φs : list (val Λ iProp Σ).
Notation wptp s t Φs := ([ list] e;Φ t;Φs, wp' s e Φ)%I.
Lemma wp'_step s e1 σ1 e2 σ2 E κs efs Φ :
prim_step e1 σ1 κs e2 σ2 efs
state_interp σ1 - wp' s E e1 Φ
={E,}= |={}=> |={, E}=>
state_interp σ2 wp' s E e2 Φ efs = [] κs = [].
Proof.
rewrite wp'_unfold /wp_pre.
iIntros (?) "Hσ H".
rewrite (val_stuck e1 σ1 κs e2 σ2 efs) //.
iMod ("H" $! σ1 with "Hσ") as "(_ & H)". iSpecialize ("H" with "[//]").
iModIntro. iApply (step_fupd_wand with "[H]"); first by iApply "H".
iIntros ">(-> & -> & Hσ & H)". eauto with iFrame.
Qed.
Lemma wptp_step s es1 es2 κ σ1 σ2 Φs :
step (es1,σ1) κ (es2, σ2)
state_interp σ1 - wptp s es1 Φs -
|={,}=> |={}=> |={,}=>
state_interp σ2
wptp s es2 Φs
κ= []
length es1 = length es2.
Proof.
iIntros (Hstep) "Hσ Ht".
destruct Hstep as [e1' σ1' e2' σ2' efs t2' t3 Hstep]; simplify_eq/=.
iDestruct (big_sepL2_app_inv_l with "Ht") as (Φs1 Φs2 ->) "[? Ht]".
iDestruct (big_sepL2_cons_inv_l with "Ht") as (Φ Φs3 ->) "[Ht ?]".
iMod (wp'_step with "Hσ Ht") as "H"; first done. iModIntro.
iApply (step_fupd_wand with "H"). iIntros ">($ & He2 & -> & ->) !>".
rewrite !app_nil_r; iFrame.
iPureIntro; split; first done.
rewrite !app_length; simpl; lia.
Qed.
Lemma wptp_steps s n es1 es2 κs σ1 σ2 Φs :
nsteps n (es1, σ1) κs (es2, σ2)
state_interp σ1 - wptp s es1 Φs
={,}= |={}=>^n |={,}=>
state_interp σ2
wptp s es2 Φs
κs= []
length es1 = length es2.
Proof.
revert es1 es2 κs σ1 σ2 Φs.
induction n as [|n IH]=> es1 es2 κs σ1 σ2 Φs /=.
{ inversion_clear 1; iIntros "? ?". iFrame. done. }
iIntros (Hsteps) "Hσ He". inversion_clear Hsteps as [|?? [t1' σ1']].
iDestruct (wptp_step with "Hσ He") as ">H"; first eauto; simplify_eq.
iModIntro. iApply step_fupd_fupd. iApply (step_fupd_wand with "H").
iIntros ">(Hσ & He & -> & %)". iMod (IH with "Hσ He") as "IH"; first done. iModIntro.
iApply (step_fupdN_wand with "IH"). iIntros ">IH".
iDestruct "IH" as "(? & ? & -> & %)".
iFrame. iPureIntro. split; first done. lia.
Qed.
Lemma wp_not_stuck e σ E Φ :
state_interp σ - wp' NotStuck E e Φ ={E}= not_stuck e σ.
Proof.
rewrite wp'_unfold /wp_pre /not_stuck. iIntros "Hσ H".
destruct (to_val e) as [v|] eqn:?; first by eauto.
iSpecialize ("H" $! σ with "Hσ"). rewrite sep_elim_l.
iMod (fupd_plain_mask with "H") as %?; eauto.
Qed.
Lemma wptp_strong_adequacy Φs s n es1 es2 κs σ1 σ2 :
nsteps n (es1, σ1) κs (es2, σ2)
state_interp σ1 - wptp s es1 Φs
={,}= |={}=>^n |={,}=>
e2, s = NotStuck e2 es2 not_stuck e2 σ2
state_interp σ2
([ list] e;Φ es2;Φs, from_option Φ True (to_val e))
length es1 = length es2
κs = [].
Proof.
iIntros (Hstep) "Hσ He". iMod (wptp_steps with "Hσ He") as "Hwp"; first done.
iModIntro. iApply (step_fupdN_wand with "Hwp").
iMod 1 as "(Hσ & Ht & $ & $)"; simplify_eq/=.
iMod (fupd_plain_keep_l
e2, s = NotStuck e2 es2 not_stuck e2 σ2 %I
(state_interp σ2
wptp s es2 (Φs))%I
with "[$Hσ $Ht]") as "(%&Hσ&Hwp)".
{ iIntros "(Hσ & Ht)" (e' -> He').
move: He' => /(elem_of_list_split _ _)[?[?->]].
iDestruct (big_sepL2_app_inv_l with "Ht") as (Φs1 Φs2 ?) "[? Hwp]".
iDestruct (big_sepL2_cons_inv_l with "Hwp") as (Φ Φs3 ->) "[Hwp ?]".
iMod (wp_not_stuck with "Hσ Hwp") as "$"; auto. }
iSplitR; first done. iFrame "Hσ".
iApply big_sepL2_fupd.
iApply (big_sepL2_impl with "Hwp").
iIntros "!#" (? e Φ ??) "Hwp".
destruct (to_val e) as [v2|] eqn:He2'; last done.
apply of_to_val in He2' as <-. simpl. iApply wp'_value_fupd'. done.
Qed.
End adequacy.
(** Iris's generic adequacy result *)
Theorem wp_strong_adequacy Σ Λ `{!invGpreS Σ} e σ1 n κs t2 σ2 φ :
( `{Hinv : !invGS_gen HasNoLc Σ},
|={}=>
(s: stuckness)
(stateI : state Λ iProp Σ)
(Φ : (val Λ iProp Σ)),
let _ : irisGS Λ Σ := IrisG _ _ Hinv stateI
in
stateI σ1
(WP e @ s; {{ Φ }})
( e',
(* there will only be a single thread *)
t2 = [e'] -
(* If this is a stuck-free triple (i.e. [s = NotStuck]), then the thread is not stuck *)
s = NotStuck not_stuck e' σ2 -
(* The state interpretation holds for [σ2] *)
stateI σ2 -
(* If the thread is done, the post-condition [Φ] holds.
Additionally, we can establish that all invariants will hold in this case.
*)
(from_option (λ v, |={,}=> Φ v) True (to_val e')) -
(* Under all these assumptions, we can conclude [φ] in the logic.
In the case that the thread is done, we can use the last assumption to
establish that all invariants hold.
After opening all required invariants, one can use [fupd_mask_subseteq] to introduce the fancy update. *)
|={,}=> φ ))
nsteps n ([e], σ1) κs (t2, σ2)
(* Then we can conclude [φ] at the meta-level. *)
φ.
Proof.
intros Hwp ?.
eapply pure_soundness.
apply (step_fupdN_soundness_no_lc _ n 0)=> Hinv.
iIntros "_".
iMod Hwp as (s stateI Φ) "(Hσ & Hwp & Hφ)".
rewrite /wp swp_eq /swp_def. iMod "Hwp".
iMod (@wptp_strong_adequacy _ _ (IrisG _ _ Hinv stateI) [_]
with "[Hσ] [Hwp]") as "H";[ done | done | | ].
{ by iApply big_sepL2_singleton. }
iAssert (|={}=>^n |={}=> φ)%I
with "[-]" as "H"; last first.
{ destruct n as [ | n ]; first done. iModIntro. by iApply step_fupdN_S_fupd. }
iApply (step_fupdN_wand with "H").
iMod 1 as "(%Hns & Hσ & Hval & %Hlen & ->) /=".
destruct t2 as [ | e' []]; simpl in Hlen; [lia | | lia].
rewrite big_sepL2_singleton.
iApply ("" with "[//] [%] Hσ Hval").
intros ->. apply Hns; first done. by rewrite elem_of_list_singleton.
Qed.
(** Since the full adequacy statement is quite a mouthful, we prove some more
intuitive and simpler corollaries. These lemmas are morover stated in terms of
[rtc erased_step] so one does not have to provide the trace. *)
Record adequate {Λ} (s : stuckness) (e1 : expr Λ) (σ1 : state Λ)
(φ : val Λ state Λ Prop) := {
adequate_result t2 σ2 v2 :
rtc erased_step ([e1], σ1) (of_val v2 :: t2, σ2) φ v2 σ2;
adequate_not_stuck t2 σ2 e2 :
s = NotStuck
rtc erased_step ([e1], σ1) (t2, σ2)
e2 t2 not_stuck e2 σ2
}.
Lemma adequate_alt {Λ} s e1 σ1 (φ : val Λ state Λ Prop) :
adequate s e1 σ1 φ t2 σ2,
rtc erased_step ([e1], σ1) (t2, σ2)
( v2 t2', t2 = of_val v2 :: t2' φ v2 σ2)
( e2, s = NotStuck e2 t2 not_stuck e2 σ2).
Proof.
split.
- intros []; naive_solver.
- constructor; naive_solver.
Qed.
Theorem adequate_tp_safe {Λ} (e1 : expr Λ) t2 σ1 σ2 φ :
adequate NotStuck e1 σ1 φ
rtc erased_step ([e1], σ1) (t2, σ2)
Forall (λ e, is_Some (to_val e)) t2 t3 σ3, erased_step (t2, σ2) (t3, σ3).
Proof.
intros Had ?.
destruct (decide (Forall (λ e, is_Some (to_val e)) t2)) as [|Ht2]; [by left|].
apply (not_Forall_Exists _), Exists_exists in Ht2; destruct Ht2 as (e2&?&He2).
destruct (adequate_not_stuck NotStuck e1 σ1 φ Had t2 σ2 e2) as [?|(κ&e3&σ3&efs&?)];
rewrite ?eq_None_not_Some; auto.
{ exfalso. eauto. }
destruct (elem_of_list_split t2 e2) as (t2'&t2''&->); auto.
right; exists (t2' ++ e3 :: t2'' ++ efs), σ3, κ; econstructor; eauto.
Qed.
Corollary wp_adequacy Σ Λ `{!invGpreS Σ} s e σ φ :
( `{Hinv : !invGS_gen HasNoLc Σ},
|={}=> (stateI : state Λ iProp Σ),
let _ : irisGS Λ Σ := IrisG _ _ Hinv stateI
in
stateI σ WP e @ s; {{ v, φ v }})
adequate s e σ (λ v _, φ v).
Proof.
intros Hwp. apply adequate_alt; intros t2 σ2 [n [κs ?]]%erased_steps_nsteps.
eapply (wp_strong_adequacy Σ _); [|done]=> ?.
iMod Hwp as (stateI) "[Hσ Hwp]".
iExists s, stateI, (λ v, φ v%I) => /=.
iIntros "{$Hσ $Hwp} !>" (e2 ->) "% H Hv".
destruct (to_val e2) as [ v2 | ] eqn:Hv.
- simpl. iMod "Hv" as "%". iApply fupd_mask_intro_discard; [done|].
iSplit; iPureIntro.
+ intros ??. destruct t2'; last done. intros [= ->].
rewrite to_of_val in Hv. injection Hv as ->. done.
+ intros ? ?. rewrite elem_of_list_singleton. naive_solver.
- iModIntro. iSplit; iPureIntro.
+ intros ??. destruct t2'; last done. intros [= ->].
rewrite to_of_val in Hv. done.
+ intros ? ?. rewrite elem_of_list_singleton. naive_solver.
Qed.

@ -0,0 +1,85 @@
(** Some derived lemmas for ectx-based languages *)
From iris.proofmode Require Import proofmode.
From iris.program_logic Require Export ectx_language.
From semantics.pl.program_logic Require Export sequential_wp lifting.
From iris.prelude Require Import options.
Section wp.
Context {Λ : ectxLanguage} `{!irisGS Λ Σ} {Hinh : Inhabited (state Λ)}.
Implicit Types s : stuckness.
Implicit Types P : iProp Σ.
Implicit Types Φ : val Λ iProp Σ.
Implicit Types v : val Λ.
Implicit Types e : expr Λ.
Local Hint Resolve head_prim_reducible head_reducible_prim_step : core.
Local Definition reducible_not_val_inhabitant e := reducible_not_val e inhabitant.
Local Hint Resolve reducible_not_val_inhabitant : core.
Local Hint Resolve head_stuck_stuck : core.
Lemma wp_lift_head_step_fupd {s E1 E2 Φ} e1 :
to_val e1 = None
(|={E1, }=> σ1, state_interp σ1 ={,}=
head_reducible e1 σ1
e2 σ2 κ efs, head_step e1 σ1 κ e2 σ2 efs ={}= |={}=>
state_interp σ2 efs = [] κ = []
WP e2 @ s; ; E2 {{ Φ }})
WP e1 @ s; E1; E2 {{ Φ }}.
Proof.
iIntros (?) "H". iApply wp_lift_step_fupd=>//. iMod "H". iIntros "!>" (σ1) "Hσ".
iMod ("H" with "Hσ") as "[% H]"; iModIntro.
iSplit; first by destruct s; eauto. iIntros (e2 σ2 κ efs ?).
iApply (step_fupd_wand with "(H []) []"); first eauto.
iIntros "($ & $ & $)".
Qed.
Lemma wp_lift_head_step {s E1 E2 Φ} e1 :
to_val e1 = None
(|={E1, }=> σ1, state_interp σ1 ={}=
head_reducible e1 σ1
e2 σ2 κ efs, head_step e1 σ1 κ e2 σ2 efs ={}=
state_interp σ2 efs = [] κ = [] WP e2 @ s; ; E2 {{ Φ }})
WP e1 @ s; E1; E2 {{ Φ }}.
Proof.
iIntros (?) "H". iApply wp_lift_head_step_fupd; [done|]. iMod "H".
iIntros "!>" (?) "?".
iMod ("H" with "[$]") as "[$ H]". iIntros "!>" (e2 σ2 κ efs ?) "!> !>". by iApply "H".
Qed.
Lemma wp_lift_head_step_fupd_nomask {s E1 E2 E3 Φ} e1 :
to_val e1 = None
( σ1, state_interp σ1 ={E1}=
head_reducible e1 σ1
e2 σ2 κ efs, head_step e1 σ1 κ e2 σ2 efs ={E1}[E3]=
state_interp σ2 efs = [] κ = []
WP e2 @ s; E1; E2 {{ Φ }})
WP e1 @ s; E1; E2 {{ Φ }}.
Proof.
iIntros (?) "H". iApply wp_lift_step_fupd_nomask; [done|].
iIntros (σ1) "Hσ1". iMod ("H" with "Hσ1") as "[% H]"; iModIntro.
iSplit; first by destruct s; auto. iIntros (e2 σ2 κ efs Hstep).
iApply (step_fupd_wand with "(H []) []"); first by eauto.
iIntros "($ & $)".
Qed.
Lemma wp_lift_pure_det_head_step {s E1 E2 E' Φ} e1 e2 :
to_val e1 = None
( σ1, head_reducible e1 σ1)
( σ1 κ e2' σ2 efs',
head_step e1 σ1 κ e2' σ2 efs' κ = [] σ2 = σ1 e2' = e2 efs' = [])
(|={E1}[E']=> WP e2 @ s; E1; E2 {{ Φ }}) WP e1 @ s; E1; E2 {{ Φ }}.
Proof using Hinh.
intros. rewrite -(wp_lift_pure_det_step e1 e2); eauto.
destruct s; by auto.
Qed.
Lemma wp_lift_pure_det_head_step' {s E1 E2 Φ} e1 e2 :
to_val e1 = None
( σ1, head_reducible e1 σ1)
( σ1 κ e2' σ2 efs',
head_step e1 σ1 κ e2' σ2 efs' κ = [] σ2 = σ1 e2' = e2 efs' = [])
WP e2 @ s; E1; E2 {{ Φ }} WP e1 @ s; E1; E2 {{ Φ }}.
Proof using Hinh.
intros. rewrite -[(WP e1 @ s; _ {{ _ }})%I]wp_lift_pure_det_head_step //.
rewrite -step_fupd_intro //.
Qed.
End wp.

@ -0,0 +1,148 @@
(** The "lifting lemmas" in this file serve to lift the rules of the operational
semantics to the program logic. *)
From iris.proofmode Require Import proofmode.
From semantics.pl.program_logic Require Export sequential_wp.
From iris.prelude Require Import options.
Section lifting.
Context `{!irisGS Λ Σ}.
Implicit Types s : stuckness.
Implicit Types v : val Λ.
Implicit Types e : expr Λ.
Implicit Types σ : state Λ.
Implicit Types P Q : iProp Σ.
Implicit Types Φ : val Λ iProp Σ.
Local Hint Resolve reducible_no_obs_reducible : core.
Lemma wp_lift_step_fupd s E1 E2 Φ e1 :
to_val e1 = None
(|={E1, }=> σ1, state_interp σ1 ={}=
if s is NotStuck then reducible e1 σ1 else True
e2 σ2 κ efs, prim_step e1 σ1 κ e2 σ2 efs
={}= efs = [] κ = [] state_interp σ2 WP e2 @ s; ; E2 {{ Φ }})
WP e1 @ s; E1; E2 {{ Φ }}.
Proof.
rewrite /wp swp_eq /swp_def wp'_unfold /wp_pre=>->.
iIntros ">Hs !>" (σ1). iIntros "Hstate".
iDestruct ("Hs" with "Hstate") as ">($ & Hs)".
iIntros "!>" (e2 σ2 κ efs Hstep). iApply (step_fupd_wand with "(Hs [//]) []").
iIntros "(-> & -> & $ & >Hwp)". eauto.
Qed.
(*
Lemma wp_lift_stuck E1 E2 Φ e :
to_val e = None
( σ, state_interp σ ={E1,}= stuck e σ)
WP e @ E1; E2 ?{{ Φ }}.
Proof.
rewrite /wp swp_eq /swp_def wp'_unfold /wp_pre=>->. iIntros "H" (σ1). "Hσ".
iMod ("H" with "Hσ") as %[? Hirr]. iModIntro. iSplit; first done.
iIntros (e2 σ2 efs ?). by case: (Hirr κ e2 σ2 efs).
Qed.
*)
(** Derived lifting lemmas. *)
Lemma wp_lift_step s E1 E2 Φ e1 :
to_val e1 = None
(|={E1, }=> σ1, state_interp σ1 ={}=
if s is NotStuck then reducible e1 σ1 else True
e2 σ2 κ efs, prim_step e1 σ1 κ e2 σ2 efs ={}=
efs = [] κ = [] state_interp σ2
WP e2 @ s; ; E2 {{ Φ }})
WP e1 @ s; E1; E2 {{ Φ }}.
Proof.
iIntros (?) "H". iApply wp_lift_step_fupd; [done|].
iMod "H" as "H". iIntros "!>" (σ1) "Hσ". iMod ("H" with "Hσ") as "($ & Hstep)".
iIntros "!> * % !> !>". by iApply "Hstep".
Qed.
Lemma wp_lift_pure_step `{!Inhabited (state Λ)} s E1 E2 E' Φ e1 :
( σ1, if s is NotStuck then reducible e1 σ1 else to_val e1 = None)
( κ σ1 e2 σ2 efs, prim_step e1 σ1 κ e2 σ2 efs κ = [] σ2 = σ1 efs = [])
(|={E1}[E']=> κ e2 efs σ, prim_step e1 σ κ e2 σ efs WP e2 @ s; E1; E2 {{ Φ }})
WP e1 @ s; E1; E2 {{ Φ }}.
Proof.
iIntros (Hsafe Hstep) "H". iApply wp_lift_step.
{ specialize (Hsafe inhabitant). destruct s; eauto using reducible_not_val. }
iMod "H" as "H".
iApply fupd_mask_intro; first set_solver. iIntros "Hclose".
iIntros (σ1) "Hσ !>". iSplit.
{ iPureIntro. destruct s; done. }
iNext. iIntros (e2 σ2 κ efs ?).
destruct (Hstep κ σ1 e2 σ2 efs) as (-> & <- & ->); auto.
iFrame "Hσ". iSplitR; first done. iSplitR; first done.
iModIntro.
iMod "Hclose". iMod "H". by iApply "H".
Qed.
(*
Lemma wp_lift_pure_stuck `{!Inhabited (state Λ)} E Φ e :
( σ, stuck e σ)
True WP e @ E ?{{ Φ }}.
Proof.
iIntros (Hstuck) "_". iApply wp_lift_stuck.
- destruct(to_val e) as [v|] eqn:He; last done.
rewrite -He. by case: (Hstuck inhabitant).
- iIntros (σ ns κs nt) "_". iApply fupd_mask_intro; auto with set_solver.
Qed.
*)
Lemma wp_lift_step_fupd_nomask {s E1 E2 E3 Φ} e1 :
to_val e1 = None
( σ1, state_interp σ1 ={E1}=
if s is NotStuck then reducible e1 σ1 else True
e2 σ2 κ efs, prim_step e1 σ1 κ e2 σ2 efs ={E1}[E3]=
state_interp σ2 efs = [] κ = []
WP e2 @ s; E1; E2 {{ Φ }})
WP e1 @ s; E1; E2 {{ Φ }}.
Proof.
iIntros (?) "H".
iApply (wp_lift_step_fupd s E1 _ _ e1)=>//.
iApply fupd_mask_intro; first set_solver. iIntros "Hcl".
iIntros (σ1) "Hσ1". iMod "Hcl" as "_".
iMod ("H" with "Hσ1") as "($ & H)".
iApply fupd_mask_intro; first set_solver.
iIntros "Hclose" (e2 σ2 κ efs ?). iMod "Hclose" as "_".
iMod ("H" $! e2 σ2 κ efs with "[#]") as "H"; [done|].
iApply fupd_mask_intro; first set_solver. iIntros "Hclose !>".
iMod "Hclose" as "_". iMod "H" as "($ & $ & $ & ?)".
iApply fupd_mask_intro; first set_solver. iIntros "Hcl".
iMod "Hcl" as "_". done.
Qed.
Lemma wp_lift_pure_det_step `{!Inhabited (state Λ)} {s E1 E2 E' Φ} e1 e2 :
( σ1, if s is NotStuck then reducible e1 σ1 else to_val e1 = None)
( σ1 κ e2' σ2 efs', prim_step e1 σ1 κ e2' σ2 efs'
κ = [] σ2 = σ1 e2' = e2 efs' = [])
(|={E1}[E']=> WP e2 @ s; E1; E2 {{ Φ }}) WP e1 @ s; E1; E2 {{ Φ }}.
Proof.
iIntros (? Hpuredet) "H". iApply (wp_lift_pure_step s E1 E2 E'); try done.
{ naive_solver. }
iApply (step_fupd_wand with "H"); iIntros "H".
iIntros (κ e' efs' σ (_&?&->&?)%Hpuredet); auto.
Qed.
Lemma wp_pure_step_fupd `{!Inhabited (state Λ)} s E1 E2 E' e1 e2 φ n Φ :
PureExec φ n e1 e2
φ
(|={E1}[E']=>^n WP e2 @ s; E1; E2 {{ Φ }}) WP e1 @ s; E1; E2 {{ Φ }}.
Proof.
iIntros (Hexec Hφ) "Hwp". specialize (Hexec Hφ).
iInduction Hexec as [e|n e1 e2 e3 [Hsafe ?]] "IH"; simpl; first done.
iApply wp_lift_pure_det_step.
- intros σ. specialize (Hsafe σ). destruct s; eauto using reducible_not_val.
- done.
- by iApply (step_fupd_wand with "Hwp").
Qed.
Lemma wp_pure_step_later `{!Inhabited (state Λ)} s E1 E2 e1 e2 φ n Φ :
PureExec φ n e1 e2
φ
^n WP e2 @ s; E1; E2 {{ Φ }} WP e1 @ s; E1; E2 {{ Φ }}.
Proof.
intros Hexec ?. rewrite -wp_pure_step_fupd //. clear Hexec.
induction n as [|n IH]; by rewrite //= -step_fupd_intro // IH.
Qed.
End lifting.

@ -0,0 +1,69 @@
From stdpp Require Export coPset.
From iris.bi Require Import interface derived_connectives.
From iris.prelude Require Import options.
Declare Scope expr_scope.
Delimit Scope expr_scope with E.
Declare Scope val_scope.
Delimit Scope val_scope with V.
Inductive stuckness := NotStuck | MaybeStuck.
Definition stuckness_leb (s1 s2 : stuckness) : bool :=
match s1, s2 with
| MaybeStuck, NotStuck => false
| _, _ => true
end.
Global Instance stuckness_le : SqSubsetEq stuckness := stuckness_leb.
Global Instance stuckness_le_po : PreOrder stuckness_le.
Proof. split; by repeat intros []. Qed.
Class Swp (PROP EXPR VAL A : Type) :=
wp : A coPset coPset EXPR (VAL PROP) PROP.
Global Arguments wp {_ _ _ _ _} _ _ _ _%E _%I.
Global Instance: Params (@wp) 9 := {}.
Notation "'WP' e @ s ; E1 ; E2 {{ Φ } }" := (wp s E1 E2 e%E Φ)
(at level 20, e, Φ at level 200, only parsing) : bi_scope.
Notation "'WP' e @ E1 ; E2 {{ Φ } }" := (wp NotStuck E1 E2 e%E Φ)
(at level 20, e, Φ at level 200, only parsing) : bi_scope.
Notation "'WP' e @ E1 ; E2 ? {{ Φ } }" := (wp MaybeStuck E1 E2 e%E Φ)
(at level 20, e, Φ at level 200, only parsing) : bi_scope.
Notation "'WP' e @ s ; E1 {{ Φ } }" := (wp s E1 E1 e%E Φ)
(at level 20, e, Φ at level 200, only parsing) : bi_scope.
Notation "'WP' e @ E1 {{ Φ } }" := (wp NotStuck E1 E1 e%E Φ)
(at level 20, e, Φ at level 200, only parsing) : bi_scope.
Notation "'WP' e @ E1 ? {{ Φ } }" := (wp MaybeStuck E1 E1 e%E Φ)
(at level 20, e, Φ at level 200, only parsing) : bi_scope.
Notation "'WP' e {{ Φ } }" := (wp NotStuck e%E Φ)
(at level 20, e, Φ at level 200, only parsing) : bi_scope.
Notation "'WP' e ? {{ Φ } }" := (wp MaybeStuck e%E Φ)
(at level 20, e, Φ at level 200, only parsing) : bi_scope.
Notation "'WP' e @ s ; E1 ; E2 {{ v , Q } }" := (wp s E1 E2 e%E (λ v, Q))
(at level 20, e, Q at level 200,
format "'[hv' 'WP' e '/' @ '[' s ; '/' E1 ; '/' E2 ']' '/' {{ '[' v , '/' Q ']' } } ']'") : bi_scope.
Notation "'WP' e @ E1 ; E2 {{ v , Q } }" := (wp NotStuck E1 E2 e%E (λ v, Q))
(at level 20, e, Q at level 200,
format "'[hv' 'WP' e '/' @ '[' E1 ; '/' E2 ']' '/' {{ '[' v , '/' Q ']' } } ']'") : bi_scope.
Notation "'WP' e @ E1 ; E2 ? {{ v , Q } }" := (wp MaybeStuck E1 E2 e%E (λ v, Q))
(at level 20, e, Q at level 200,
format "'[hv' 'WP' e '/' @ '[' E1 ; '/' E2 ']' '/' ? {{ '[' v , '/' Q ']' } } ']'") : bi_scope.
Notation "'WP' e @ s ; E {{ v , Q } }" := (wp s E E e%E (λ v, Q))
(at level 20, e, Q at level 200,
format "'[hv' 'WP' e '/' @ '[' s ; '/' E ']' '/' {{ '[' v , '/' Q ']' } } ']'") : bi_scope.
Notation "'WP' e @ E {{ v , Q } }" := (wp NotStuck E E e%E (λ v, Q))
(at level 20, e, Q at level 200,
format "'[hv' 'WP' e '/' @ E '/' {{ '[' v , '/' Q ']' } } ']'") : bi_scope.
Notation "'WP' e @ E ? {{ v , Q } }" := (wp MaybeStuck E E e%E (λ v, Q))
(at level 20, e, Q at level 200,
format "'[hv' 'WP' e '/' @ E '/' ? {{ '[' v , '/' Q ']' } } ']'") : bi_scope.
Notation "'WP' e {{ v , Q } }" := (wp NotStuck e%E (λ v, Q))
(at level 20, e, Q at level 200,
format "'[hv' 'WP' e '/' {{ '[' v , '/' Q ']' } } ']'") : bi_scope.
Notation "'WP' e ? {{ v , Q } }" := (wp MaybeStuck e%E (λ v, Q))
(at level 20, e, Q at level 200,
format "'[hv' 'WP' e '/' ? {{ '[' v , '/' Q ']' } } ']'") : bi_scope.

@ -0,0 +1,372 @@
From iris.proofmode Require Import base proofmode classes.
From iris.base_logic.lib Require Export fancy_updates.
From iris.program_logic Require Export language.
From semantics.pl.program_logic Require Export notation.
From iris.prelude Require Import options.
Import uPred.
Class irisGS (Λ : language) (Σ : gFunctors) := IrisG {
iris_invGS : invGS_gen HasNoLc Σ;
(** The state interpretation is an invariant that should hold in
between each step of reduction. Here [state Λ] is the global state. *)
state_interp : state Λ iProp Σ;
}.
#[export] Existing Instance iris_invGS.
Global Opaque iris_invGS.
Definition wp_pre `{!irisGS Λ Σ} (s : stuckness)
(wp : coPset -d> expr Λ -d> (val Λ -d> iPropO Σ) -d> iPropO Σ) :
coPset -d> expr Λ -d> (val Λ -d> iPropO Σ) -d> iPropO Σ := λ E e1 Φ,
match to_val e1 with
| Some v => |={E}=> Φ v
| None => σ1,
state_interp σ1 ={E,}=
if s is NotStuck then reducible e1 σ1 else True
e2 σ2 κ efs, prim_step e1 σ1 κ e2 σ2 efs
={}= |={,E}=>
efs = [] κ = [] state_interp σ2 wp E e2 Φ
end%I.
Local Instance wp_pre_contractive `{!irisGS Λ Σ} s : Contractive (wp_pre s).
Proof.
rewrite /wp_pre /= => n wp wp' Hwp E e1 Φ.
do 22 (f_contractive || f_equiv).
apply Hwp.
Qed.
Definition wp_def `{!irisGS Λ Σ} := λ (s : stuckness), fixpoint (wp_pre s).
Definition wp_aux : seal (@wp_def). Proof. by eexists. Qed.
Definition wp' := wp_aux.(unseal).
Global Arguments wp' {Λ Σ _}.
Lemma wp_eq `{!irisGS Λ Σ} : wp' = @wp_def Λ Σ _.
Proof. rewrite -wp_aux.(seal_eq) //. Qed.
(* sequential version that allows opening invariants *)
Definition swp_def `{!irisGS Λ Σ} : Swp (iProp Σ) (expr Λ) (val Λ) stuckness := λ s E1 E2 e Φ, (|={E1, }=> wp' s e (λ v, |={, E2}=> Φ v))%I.
Definition swp_aux : seal (@swp_def). Proof. by eexists. Qed.
Definition swp := swp_aux.(unseal).
Global Arguments swp {Λ Σ _}.
Global Existing Instance swp.
Lemma swp_eq `{!irisGS Λ Σ} : swp = @swp_def Λ Σ _.
Proof. rewrite -swp_aux.(seal_eq) //. Qed.
Section wp.
Context `{!irisGS Λ Σ}.
Implicit Types s : stuckness.
Implicit Types P : iProp Σ.
Implicit Types Φ : val Λ iProp Σ.
Implicit Types v : val Λ.
Implicit Types e : expr Λ.
(* Weakest pre *)
Lemma wp'_unfold s E e Φ :
wp' s E e Φ wp_pre s (wp' s) E e Φ.
Proof. rewrite wp_eq. apply (fixpoint_unfold (wp_pre s)). Qed.
Global Instance wp'_ne s E e n :
Proper (pointwise_relation _ (dist n) ==> dist n) (wp' s E e).
Proof.
revert e. induction (lt_wf n) as [n _ IH]=> e Φ Ψ HΦ.
rewrite !wp'_unfold /wp_pre /=.
(* FIXME: figure out a way to properly automate this proof *)
(* FIXME: reflexivity, as being called many times by f_equiv and f_contractive
is very slow here *)
do 22 (f_contractive || f_equiv).
rewrite IH; [done | lia | ]. intros v. eapply dist_lt; done.
Qed.
Global Instance wp'_proper s E e :
Proper (pointwise_relation _ () ==> ()) (wp' s E e).
Proof.
by intros Φ Φ' ?; apply equiv_dist=>n; apply wp'_ne=>v; apply equiv_dist.
Qed.
Global Instance wp'_contractive s E e n :
TCEq (to_val e) None
Proper (pointwise_relation _ (dist_later n) ==> dist n) (wp' s E e).
Proof.
intros He Φ Ψ HΦ. rewrite !wp'_unfold /wp_pre He /=.
do 23 (f_contractive || f_equiv).
by do 4 f_equiv.
Qed.
Lemma wp'_value_fupd' s E Φ v : wp' s E (of_val v) Φ |={E}=> Φ v.
Proof. rewrite wp'_unfold /wp_pre to_of_val. auto. Qed.
Lemma wp'_strong_mono s1 s2 E1 E2 e Φ Ψ :
s1 s2 E1 E2
wp' s1 E1 e Φ - ( v, Φ v ={E2}= Ψ v) - wp' s2 E2 e Ψ.
Proof.
iIntros (? HE) "H HΦ". iLöb as "IH" forall (e E1 E2 HE Φ Ψ).
rewrite !wp'_unfold /wp_pre /=.
destruct (to_val e) as [v|] eqn:?.
{ iApply ("" with "[> -]"). by iApply (fupd_mask_mono E1 _). }
iIntros (σ1) "Hσ".
iMod (fupd_mask_subseteq E1) as "Hclose"; first done.
iMod ("H" with "[$]") as "[% H]".
iModIntro. iSplit; [by destruct s1, s2|]. iIntros (e2 σ2 κ efs Hstep).
iMod ("H" with "[//]") as "H". iIntros "!> !>". iMod "H". iModIntro.
iMod "H" as "($ & $ & $ & H)".
iMod "Hclose" as "_". iModIntro.
iApply ("IH" with "[//] H HΦ").
Qed.
Lemma fupd_wp' s E e Φ : (|={E}=> wp' s E e Φ) wp' s E e Φ.
Proof.
rewrite wp'_unfold /wp_pre. iIntros "H". destruct (to_val e) as [v|] eqn:?.
{ by iMod "H". }
iIntros (σ1) "Hσ1". iMod "H". by iApply "H".
Qed.
Lemma wp'_fupd s E e Φ : wp' s E e (λ v, |={E}=> Φ v) wp' s E e Φ.
Proof. iIntros "H". iApply (wp'_strong_mono s s E with "H"); auto. Qed.
Lemma wp'_bind K `{!LanguageCtx K} s E e Φ :
wp' s E e (λ v, wp' s E (K (of_val v)) Φ) wp' s E (K e) Φ.
Proof.
iIntros "H". iLöb as "IH" forall (E e Φ). rewrite wp'_unfold /wp_pre.
destruct (to_val e) as [v|] eqn:He.
{ apply of_to_val in He as <-. by iApply fupd_wp'. }
rewrite wp'_unfold /wp_pre fill_not_val /=; [|done].
iIntros (σ1) "Hσ". iMod ("H" with "[$]") as "[% H]".
iModIntro; iSplit.
{ destruct s; eauto using reducible_fill. }
iIntros (e2 σ2 κ efs Hstep).
destruct (fill_step_inv e σ1 κ e2 σ2 efs) as (e2'&->&?); auto.
iMod ("H" $! e2' σ2 κ efs with "[//]") as "H". iIntros "!>!>".
iMod "H". iModIntro. iMod "H" as "($ & $ & $ & H)". iModIntro. by iApply "IH".
Qed.
Lemma wp'_step_fupd s E1 E2 e P Φ :
TCEq (to_val e) None E2 E1
(|={E1}[E2]=> P) - wp' s E2 e (λ v, P ={E1}= Φ v) - wp' s E1 e Φ.
Proof.
iIntros (?%TCEq_eq ?) "HR H".
rewrite !wp'_unfold /wp_pre /=.
destruct (to_val e) as [v|] eqn:He.
{ apply of_to_val in He as <-. done. }
iIntros (σ1) "Hσ".
iMod "HR".
iMod ("H" with "[$]") as "[% H]".
iModIntro; iSplit.
{ destruct s; eauto. }
iIntros (e2 σ2 κ efs Hstep).
iMod ("H" $! _ _ _ with "[//]") as "H". iIntros "!>!>!>".
iMod "H". iMod "H" as "($ & $ & $ & H)". iMod "HR". iModIntro.
iApply (wp'_strong_mono with "H [HR]"); [done | done | ].
iIntros (v) "". by iApply "".
Qed.
End wp.
Section swp.
Context `{!irisGS Λ Σ}.
Implicit Types s : stuckness.
Implicit Types P : iProp Σ.
Implicit Types Φ : val Λ iProp Σ.
Implicit Types v : val Λ.
Implicit Types e : expr Λ.
(* Weakest pre *)
Global Instance wp_ne s E1 E2 e n :
Proper (pointwise_relation _ (dist n) ==> dist n) (wp s E1 E2 e).
Proof.
intros ???.
rewrite /wp swp_eq /swp_def /=.
do 2 f_equiv. intros ?. f_equiv. done.
Qed.
Global Instance wp_proper s E1 E2 e :
Proper (pointwise_relation _ () ==> ()) (wp s E1 E2 e).
Proof.
intros ???. rewrite /wp swp_eq /swp_def /=.
do 2 f_equiv. intros ?. f_equiv. done.
Qed.
Lemma wp_value_fupd' s E1 E2 Φ v : wp s E1 E2 (of_val v) Φ |={E1, E2}=> Φ v.
Proof.
rewrite /wp swp_eq /swp_def wp'_value_fupd'.
iSplit.
- iIntros "H". iMod "H". iMod "H". iMod "H". done.
- iIntros "H". iMod (fupd_mask_subseteq ) as "Hcl"; first set_solver.
iModIntro. iModIntro. iMod "Hcl" as "_". done.
Qed.
Lemma wp_strong_mono s1 s2 E1 E2 E3 e Φ Ψ :
s1 s2
wp s1 E1 E2 e Φ - ( v, Φ v ={E2, E3}= Ψ v) - wp s2 E1 E3 e Ψ.
Proof.
iIntros (?) "H HΦ".
rewrite /wp swp_eq /swp_def.
iMod "H". iModIntro.
iApply (wp'_strong_mono _ _ with "H [HΦ]"); [done | done | ].
iIntros (v) "H". iModIntro. iMod "H". by iApply "".
Qed.
Lemma fupd_wp s E1 E2 E3 e Φ : (|={E1, E2}=> wp s E2 E3 e Φ) wp s E1 E3 e Φ.
Proof.
rewrite /wp swp_eq /swp_def. iIntros "H". iApply fupd_wp'.
iMod "H". iMod "H". iModIntro. iModIntro. done.
Qed.
Lemma wp_fupd' s E1 E2 e Φ : wp s E1 E1 e (λ v, |={E1, E2}=> Φ v) wp s E1 E2 e Φ.
Proof. iIntros "H". iApply (wp_strong_mono s s E1 E1 with "H"); auto. Qed.
Lemma wp_fupd s E1 E2 e Φ : wp s E1 E2 e (λ v, |={E2}=> Φ v) wp s E1 E2 e Φ.
Proof. iIntros "H". iApply (wp_strong_mono s s E1 E2 with "H"); auto. Qed.
Lemma wp_bind K `{!LanguageCtx K} s E1 E2 E3 e Φ :
wp s E1 E2 e (λ v, wp s E2 E3 (K (of_val v)) Φ) wp s E1 E3 (K e) Φ.
Proof.
iIntros "H".
rewrite /wp swp_eq /swp_def. iMod "H". iApply wp'_bind.
iModIntro. iApply (wp'_strong_mono with "H"); [done | done | ].
iIntros (v) "H". iMod "H". iMod "H". done.
Qed.
Lemma wp_step_fupd s E1 E2 E3 e P Φ :
TCEq (to_val e) None
(|={E1}[E2]=> P) - wp s E2 E2 e (λ v, P ={E1, E3}= Φ v) - wp s E1 E3 e Φ.
Proof.
iIntros (?) "HR H".
rewrite /wp swp_eq /swp_def.
iMod "HR". iMod "H". iModIntro.
iApply (wp'_step_fupd _ _ (|={E2, E1}=> P) with "[HR] [H]"); [done | | ].
{ iApply (step_fupd_intro ); done. }
iApply (wp'_strong_mono with "H"); [done | done | ].
iIntros (v) "H1 !> H2 !>".
iMod "H1". iMod "H2". by iMod ("H1" with "H2").
Qed.
(** * Derived rules *)
Lemma wp_mono s E1 E2 e Φ Ψ : ( v, Φ v Ψ v) WP e @ s; E1; E2 {{ Φ }} WP e @ s; E1; E2 {{ Ψ }}.
Proof.
iIntros (HΦ) "H"; iApply (wp_strong_mono with "H"); auto.
iIntros (v) "?". by iApply HΦ.
Qed.
Lemma wp_stuck_mono s1 s2 E1 E2 e Φ :
s1 s2 WP e @ s1; E1; E2 {{ Φ }} WP e @ s2; E1; E2 {{ Φ }}.
Proof. iIntros (?) "H". iApply (wp_strong_mono with "H"); auto. Qed.
Lemma wp_stuck_weaken s E1 E2 e Φ :
WP e @ s; E1; E2 {{ Φ }} WP e @ E1; E2 ?{{ Φ }}.
Proof. apply wp_stuck_mono. by destruct s. Qed.
Global Instance wp_mono' s E1 E2 e :
Proper (pointwise_relation _ () ==> ()) (wp (PROP:=iProp Σ) s E1 E2 e).
Proof. by intros Φ Φ' ?; apply wp_mono. Qed.
Global Instance wp_flip_mono' s E1 E2 e :
Proper (pointwise_relation _ (flip ()) ==> (flip ())) (wp (PROP:=iProp Σ) s E1 E2 e).
Proof. by intros Φ Φ' ?; apply wp_mono. Qed.
Lemma wp_value_fupd s E1 E2 Φ e v : IntoVal e v WP e @ s; E1; E2 {{ Φ }} |={E1, E2}=> Φ v.
Proof. intros <-. by apply wp_value_fupd'. Qed.
Lemma wp_value' s E Φ v : Φ v WP (of_val v) @ s; E {{ Φ }}.
Proof. rewrite wp_value_fupd'. auto. Qed.
Lemma wp_value s E Φ e v : IntoVal e v Φ v WP e @ s; E {{ Φ }}.
Proof. intros <-. apply wp_value'. Qed.
Lemma wp_frame_l s E1 E2 e Φ R : R WP e @ s; E1; E2 {{ Φ }} WP e @ s; E1; E2 {{ v, R Φ v }}.
Proof. iIntros "[? H]". iApply (wp_strong_mono with "H"); auto with iFrame. Qed.
Lemma wp_frame_r s E1 E2 e Φ R : WP e @ s; E1; E2 {{ Φ }} R WP e @ s; E1; E2 {{ v, Φ v R }}.
Proof. iIntros "[H ?]". iApply (wp_strong_mono with "H"); auto with iFrame. Qed.
Lemma wp_frame_step_l s E1 E2 E3 e Φ R :
TCEq (to_val e) None
(|={E1}[E2]=> R) WP e @ s; E2; E2 {{ v, |={E1, E3}=> Φ v }} WP e @ s; E1; E3 {{ v, R Φ v }}.
Proof.
iIntros (?) "[Hu Hwp]". iApply (wp_step_fupd with "Hu"); try done.
iApply (wp_mono with "Hwp"). iIntros (?) "Hf $". iApply "Hf".
Qed.
Lemma wp_frame_step_r s E1 E2 E3 e Φ R :
TCEq (to_val e) None
WP e @ s; E2; E2 {{ v, |={E1, E3}=> Φ v }} (|={E1}[E2]=> R) WP e @ s; E1; E3 {{ v, Φ v R }}.
Proof.
rewrite [(WP _ @ _; _ {{ _ }} _)%I]comm; setoid_rewrite (comm _ _ R).
apply wp_frame_step_l.
Qed.
Lemma wp_frame_step_l' s E1 E2 e Φ R :
TCEq (to_val e) None E1 E2 R WP e @ s; E1; E2 {{ Φ }} WP e @ s; E1; E2 {{ v, R Φ v }}.
Proof.
iIntros (??) "[??]". iApply (wp_frame_step_l s E1 E1 E2).
iFrame. iSplitR; first eauto. iApply (wp_strong_mono with "[$]"); first done.
iIntros (v) "?". iMod (fupd_mask_subseteq E1) as "Hcl"; first done. iModIntro. iMod "Hcl". eauto.
Qed.
Lemma wp_frame_step_r' s E1 E2 e Φ R :
TCEq (to_val e) None E1 E2 WP e @ s; E1; E2 {{ Φ }} R WP e @ s; E1; E2 {{ v, Φ v R }}.
Proof.
rewrite [(WP _ @ _; _ {{ _ }} _)%I]comm; setoid_rewrite (comm _ _ R).
apply wp_frame_step_l'.
Qed.
Lemma wp_wand s E1 E2 e Φ Ψ :
WP e @ s; E1; E2 {{ Φ }} - ( v, Φ v - Ψ v) - WP e @ s; E1; E2 {{ Ψ }}.
Proof.
iIntros "Hwp H". iApply (wp_strong_mono with "Hwp"); auto.
iIntros (?) "?". by iApply "H".
Qed.
Lemma wp_wand_l s E1 E2 e Φ Ψ :
( v, Φ v - Ψ v) WP e @ s; E1; E2 {{ Φ }} WP e @ s; E1; E2 {{ Ψ }}.
Proof. iIntros "[H Hwp]". iApply (wp_wand with "Hwp H"). Qed.
Lemma wp_wand_r s E1 E2 e Φ Ψ :
WP e @ s; E1; E2 {{ Φ }} ( v, Φ v - Ψ v) WP e @ s; E1; E2 {{ Ψ }}.
Proof. iIntros "[Hwp H]". iApply (wp_wand with "Hwp H"). Qed.
Lemma wp_frame_wand s E1 E2 e Φ R :
R - WP e @ s; E1; E2 {{ v, R - Φ v }} - WP e @ s; E1; E2 {{ Φ }}.
Proof.
iIntros "HR HWP". iApply (wp_wand with "HWP").
iIntros (v) "". by iApply "".
Qed.
Lemma wp_bind' K `{!LanguageCtx K} s E1 E2 e Φ :
wp s E1 E1 e (λ v, wp s E1 E2 (K (of_val v)) Φ) wp s E1 E2 (K e) Φ.
Proof. iApply wp_bind. Qed.
End swp.
(** Proofmode class instances *)
Section proofmode_classes.
Context `{!irisGS Λ Σ}.
Implicit Types P Q : iProp Σ.
Implicit Types Φ : val Λ iProp Σ.
Implicit Types v : val Λ.
Implicit Types e : expr Λ.
Global Instance frame_wp p s E1 E2 e R Φ Ψ :
( v, Frame p R (Φ v) (Ψ v))
Frame p R (WP e @ s; E1; E2 {{ Φ }}) (WP e @ s; E1; E2 {{ Ψ }}) | 2.
Proof. rewrite /Frame=> HR. rewrite wp_frame_l. apply wp_mono, HR. Qed.
Global Instance is_except_0_wp s E1 E2 e Φ : IsExcept0 (WP e @ s; E1; E2 {{ Φ }}).
Proof. by rewrite /IsExcept0 -{2}(fupd_wp _ E1 E1) -except_0_fupd -fupd_intro. Qed.
Global Instance elim_modal_bupd_wp p s E1 E2 e P Φ :
ElimModal True p false (|==> P) P (WP e @ s; E1; E2 {{ Φ }}) (WP e @ s; E1; E2 {{ Φ }}).
Proof.
by rewrite /ElimModal intuitionistically_if_elim
(bupd_fupd E1) fupd_frame_r wand_elim_r fupd_wp.
Qed.
Global Instance elim_modal_fupd_wp p s E1 E2 e P Φ :
ElimModal True p false (|={E1}=> P) P (WP e @ s; E1; E2 {{ Φ }}) (WP e @ s; E1; E2 {{ Φ }}).
Proof.
by rewrite /ElimModal intuitionistically_if_elim
fupd_frame_r wand_elim_r fupd_wp.
Qed.
Global Instance elim_modal_fupd_wp_ne p s E1 E2 E3 e P Φ :
ElimModal True p false
(|={E1,E2}=> P) P
(WP e @ s; E1; E3 {{ Φ }}) (WP e @ s; E2; E3 {{ Φ }})%I | 100.
Proof.
intros ?. rewrite intuitionistically_if_elim fupd_frame_r wand_elim_r.
rewrite fupd_wp //.
Qed.
Global Instance add_modal_fupd_wp s E1 E2 e P Φ :
AddModal (|={E1}=> P) P (WP e @ s; E1; E2 {{ Φ }}).
Proof. by rewrite /AddModal fupd_frame_r wand_elim_r fupd_wp. Qed.
Global Instance elim_acc_wp_nonatomic {X} E0 E1 E2 α β γ e s Φ :
ElimAcc (X:=X) True (fupd E1 E0) (fupd E2 E2)
α β γ (WP e @ s; E1; E2 {{ Φ }})
(λ x, WP e @ s; E0; E2 {{ v, |={E2}=> β x (γ x -? Φ v) }})%I.
Proof.
iIntros (_) "Hinner >Hacc". iDestruct "Hacc" as (x) "[Hα Hclose]".
iApply wp_fupd.
iApply (wp_wand with "(Hinner Hα)").
iIntros (v) ">[Hβ HΦ]". iApply "". by iApply "Hclose".
Qed.
End proofmode_classes.
Loading…
Cancel
Save