From c48ae35d3700962d42192ab95fa98120d248d09a Mon Sep 17 00:00:00 2001
From: Robbert Krebbers <mail@robbertkrebbers.nl>
Date: Tue, 17 Jan 2017 10:51:59 +0100
Subject: [PATCH] Add examples of IPM paper to tests/ipm_paper.v.

---
 _CoqProject                |   2 +-
 theories/tests/counter.v   | 140 ---------------------
 theories/tests/ipm_paper.v | 241 +++++++++++++++++++++++++++++++++++++
 3 files changed, 242 insertions(+), 141 deletions(-)
 delete mode 100644 theories/tests/counter.v
 create mode 100644 theories/tests/ipm_paper.v

diff --git a/_CoqProject b/_CoqProject
index c1a43de3b..58f275caf 100644
--- a/_CoqProject
+++ b/_CoqProject
@@ -119,7 +119,7 @@ theories/tests/proofmode.v
 theories/tests/barrier_client.v
 theories/tests/list_reverse.v
 theories/tests/tree_sum.v
-theories/tests/counter.v
+theories/tests/ipm_paper.v
 theories/proofmode/strings.v
 theories/proofmode/coq_tactics.v
 theories/proofmode/environments.v
diff --git a/theories/tests/counter.v b/theories/tests/counter.v
deleted file mode 100644
index 9206a9fcc..000000000
--- a/theories/tests/counter.v
+++ /dev/null
@@ -1,140 +0,0 @@
-(* This file contains a formalization of the monotone counter, but with an
-explicit contruction of the monoid, as we have also done in the proof mode
-paper. This should simplify explaining and understanding what is happening.
-A version that uses the authoritative monoid and natural number monoid
-under max can be found in `heap_lang/lib/counter.v`. *)
-From iris.program_logic Require Export weakestpre.
-From iris.heap_lang Require Export lang.
-From iris.program_logic Require Export hoare.
-From iris.proofmode Require Import tactics.
-From iris.heap_lang Require Import proofmode notation.
-Set Default Proof Using "Type".
-Import uPred.
-
-Definition newcounter : val := λ: <>, ref #0.
-Definition incr : val :=
-  rec: "incr" "l" :=
-    let: "n" := !"l" in
-    if: CAS "l" "n" (#1 + "n") then #() else "incr" "l".
-Definition read : val := λ: "l", !"l".
-
-(** The CMRA we need. *)
-Inductive M := Auth : nat → M | Frag : nat → M | Bot.
-
-Section M.
-  Arguments cmra_op _ !_ !_/.
-  Arguments op _ _ !_ !_/.
-  Arguments core _ _ !_/.
-
-  Canonical Structure M_C : ofeT := leibnizC M.
-
-  Instance M_valid : Valid M := λ x, x ≠ Bot.
-  Instance M_op : Op M := λ x y,
-    match x, y with
-    | Auth n, Frag j | Frag j, Auth n => if decide (j ≤ n)%nat then Auth n else Bot
-    | Frag i, Frag j => Frag (max i j)
-    | _, _ => Bot
-    end.
-  Instance M_pcore : PCore M := λ x,
-    Some match x with Auth j | Frag j => Frag j | _ => Bot end.
-  Instance M_empty : Empty M := Frag 0.
-
-  Definition M_ra_mixin : RAMixin M.
-  Proof.
-    apply ra_total_mixin; try solve_proper || eauto.
-    - intros [n1|i1|] [n2|i2|] [n3|i3|];
-        repeat (simpl; case_decide); f_equal/=; lia.
-    - intros [n1|i1|] [n2|i2|]; repeat (simpl; case_decide); f_equal/=; lia.
-    - intros [n|i|]; repeat (simpl; case_decide); f_equal/=; lia.
-    - by intros [n|i|].
-    - intros [n1|i1|] y [[n2|i2|] ?]; exists (core y); simplify_eq/=;
-        repeat (simpl; case_decide); f_equal/=; lia.
-    - intros [n1|i1|] [n2|i2|]; simpl; by try case_decide.
-  Qed.
-  Canonical Structure M_R : cmraT := discreteR M M_ra_mixin.
-  Definition M_ucmra_mixin : UCMRAMixin M.
-  Proof.
-    split; try (done || apply _).
-    intros [?|?|]; simpl; try case_decide; f_equal/=; lia.
-  Qed.
-  Canonical Structure M_UR : ucmraT := discreteUR M M_ra_mixin M_ucmra_mixin.
-
-  Global Instance frag_persistent n : Persistent (Frag n).
-  Proof. by constructor. Qed.
-  Lemma auth_frag_valid j n : ✓ (Auth n ⋅ Frag j) → (j ≤ n)%nat.
-  Proof. simpl. case_decide. done. by intros []. Qed.
-  Lemma auth_frag_op (j n : nat) : (j ≤ n)%nat → Auth n = Auth n ⋅ Frag j.
-  Proof. intros. by rewrite /= decide_True. Qed.
-
-  Lemma M_update n : Auth n ~~> Auth (S n).
-  Proof.
-    apply cmra_discrete_update=>-[m|j|] /= ?; repeat case_decide; done || lia.
-  Qed.
-End M.
-
-Class counterG Σ := CounterG { counter_tokG :> inG Σ M_UR }.
-Definition counterΣ : gFunctors := #[GFunctor M_UR].
-Instance subG_counterΣ {Σ} : subG counterΣ Σ → counterG Σ.
-Proof. solve_inG. Qed.
-
-Section proof.
-Context `{!heapG Σ, !counterG Σ}.
-Implicit Types l : loc.
-
-Definition I (γ : gname) (l : loc) : iProp Σ :=
-  (∃ c : nat, l ↦ #c ∗ own γ (Auth c))%I.
-
-Definition C (l : loc) (n : nat) : iProp Σ :=
-  (∃ N γ, inv N (I γ l) ∧ own γ (Frag n))%I.
-
-(** The main proofs. *)
-Global Instance C_persistent l n : PersistentP (C l n).
-Proof. apply _. Qed.
-
-Lemma newcounter_spec :
-  {{ True }} newcounter #() {{ v, ∃ l, ⌜v = #l⌝ ∧ C l 0 }}.
-Proof.
-  iIntros "!# _ /=". rewrite -wp_fupd /newcounter /=. wp_seq. wp_alloc l as "Hl".
-  iMod (own_alloc (Auth 0)) as (γ) "Hγ"; first done.
-  rewrite (auth_frag_op 0 0) //; iDestruct "Hγ" as "[Hγ Hγf]".
-  set (N := nroot .@ "C").
-  iMod (inv_alloc N _ (I γ l) with "[Hl Hγ]") as "#?".
-  { iIntros "!>". iExists 0%nat. by iFrame. }
-  iModIntro. rewrite /C; eauto 10.
-Qed.
-
-Lemma incr_spec l n :
-  {{ C l n }} incr #l {{ v, ⌜v = #()⌝ ∧ C l (S n) }}.
-Proof.
-  iIntros "!# Hl /=". iLöb as "IH". wp_rec.
-  iDestruct "Hl" as (N γ) "[#Hinv Hγf]".
-  wp_bind (! _)%E; iInv N as (c) "[Hl Hγ]" "Hclose".
-  wp_load. iMod ("Hclose" with "[Hl Hγ]"); [iNext; iExists c; by iFrame|].
-  iModIntro. wp_let. wp_op.
-  wp_bind (CAS _ _ _). iInv N as (c') ">[Hl Hγ]" "Hclose".
-  destruct (decide (c' = c)) as [->|].
-  - iDestruct (own_valid_2 with "Hγ Hγf") as %?%auth_frag_valid.
-    iMod (own_update_2 with "Hγ Hγf") as "Hγ".
-    { rewrite -auth_frag_op. apply M_update. done. }
-    rewrite (auth_frag_op (S n) (S c)); last omega.
-    iDestruct "Hγ" as "[Hγ Hγf]".
-    wp_cas_suc. iMod ("Hclose" with "[Hl Hγ]").
-    { iNext. iExists (S c). rewrite Nat2Z.inj_succ Z.add_1_l. by iFrame. }
-    iModIntro. wp_if. rewrite {3}/C; eauto 10.
-  - wp_cas_fail; first (intros [=]; abstract omega).
-    iMod ("Hclose" with "[Hl Hγ]"); [iNext; iExists c'; by iFrame|].
-    iModIntro. wp_if. iApply ("IH" with "[Hγf]"). rewrite {3}/C; eauto 10.
-Qed.
-
-Lemma read_spec l n :
-  {{ C l n }} read #l {{ v, ∃ m : nat, ⌜v = #m ∧ n ≤ m⌝ ∧ C l m }}.
-Proof.
-  iIntros "!# Hl /=". iDestruct "Hl" as (N γ) "[#Hinv Hγf]".
-  rewrite /read /=. wp_let. iInv N as (c) "[Hl Hγ]" "Hclose". wp_load.
-  iDestruct (own_valid γ (Frag n ⋅ Auth c) with "[-]") as % ?%auth_frag_valid.
-  { iApply own_op. by iFrame. }
-  rewrite (auth_frag_op c c); last lia; iDestruct "Hγ" as "[Hγ Hγf']".
-  iMod ("Hclose" with "[Hl Hγ]"); [iNext; iExists c; by iFrame|].
-  iModIntro; rewrite /C; eauto 10 with omega.
-Qed.
-End proof.
diff --git a/theories/tests/ipm_paper.v b/theories/tests/ipm_paper.v
new file mode 100644
index 000000000..3449ede5e
--- /dev/null
+++ b/theories/tests/ipm_paper.v
@@ -0,0 +1,241 @@
+From iris.base_logic Require Import base_logic.
+From iris.proofmode Require Import tactics.
+From iris.program_logic Require Export hoare.
+From iris.heap_lang Require Import proofmode notation.
+Set Default Proof Using "Type".
+
+(** The proofs from Section 3.1 *)
+Section demo.
+  Context {M : ucmraT}.
+  Notation iProp := (uPred M).
+
+  (* The version in Coq *)
+  Lemma and_exist A (P R: Prop) (Ψ: A → Prop) :
+    P ∧ (∃ a, Ψ a) ∧ R → ∃ a, P ∧ Ψ a.
+  Proof.
+    intros [HP [HΨ HR]].
+    destruct HΨ as [x HΨ].
+    exists x.
+    split. assumption. assumption.
+  Qed.
+
+  (* The version in IPM *)
+  Lemma sep_exist A (P R: iProp) (Ψ: A → iProp) :
+    P ∗ (∃ a, Ψ a) ∗ R ⊢ ∃ a, Ψ a ∗ P.
+  Proof.
+    iIntros "[HP [HΨ HR]]".
+    iDestruct "HΨ" as (x) "HΨ".
+    iExists x.
+    iSplitL "HΨ". iAssumption. iAssumption.
+  Qed.
+
+  (* The short version in IPM, as in the paper *)
+  Lemma sep_exist_short A (P R: iProp) (Ψ: A → iProp) :
+    P ∗ (∃ a, Ψ a) ∗ R ⊢ ∃ a, Ψ a ∗ P.
+  Proof. iIntros "[HP [HΨ HR]]". iFrame "HP". iAssumption. Qed.
+
+  (* An even shorter version in IPM, using the frame introduction pattern `$` *)
+  Lemma sep_exist_shorter A (P R: iProp) (Ψ: A → iProp) :
+    P ∗ (∃ a, Ψ a) ∗ R ⊢ ∃ a, Ψ a ∗ P.
+  Proof. by iIntros "[$ [??]]". Qed.
+End demo.
+
+(** The proofs from Section 3.2 *)
+(** In the Iris development we often write specifications directly using weakest
+preconditions, in sort of `CPS` style, so that they can be applied easier when
+proving client code. A version of [list_reverse] in that style can be found in
+the file [theories/tests/list_reverse.v]. *)
+Section list_reverse.
+  Context `{!heapG Σ}.
+  Notation iProp := (iProp Σ).
+  Implicit Types l : loc.
+
+  Fixpoint is_list (hd : val) (xs : list val) : iProp :=
+    match xs with
+    | [] => ⌜hd = NONEV⌝
+    | x :: xs => ∃ l hd', ⌜hd = SOMEV #l⌝ ∗ l ↦ (x,hd') ∗ is_list hd' xs
+    end%I.
+
+  Definition rev : val :=
+    rec: "rev" "hd" "acc" :=
+      match: "hd" with
+        NONE => "acc"
+      | SOME "l" =>
+         let: "tmp1" := Fst !"l" in
+         let: "tmp2" := Snd !"l" in
+         "l" <- ("tmp1", "acc");;
+         "rev" "tmp2" "hd"
+      end.
+
+  Lemma rev_acc_ht hd acc xs ys :
+    {{ is_list hd xs ∗ is_list acc ys }} rev hd acc {{ w, is_list w (reverse xs ++ ys) }}.
+  Proof.
+    iIntros "!# [Hxs Hys]".
+    iLöb as "IH" forall (hd acc xs ys). wp_rec; wp_let.
+    destruct xs as [|x xs]; iSimplifyEq.
+    - (* nil *) by wp_match.
+    - (* cons *) iDestruct "Hxs" as (l hd') "(% & Hx & Hxs)"; iSimplifyEq.
+      wp_match. wp_load. wp_proj. wp_let. wp_load. wp_proj. wp_let. wp_store.
+      rewrite reverse_cons -assoc.
+      iApply ("IH" $! hd' (InjRV #l) xs (x :: ys) with "Hxs [Hx Hys]").
+      iExists l, acc; by iFrame.
+  Qed.
+
+  Lemma rev_ht hd xs :
+    {{ is_list hd xs }} rev hd NONE {{ w, is_list w (reverse xs) }}.
+  Proof.
+    iIntros "!# Hxs". rewrite -(right_id_L [] (++) (reverse xs)).
+    iApply (rev_acc_ht hd NONEV with "[Hxs]"); simpl; by iFrame.
+  Qed.
+End list_reverse.
+
+(** The proofs from Section 5 *)
+(** This part contains a formalization of the monotone counter, but with an
+explicit contruction of the monoid, as we have also done in the proof mode
+paper. This should simplify explaining and understanding what is happening.
+A version that uses the authoritative monoid and natural number monoid
+under max can be found in [theories/heap_lang/lib/counter.v]. *)
+
+(** The invariant rule in the paper is in fact derived from mask changing
+update modalities (which we did not cover in the paper). Normally we use these
+mask changing update modalities directly in our proofs, but in this file we use
+the first prove the rule as a lemma, and then use that. *)
+Lemma wp_inv_open `{irisG Λ Σ} N E P e Φ :
+  nclose N ⊆ E → atomic e →
+  inv N P ∗ (▷ P -∗ WP e @ E ∖ ↑N {{ v, ▷ P ∗ Φ v }}) ⊢ WP e @ E {{ Φ }}.
+Proof.
+  iIntros (??) "[#Hinv Hwp]".
+  iMod (inv_open E N P with "Hinv") as "[HP Hclose]"=>//.
+  iApply wp_wand_r; iSplitL "HP Hwp"; [by iApply "Hwp"|].
+  iIntros (v) "[HP $]". by iApply "Hclose".
+Qed.
+
+Definition newcounter : val := λ: <>, ref #0.
+Definition incr : val :=
+  rec: "incr" "l" :=
+    let: "n" := !"l" in
+    if: CAS "l" "n" (#1 + "n") then #() else "incr" "l".
+Definition read : val := λ: "l", !"l".
+
+(** The CMRA we need. *)
+Inductive M := Auth : nat → M | Frag : nat → M | Bot.
+
+Section M.
+  Arguments cmra_op _ !_ !_/.
+  Arguments op _ _ !_ !_/.
+  Arguments core _ _ !_/.
+
+  Canonical Structure M_C : ofeT := leibnizC M.
+
+  Instance M_valid : Valid M := λ x, x ≠ Bot.
+  Instance M_op : Op M := λ x y,
+    match x, y with
+    | Auth n, Frag j | Frag j, Auth n => if decide (j ≤ n)%nat then Auth n else Bot
+    | Frag i, Frag j => Frag (max i j)
+    | _, _ => Bot
+    end.
+  Instance M_pcore : PCore M := λ x,
+    Some match x with Auth j | Frag j => Frag j | _ => Bot end.
+  Instance M_empty : Empty M := Frag 0.
+
+  Definition M_ra_mixin : RAMixin M.
+  Proof.
+    apply ra_total_mixin; try solve_proper || eauto.
+    - intros [n1|i1|] [n2|i2|] [n3|i3|];
+        repeat (simpl; case_decide); f_equal/=; lia.
+    - intros [n1|i1|] [n2|i2|]; repeat (simpl; case_decide); f_equal/=; lia.
+    - intros [n|i|]; repeat (simpl; case_decide); f_equal/=; lia.
+    - by intros [n|i|].
+    - intros [n1|i1|] y [[n2|i2|] ?]; exists (core y); simplify_eq/=;
+        repeat (simpl; case_decide); f_equal/=; lia.
+    - intros [n1|i1|] [n2|i2|]; simpl; by try case_decide.
+  Qed.
+  Canonical Structure M_R : cmraT := discreteR M M_ra_mixin.
+  Definition M_ucmra_mixin : UCMRAMixin M.
+  Proof.
+    split; try (done || apply _).
+    intros [?|?|]; simpl; try case_decide; f_equal/=; lia.
+  Qed.
+  Canonical Structure M_UR : ucmraT := discreteUR M M_ra_mixin M_ucmra_mixin.
+
+  Global Instance frag_persistent n : Persistent (Frag n).
+  Proof. by constructor. Qed.
+  Lemma auth_frag_valid j n : ✓ (Auth n ⋅ Frag j) → (j ≤ n)%nat.
+  Proof. simpl. case_decide. done. by intros []. Qed.
+  Lemma auth_frag_op (j n : nat) : (j ≤ n)%nat → Auth n = Auth n ⋅ Frag j.
+  Proof. intros. by rewrite /= decide_True. Qed.
+
+  Lemma M_update n : Auth n ~~> Auth (S n).
+  Proof.
+    apply cmra_discrete_update=>-[m|j|] /= ?; repeat case_decide; done || lia.
+  Qed.
+End M.
+
+Class counterG Σ := CounterG { counter_tokG :> inG Σ M_UR }.
+Definition counterΣ : gFunctors := #[GFunctor (constRF M_UR)].
+Instance subG_counterΣ {Σ} : subG counterΣ Σ → counterG Σ.
+Proof. intros [?%subG_inG _]%subG_inv. split; apply _. Qed.
+
+Section counter_proof.
+  Context `{!heapG Σ, !counterG Σ}.
+  Implicit Types l : loc.
+
+  Definition I (γ : gname) (l : loc) : iProp Σ :=
+    (∃ c : nat, l ↦ #c ∗ own γ (Auth c))%I.
+
+  Definition C (l : loc) (n : nat) : iProp Σ :=
+    (∃ N γ, inv N (I γ l) ∧ own γ (Frag n))%I.
+
+  (** The main proofs. *)
+  Global Instance C_persistent l n : PersistentP (C l n).
+  Proof. apply _. Qed.
+
+  Lemma newcounter_spec :
+    {{ True }} newcounter #() {{ v, ∃ l, ⌜v = #l⌝ ∧ C l 0 }}.
+  Proof.
+    iIntros "!# _ /=". rewrite -wp_fupd /newcounter /=. wp_seq. wp_alloc l as "Hl".
+    iMod (own_alloc (Auth 0)) as (γ) "Hγ"; first done.
+    rewrite (auth_frag_op 0 0) //; iDestruct "Hγ" as "[Hγ Hγf]".
+    set (N:= nroot .@ "counter").
+    iMod (inv_alloc N _ (I γ l) with "[Hl Hγ]") as "#?".
+    { iIntros "!>". iExists 0%nat. by iFrame. }
+    iModIntro. rewrite /C; eauto 10.
+  Qed.
+
+  Lemma incr_spec l n :
+    {{ C l n }} incr #l {{ v, ⌜v = #()⌝ ∧ C l (S n) }}.
+  Proof.
+    iIntros "!# Hl /=". iLöb as "IH". wp_rec.
+    iDestruct "Hl" as (N γ) "[#Hinv Hγf]".
+    wp_bind (! _)%E. iApply wp_inv_open; last iFrame "Hinv"; auto.
+    iDestruct 1 as (c) "[Hl Hγ]".
+    wp_load. iSplitL "Hl Hγ"; [iNext; iExists c; by iFrame|].
+    wp_let. wp_op.
+    wp_bind (CAS _ _ _). iApply wp_inv_open; last iFrame "Hinv"; auto.
+    iDestruct 1 as (c') ">[Hl Hγ]".
+    destruct (decide (c' = c)) as [->|].
+    - iCombine "Hγ" "Hγf" as "Hγ".
+      iDestruct (own_valid with "Hγ") as %?%auth_frag_valid; rewrite -auth_frag_op //.
+      iMod (own_update with "Hγ") as "Hγ"; first apply M_update.
+      rewrite (auth_frag_op (S n) (S c)); last lia; iDestruct "Hγ" as "[Hγ Hγf]".
+      wp_cas_suc. iSplitL "Hl Hγ".
+      { iNext. iExists (S c). rewrite Nat2Z.inj_succ Z.add_1_l. by iFrame. }
+      wp_if. rewrite {3}/C; eauto 10.
+    - wp_cas_fail; first (intros [=]; abstract omega).
+      iSplitL "Hl Hγ"; [iNext; iExists c'; by iFrame|].
+      wp_if. iApply ("IH" with "[Hγf]"). rewrite {3}/C; eauto 10.
+  Qed.
+
+  Lemma read_spec l n :
+    {{ C l n }} read #l {{ v, ∃ m : nat, ⌜v = #m ∧ n ≤ m⌝ ∧ C l m }}.
+  Proof.
+    iIntros "!# Hl /=". iDestruct "Hl" as (N γ) "[#Hinv Hγf]".
+    rewrite /read /=. wp_let. iApply wp_inv_open; last iFrame "Hinv"; auto.
+    iDestruct 1 as (c) "[Hl Hγ]". wp_load.
+    iDestruct (own_valid γ (Frag n ⋅ Auth c) with "[-]") as % ?%auth_frag_valid.
+    { iApply own_op. by iFrame. }
+    rewrite (auth_frag_op c c); last lia; iDestruct "Hγ" as "[Hγ Hγf']".
+    iSplitL "Hl Hγ"; [iNext; iExists c; by iFrame|].
+    rewrite /C; eauto 10 with omega.
+  Qed.
+End counter_proof.
-- 
GitLab