Skip to content
Snippets Groups Projects
Commit af5611c8 authored by Ralf Jung's avatar Ralf Jung
Browse files

Merge branch 'ci/ralf/atomic' into 'master'

Logically atomic triples: Notation, tactics, small example

See merge request FP/iris-coq!163
parents 0da8e832 9a672643
No related branches found
No related tags found
No related merge requests found
...@@ -20,6 +20,8 @@ Changes in and extensions of the theory: ...@@ -20,6 +20,8 @@ Changes in and extensions of the theory:
* [#] The Löb rule is now a derived rule; it follows from later-intro, later * [#] The Löb rule is now a derived rule; it follows from later-intro, later
being contractive and the fact that we can take fixpoints of contractive being contractive and the fact that we can take fixpoints of contractive
functions. functions.
* [#] Add atomic updates and logically atomic triples, including tactic support.
See `heap_lang/lib/increment.v` for an example.
Changes in Coq: Changes in Coq:
......
1 subgoal
Σ : gFunctors
heapG0 : heapG Σ
P : val → iProp Σ
============================
<<< ∀ x : val, P x >>> code @ ⊤ <<< ∃ y : val, P y, RET #() >>>
1 subgoal
Σ : gFunctors
heapG0 : heapG Σ
P : val → iProp Σ
Q : iPropI Σ
Φ : language.val heap_lang → iProp Σ
============================
_ : Q
"AU" : AU << ∀ x : val, P x >> @ ⊤, ∅ << ∃ y : val, P y, COMM Q -∗ Φ #() >>
--------------------------------------∗
WP code {{ v, Φ v }}
1 subgoal
Σ : gFunctors
heapG0 : heapG Σ
P : val → iProp Σ
Q : iPropI Σ
Φ : language.val heap_lang → iProp Σ
============================
_ : Q
_ : AACC << ∀ x : val, P x
ABORT AU << ∀ x : val, P x >> @ ⊤, ∅
<< ∃ y : val, P y, COMM Q -∗ Φ #() >> >> @ ⊤, ∅
<< ∃ y : val, P y, COMM Q -∗ Φ #() >>
--------------------------------------∗
WP code {{ v, Φ v }}
1 subgoal
Σ : gFunctors
heapG0 : heapG Σ
l : loc
============================
<<< ∀ x : val, l ↦ x >>> code @ ⊤ <<< l ↦ x, RET #() >>>
1 subgoal
Σ : gFunctors
heapG0 : heapG Σ
l : loc
Q : iPropI Σ
Φ : language.val heap_lang → iProp Σ
============================
_ : Q
"AU" : AU << ∀ x : val, l ↦ x >> @ ⊤, ∅ << l ↦ x, COMM Q -∗ Φ #() >>
--------------------------------------∗
WP code {{ v, Φ v }}
1 subgoal
Σ : gFunctors
heapG0 : heapG Σ
l : loc
Q : iPropI Σ
Φ : language.val heap_lang → iProp Σ
============================
_ : Q
_ : AACC << ∀ x : val, l ↦ x
ABORT AU << ∀ x : val, l ↦ x >> @ ⊤, ∅
<< l ↦ x, COMM Q -∗ Φ #() >> >> @ ⊤, ∅
<< l ↦ x, COMM Q -∗ Φ #() >>
--------------------------------------∗
WP code {{ v, Φ v }}
1 subgoal
Σ : gFunctors
heapG0 : heapG Σ
l : loc
============================
<<< l ↦ #() >>> code @ ⊤ <<< ∃ y : val, l ↦ y, RET #() >>>
1 subgoal
Σ : gFunctors
heapG0 : heapG Σ
l : loc
Q : iPropI Σ
Φ : language.val heap_lang → iProp Σ
============================
_ : Q
"AU" : AU << l ↦ #() >> @ ⊤, ∅ << ∃ y : val, l ↦ y, COMM Q -∗ Φ #() >>
--------------------------------------∗
WP code {{ v, Φ v }}
1 subgoal
Σ : gFunctors
heapG0 : heapG Σ
l : loc
Q : iPropI Σ
Φ : language.val heap_lang → iProp Σ
============================
_ : Q
_ : AACC << l ↦ #()
ABORT AU << l ↦ #() >> @ ⊤, ∅
<< ∃ y : val, l ↦ y, COMM Q -∗ Φ #() >> >> @ ⊤, ∅
<< ∃ y : val, l ↦ y, COMM Q -∗ Φ #() >>
--------------------------------------∗
WP code {{ v, Φ v }}
1 subgoal
Σ : gFunctors
heapG0 : heapG Σ
l : loc
============================
<<< l ↦ #() >>> code @ ⊤ <<< l ↦ #(), RET #() >>>
1 subgoal
Σ : gFunctors
heapG0 : heapG Σ
l : loc
Q : iPropI Σ
Φ : language.val heap_lang → iProp Σ
============================
_ : Q
"AU" : AU << l ↦ #() >> @ ⊤, ∅ << l ↦ #(), COMM Q -∗ Φ #() >>
--------------------------------------∗
WP code {{ v, Φ v }}
1 subgoal
Σ : gFunctors
heapG0 : heapG Σ
l : loc
Q : iPropI Σ
Φ : language.val heap_lang → iProp Σ
============================
_ : Q
_ : AACC << l ↦ #()
ABORT AU << l ↦ #() >> @ ⊤, ∅ << l ↦ #(), COMM Q -∗ Φ #() >> >>
@ ⊤, ∅ << l ↦ #(), COMM Q -∗ Φ #() >>
--------------------------------------∗
WP code {{ v, Φ v }}
"Now come the long pre/post tests"
: string
1 subgoal
Σ : gFunctors
heapG0 : heapG Σ
l : loc
============================
<<< ∀ x : val, l ↦ x ∗ l ↦ x >>> code @ ⊤ <<< ∃ y : val, l ↦ y, RET #() >>>
1 subgoal
Σ : gFunctors
heapG0 : heapG Σ
l : loc
Q : iPropI Σ
Φ : language.val heap_lang → iProp Σ
============================
_ : Q
"AU" : AU << ∀ x : val, l ↦ x ∗ l ↦ x >> @ ⊤, ∅
<< ∃ y : val, l ↦ y, COMM Q -∗ Φ #() >>
--------------------------------------∗
WP code {{ v, Φ v }}
1 subgoal
Σ : gFunctors
heapG0 : heapG Σ
l : loc
============================
<<< ∀ x : val, l ↦ x ∗ l ↦ x ∗ l ↦ x ∗ l ↦ x ∗ l ↦ x ∗ l ↦ x >>>
code @ ⊤
<<< ∃ y : val, l ↦ y, RET #() >>>
1 subgoal
Σ : gFunctors
heapG0 : heapG Σ
l : loc
Q : iPropI Σ
Φ : language.val heap_lang → iProp Σ
============================
_ : Q
"AU" : AU << ∀ x : val, l ↦ x ∗ l ↦ x ∗ l ↦ x ∗ l ↦ x ∗ l ↦ x ∗ l ↦ x >>
@ ⊤, ∅ << ∃ y : val, l ↦ y, COMM Q -∗ Φ #() >>
--------------------------------------∗
WP code {{ v, Φ v }}
1 subgoal
Σ : gFunctors
heapG0 : heapG Σ
l : loc
============================
<<< ∀ xx : val, l ↦ xx ∗ l ↦ xx ∗ l ↦ xx >>>
code @ ⊤
<<< ∃ yyyy : val, l ↦ yyyy ∗ l ↦ xx ∗ l ↦ xx ∗ l ↦ xx ∗ l ↦ xx ∗ l ↦ xx,
RET #() >>>
1 subgoal
Σ : gFunctors
heapG0 : heapG Σ
l : loc
Q : iPropI Σ
Φ : language.val heap_lang → iProp Σ
============================
_ : Q
_ : AU << ∀ xx : val, l ↦ xx ∗ l ↦ xx ∗ l ↦ xx >> @ ⊤, ∅
<< ∃ yyyy : val, l ↦ yyyy
∗ l ↦ xx ∗ l ↦ xx ∗ l ↦ xx ∗ l ↦ xx ∗ l ↦ xx,
COMM Q -∗ Φ #() >>
--------------------------------------∗
WP code {{ v, Φ v }}
1 subgoal
Σ : gFunctors
heapG0 : heapG Σ
l : loc
============================
<<< ∀ x : val, l ↦ x ∗ l ↦ x ∗ l ↦ x ∗ l ↦ x >>>
code @ ⊤
<<< l ↦ x, RET #() >>>
1 subgoal
Σ : gFunctors
heapG0 : heapG Σ
l : loc
Q : iPropI Σ
Φ : language.val heap_lang → iProp Σ
============================
_ : Q
"AU" : AU << ∀ x : val, l ↦ x ∗ l ↦ x ∗ l ↦ x ∗ l ↦ x >> @ ⊤, ∅
<< l ↦ x, COMM Q -∗ Φ #() >>
--------------------------------------∗
WP code {{ v, Φ v }}
1 subgoal
Σ : gFunctors
heapG0 : heapG Σ
l : loc
x : val
============================
<<< l ↦ x ∗ l ↦ x ∗ l ↦ x ∗ l ↦ x ∗ l ↦ x ∗ l ↦ x >>>
code @ ⊤
<<< ∃ y : val, l ↦ y, RET #() >>>
1 subgoal
Σ : gFunctors
heapG0 : heapG Σ
l : loc
x : val
Q : iPropI Σ
Φ : language.val heap_lang → iProp Σ
============================
_ : Q
"AU" : AU << l ↦ x ∗ l ↦ x ∗ l ↦ x ∗ l ↦ x ∗ l ↦ x ∗ l ↦ x >> @ ⊤, ∅
<< ∃ y : val, l ↦ y, COMM Q -∗ Φ #() >>
--------------------------------------∗
WP code {{ v, Φ v }}
1 subgoal
Σ : gFunctors
heapG0 : heapG Σ
l : loc
x : val
============================
<<< l ↦ x ∗ l ↦ x ∗ l ↦ x ∗ l ↦ x ∗ l ↦ x ∗ l ↦ x >>>
code @ ⊤
<<< l ↦ #(), RET #() >>>
1 subgoal
Σ : gFunctors
heapG0 : heapG Σ
l : loc
x : val
Q : iPropI Σ
Φ : language.val heap_lang → iProp Σ
============================
_ : Q
"AU" : AU << l ↦ x ∗ l ↦ x ∗ l ↦ x ∗ l ↦ x ∗ l ↦ x ∗ l ↦ x >> @ ⊤, ∅
<< l ↦ #(), COMM Q -∗ Φ #() >>
--------------------------------------∗
WP code {{ v, Φ v }}
1 subgoal
Σ : gFunctors
heapG0 : heapG Σ
l : loc
xx, yyyy : val
============================
<<< l ↦ xx ∗ l ↦ xx ∗ l ↦ xx ∗ l ↦ xx ∗ l ↦ xx ∗ l ↦ xx ∗ l ↦ xx >>>
code @ ⊤
<<< l ↦ yyyy, RET #() >>>
1 subgoal
Σ : gFunctors
heapG0 : heapG Σ
l : loc
xx, yyyy : val
Q : iPropI Σ
Φ : language.val heap_lang → iProp Σ
============================
_ : Q
"AU" : AU << l ↦ xx ∗ l ↦ xx ∗ l ↦ xx ∗ l ↦ xx ∗ l ↦ xx ∗ l ↦ xx ∗ l ↦ xx >>
@ ⊤, ∅ << l ↦ yyyy, COMM Q -∗ Φ #() >>
--------------------------------------∗
WP code {{ v, Φ v }}
1 subgoal
Σ : gFunctors
heapG0 : heapG Σ
l : loc
xx, yyyy : val
============================
<<< l ↦ xx ∗ l ↦ xx ∗ l ↦ xx >>>
code @ ⊤
<<< l ↦ yyyy ∗ l ↦ xx ∗ l ↦ xx ∗ l ↦ xx ∗ l ↦ xx ∗ l ↦ xx ∗ l ↦ xx,
RET #() >>>
1 subgoal
Σ : gFunctors
heapG0 : heapG Σ
l : loc
xx, yyyy : val
Q : iPropI Σ
Φ : language.val heap_lang → iProp Σ
============================
_ : Q
"AU" : AU << l ↦ xx ∗ l ↦ xx ∗ l ↦ xx >> @ ⊤, ∅
<< l ↦ yyyy ∗ l ↦ xx ∗ l ↦ xx ∗ l ↦ xx ∗ l ↦ xx ∗ l ↦ xx ∗ l ↦ xx,
COMM Q -∗ Φ #() >>
--------------------------------------∗
WP code {{ v, Φ v }}
From iris.heap_lang Require Export lifting notation.
From iris.program_logic Require Export atomic.
From iris.proofmode Require Import tactics.
From iris.heap_lang Require Import proofmode notation.
Set Default Proof Using "Type".
(* Test if AWP and the AU obtained from AWP print. *)
Section printing.
Context `{!heapG Σ}.
Definition code : expr := #().
Lemma print_both_quant (P : val iProp Σ) :
<<< x, P x >>> code @ <<< y, P y, RET #() >>>.
Proof.
Show. iIntros (Q Φ) "? AU". Show.
iPoseProof (aupd_aacc with "AU") as "?". Show.
Abort.
Lemma print_first_quant l :
<<< x, l x >>> code @ <<< l x, RET #() >>>.
Proof.
Show. iIntros (Q Φ) "? AU". Show.
iPoseProof (aupd_aacc with "AU") as "?". Show.
Abort.
Lemma print_second_quant l :
<<< l #() >>> code @ <<< y, l y, RET #() >>>.
Proof.
Show. iIntros (Q Φ) "? AU". Show.
iPoseProof (aupd_aacc with "AU") as "?". Show.
Abort.
Lemma print_no_quant l :
<<< l #() >>> code @ <<< l #(), RET #() >>>.
Proof.
Show. iIntros (Q Φ) "? AU". Show.
iPoseProof (aupd_aacc with "AU") as "?". Show.
Abort.
Check "Now come the long pre/post tests".
Lemma print_both_quant_long l :
<<< x, l x l x >>> code @ <<< y, l y, RET #() >>>.
Proof.
Show. iIntros (Q Φ) "? AU". Show.
Abort.
Lemma print_both_quant_longpre l :
<<< x, l x l x l x l x l x l x >>> code @ <<< y, l y, RET #() >>>.
Proof.
Show. iIntros (Q Φ) "? AU". Show.
Abort.
Lemma print_both_quant_longpost l :
<<< xx, l xx l xx l xx >>> code @ <<< yyyy, l yyyy l xx l xx l xx l xx l xx, RET #() >>>.
Proof.
Show. iIntros (Q Φ) "? ?". Show.
Abort.
Lemma print_first_quant_long l :
<<< x, l x l x l x l x >>> code @ <<< l x, RET #() >>>.
Proof.
Show. iIntros (Q Φ) "? AU". Show.
Abort.
Lemma print_second_quant_long l x :
<<< l x l x l x l x l x l x >>> code @ <<< y, l y, RET #() >>>.
Proof.
Show. iIntros (Q Φ) "? AU". Show.
Abort.
Lemma print_no_quant_long l x :
<<< l x l x l x l x l x l x >>> code @ <<< l #(), RET #() >>>.
Proof.
Show. iIntros (Q Φ) "? AU". Show.
Abort.
Lemma print_no_quant_longpre l xx yyyy :
<<< l xx l xx l xx l xx l xx l xx l xx >>> code @ <<< l yyyy, RET #() >>>.
Proof.
Show. iIntros (Q Φ) "? AU". Show.
Abort.
Lemma print_no_quant_longpost l xx yyyy :
<<< l xx l xx l xx >>> code @ <<< l yyyy l xx l xx l xx l xx l xx l xx, RET #() >>>.
Proof.
Show. iIntros (Q Φ) "? AU". Show.
Abort.
End printing.
This diff is collapsed.
...@@ -14,6 +14,9 @@ Section instances. ...@@ -14,6 +14,9 @@ Section instances.
Implicit Types P : PROP. Implicit Types P : PROP.
Implicit Types Ps : list PROP. Implicit Types Ps : list PROP.
Global Instance laterable_proper : Proper ((⊣⊢) ==> ()) (@Laterable PROP).
Proof. solve_proper. Qed.
Global Instance later_laterable P : Laterable ( P). Global Instance later_laterable P : Laterable ( P).
Proof. Proof.
rewrite /Laterable. iIntros "HP". iExists P. iFrame. rewrite /Laterable. iIntros "HP". iExists P. iFrame.
...@@ -57,4 +60,44 @@ Section instances. ...@@ -57,4 +60,44 @@ Section instances.
TCForall Laterable Ps TCForall Laterable Ps
Laterable ([] Ps). Laterable ([] Ps).
Proof. induction 2; simpl; apply _. Qed. Proof. induction 2; simpl; apply _. Qed.
(* A wrapper to obtain a weaker, laterable form of any assertion. *)
Definition make_laterable (Q : PROP) : PROP :=
( P, P ( P -∗ Q))%I.
Global Instance make_laterable_ne : NonExpansive make_laterable.
Proof. solve_proper. Qed.
Global Instance make_laterable_proper : Proper (() ==> ()) make_laterable := ne_proper _.
Lemma make_laterable_wand Q1 Q2 :
(Q1 -∗ Q2) -∗ (make_laterable Q1 -∗ make_laterable Q2).
Proof.
iIntros "#HQ HQ1". iDestruct "HQ1" as (P) "[HP #HQ1]".
iExists P. iFrame. iIntros "!# HP". iApply "HQ". iApply "HQ1". done.
Qed.
Global Instance make_laterable_laterable Q :
Laterable (make_laterable Q).
Proof.
rewrite /Laterable. iIntros "HQ". iDestruct "HQ" as (P) "[HP #HQ]".
iExists P. iFrame. iIntros "!# HP !>". iExists P. by iFrame.
Qed.
Lemma make_laterable_elim Q :
make_laterable Q -∗ Q.
Proof.
iIntros "HQ". iDestruct "HQ" as (P) "[HP #HQ]". by iApply "HQ".
Qed.
Lemma make_laterable_intro P Q :
Laterable P
( P -∗ Q) -∗ P -∗ make_laterable Q.
Proof.
iIntros (?) "#HQ HP".
iDestruct (laterable with "HP") as (P') "[HP' #HPi]". iExists P'.
iFrame. iIntros "!# HP'". iApply "HQ". iApply "HPi". done.
Qed.
End instances. End instances.
Typeclasses Opaque make_laterable.
From iris.heap_lang Require Export lifting notation. From iris.heap_lang Require Export lifting notation.
From iris.base_logic.lib Require Export invariants.
From iris.program_logic Require Export atomic. From iris.program_logic Require Export atomic.
From iris.proofmode Require Import tactics. From iris.proofmode Require Import tactics.
From iris.heap_lang Require Import proofmode notation. From iris.heap_lang Require Import proofmode notation.
From iris.bi.lib Require Import fractional.
Set Default Proof Using "Type". Set Default Proof Using "Type".
(** A general logically atomic interface for a heap. *) (** A general logically atomic interface for a heap. *)
Structure atomic_heap {Σ} `{!heapG Σ} := AtomicHeap { Class atomic_heap {Σ} `{!heapG Σ} := AtomicHeap {
(* -- operations -- *) (* -- operations -- *)
alloc : val; alloc : val;
load : val; load : val;
store : val; store : val;
cas : val; cas : val;
(* -- predicates -- *) (* -- predicates -- *)
(* name is used to associate locked with is_lock *)
mapsto (l : loc) (q: Qp) (v : val) : iProp Σ; mapsto (l : loc) (q: Qp) (v : val) : iProp Σ;
(* -- general properties -- *) (* -- mapsto properties -- *)
mapsto_timeless l q v : Timeless (mapsto l q v); mapsto_timeless l q v :> Timeless (mapsto l q v);
mapsto_fractional l v :> Fractional (λ q, mapsto l q v);
mapsto_as_fractional l q v :>
AsFractional (mapsto l q v) (λ q, mapsto l q v) q;
mapsto_agree l q1 q2 v1 v2 :> mapsto l q1 v1 -∗ mapsto l q2 v2 -∗ v1 = v2;
(* -- operation specs -- *) (* -- operation specs -- *)
alloc_spec v : alloc_spec e v :
{{{ True }}} alloc v {{{ l, RET #l; mapsto l 1 v }}}; IntoVal e v {{{ True }}} alloc e {{{ l, RET #l; mapsto l 1 v }}};
load_spec (l : loc) : load_spec (l : loc) :
atomic_wp (load #l)%E <<< (v : val) q, mapsto l q v >>> load #l @ <<< mapsto l q v, RET v >>>;
(λ '(v, q), mapsto l q v)
(λ '(v, q) (_:()), mapsto l q v)
(λ '(v, q) _, v);
store_spec (l : loc) (e : expr) (w : val) : store_spec (l : loc) (e : expr) (w : val) :
IntoVal e w IntoVal e w
atomic_wp (store (#l, e))%E <<< v, mapsto l 1 v >>> store (#l, e) @
<<< mapsto l 1 w, RET #() >>>;
(λ v, mapsto l 1 v)
(λ v (_:()), mapsto l 1 w)
(λ _ _, #()%V);
(* This spec is slightly weaker than it could be: It is sufficient for [w1] (* This spec is slightly weaker than it could be: It is sufficient for [w1]
*or* [v] to be unboxed. However, by writing it this way the [val_is_unboxed] *or* [v] to be unboxed. However, by writing it this way the [val_is_unboxed]
is outside the atomic triple, which makes it much easier to use -- and the is outside the atomic triple, which makes it much easier to use -- and the
spec is still good enough for all our applications. *) spec is still good enough for all our applications. *)
cas_spec (l : loc) (e1 e2 : expr) (w1 w2 : val) : cas_spec (l : loc) (e1 e2 : expr) (w1 w2 : val) :
IntoVal e1 w1 IntoVal e2 w2 val_is_unboxed w1 IntoVal e1 w1 IntoVal e2 w2 val_is_unboxed w1
atomic_wp (cas (#l, e1, e2))%E <<< v, mapsto l 1 v >>> cas (#l, e1, e2) @
<<< if decide (v = w1) then mapsto l 1 w2 else mapsto l 1 v,
(λ v, mapsto l 1 v)%I RET #(if decide (v = w1) then true else false) >>>;
(λ v (_:()), if decide (v = w1) then mapsto l 1 w2 else mapsto l 1 v)
(λ v _, #(if decide (v = w1) then true else false)%V);
}. }.
Arguments atomic_heap _ {_}. Arguments atomic_heap _ {_}.
(** Notation for heap primitives, in a module so you can import it separately. *)
Module notation.
Notation "l ↦{ q } v" := (mapsto l q v)
(at level 20, q at level 50, format "l ↦{ q } v") : bi_scope.
Notation "l ↦ v" := (mapsto l 1 v) (at level 20) : bi_scope.
Notation "l ↦{ q } -" := ( v, l {q} v)%I
(at level 20, q at level 50, format "l ↦{ q } -") : bi_scope.
Notation "l ↦ -" := (l {1} -)%I (at level 20) : bi_scope.
Notation "'ref' e" := (alloc e) : expr_scope.
Notation "! e" := (load e) : expr_scope.
Notation "e1 <- e2" := (store (e1, e2)%E) : expr_scope.
Notation CAS e1 e2 e3 := (cas (e1, e2, e3)%E).
End notation.
(** Proof that the primitive physical operations of heap_lang satisfy said interface. *) (** Proof that the primitive physical operations of heap_lang satisfy said interface. *)
Definition primitive_alloc : val := Definition primitive_alloc : val :=
λ: "v", ref "v". λ: "v", ref "v".
...@@ -60,54 +72,50 @@ Definition primitive_cas : val := ...@@ -60,54 +72,50 @@ Definition primitive_cas : val :=
Section proof. Section proof.
Context `{!heapG Σ}. Context `{!heapG Σ}.
Lemma primitive_alloc_spec v : Lemma primitive_alloc_spec e v :
{{{ True }}} primitive_alloc v {{{ l, RET #l; l v }}}. IntoVal e v {{{ True }}} primitive_alloc e {{{ l, RET #l; l v }}}.
Proof. Proof.
iIntros (Φ) "_ HΦ". wp_let. wp_alloc l. iApply "HΦ". done. iIntros (<- Φ) "_ HΦ". wp_let. wp_alloc l. iApply "HΦ". done.
Qed. Qed.
Lemma primitive_load_spec (l : loc) : Lemma primitive_load_spec (l : loc) :
atomic_wp (primitive_load #l)%E <<< (v : val) q, l {q} v >>> primitive_load #l @
<<< l {q} v, RET v >>>.
(λ '(v, q), l {q} v)%I
(λ '(v, q) (_:()), l {q} v)%I
(λ '(v, q) _, v).
Proof. Proof.
iIntros (Q Φ) "? AU". wp_let. iIntros (Q Φ) "? AU". wp_let.
iMod (aupd_acc with "AU") as ((v, q)) "[H↦ [_ Hclose]]"; first solve_ndisj. iMod "AU" as (v q) "[H↦ [_ Hclose]]".
wp_load. iMod ("Hclose" $! () with "H↦") as "HΦ". by iApply "HΦ". wp_load. iMod ("Hclose" with "H↦") as "HΦ". by iApply "HΦ".
Qed. Qed.
Lemma primitive_store_spec (l : loc) (e : expr) (w : val) : Lemma primitive_store_spec (l : loc) (e : expr) (w : val) :
IntoVal e w IntoVal e w
atomic_wp (primitive_store (#l, e))%E <<< v, l v >>> primitive_store (#l, e) @
<<< l w, RET #() >>>.
(λ v, l v)%I
(λ v (_:()), l w)%I
(λ _ _, #()%V).
Proof. Proof.
iIntros (<- Q Φ) "? AU". wp_let. wp_proj. wp_proj. iIntros (<- Q Φ) "? AU". wp_let. wp_proj. wp_proj.
iMod (aupd_acc with "AU") as (v) "[H↦ [_ Hclose]]"; first solve_ndisj. iMod "AU" as (v) "[H↦ [_ Hclose]]".
wp_store. iMod ("Hclose" $! () with "H↦") as "HΦ". by iApply "HΦ". wp_store. iMod ("Hclose" with "H↦") as "HΦ". by iApply "HΦ".
Qed. Qed.
Lemma primitive_cas_spec (l : loc) e1 e2 (w1 w2 : val) : Lemma primitive_cas_spec (l : loc) e1 e2 (w1 w2 : val) :
IntoVal e1 w1 IntoVal e2 w2 val_is_unboxed w1 IntoVal e1 w1 IntoVal e2 w2 val_is_unboxed w1
atomic_wp (primitive_cas (#l, e1, e2))%E <<< (v : val), l v >>>
primitive_cas (#l, e1, e2) @
(λ v, l v)%I <<< if decide (v = w1) then l w2 else l v,
(λ v (_:()), if decide (v = w1) then l w2 else l v)%I RET #(if decide (v = w1) then true else false) >>>.
(λ v _, #(if decide (v = w1) then true else false)%V).
Proof. Proof.
iIntros (<- <- ? Q Φ) "? AU". wp_let. repeat wp_proj. iIntros (<- <- ? Q Φ) "? AU". wp_let. repeat wp_proj.
iMod (aupd_acc with "AU") as (v) "[H↦ [_ Hclose]]"; first solve_ndisj. iMod "AU" as (v) "[H↦ [_ Hclose]]".
destruct (decide (v = w1)) as [<-|Hv]; [wp_cas_suc|wp_cas_fail]; destruct (decide (v = w1)) as [<-|Hv]; [wp_cas_suc|wp_cas_fail];
iMod ("Hclose" $! () with "H↦") as "HΦ"; by iApply "HΦ". iMod ("Hclose" with "H↦") as "HΦ"; by iApply "HΦ".
Qed. Qed.
Definition primitive_atomic_heap : atomic_heap Σ :=
{| alloc_spec := primitive_alloc_spec;
load_spec := primitive_load_spec;
store_spec := primitive_store_spec;
cas_spec := primitive_cas_spec |}.
End proof. End proof.
(* NOT an instance because users should choose explicitly to use it
(using [Explicit Instance]). *)
Definition primitive_atomic_heap `{!heapG Σ} : atomic_heap Σ :=
{| alloc_spec := primitive_alloc_spec;
load_spec := primitive_load_spec;
store_spec := primitive_store_spec;
cas_spec := primitive_cas_spec;
mapsto_agree := gen_heap.mapsto_agree |}.
...@@ -2,51 +2,73 @@ From iris.base_logic.lib Require Export invariants. ...@@ -2,51 +2,73 @@ From iris.base_logic.lib Require Export invariants.
From iris.program_logic Require Export atomic. From iris.program_logic Require Export atomic.
From iris.proofmode Require Import tactics. From iris.proofmode Require Import tactics.
From iris.heap_lang Require Import proofmode notation atomic_heap par. From iris.heap_lang Require Import proofmode notation atomic_heap par.
From iris.bi.lib Require Import fractional.
Set Default Proof Using "Type". Set Default Proof Using "Type".
(** Show that implementing fetch-and-add on top of CAS preserves logical (** Show that implementing fetch-and-add on top of CAS preserves logical
atomicity. *) atomicity. *)
(* TODO: Move this to iris-examples once gen_proofmode is merged. *)
Section increment. Section increment.
Context `{!heapG Σ} (aheap: atomic_heap Σ). Context `{!heapG Σ} {aheap: atomic_heap Σ}.
Import atomic_heap.notation.
Definition incr: val := Definition incr: val :=
rec: "incr" "l" := rec: "incr" "l" :=
let: "oldv" := aheap.(load) "l" in let: "oldv" := !"l" in
if: aheap.(cas) ("l", "oldv", ("oldv" + #1)) if: CAS "l" "oldv" ("oldv" + #1)
then "oldv" (* return old value if success *) then "oldv" (* return old value if success *)
else "incr" "l". else "incr" "l".
Lemma incr_spec (l: loc) : Lemma incr_spec (l: loc) :
atomic_wp (incr #l) <<< (v : Z), l #v >>> incr #l @ <<< l #(v + 1), RET #v >>>.
(λ (v: Z), aheap.(mapsto) l 1 #v)%I
(λ v (_:()), aheap.(mapsto) l 1 #(v + 1))%I
(λ v _, #v).
Proof. Proof.
iIntros (Q Φ) "HQ AU". iLöb as "IH". wp_let. iApply wp_atomic_intro. iIntros (Φ) "AU". iLöb as "IH". wp_let.
wp_apply (load_spec with "[HQ]"); first by iAccu. wp_apply load_spec; first by iAccu.
(* Prove the atomic shift for load *) (* Prove the atomic update for load *)
iAuIntro. iApply (aacc_aupd_abort with "AU"); first done. iAuIntro. iApply (aacc_aupd_abort with "AU"); first done.
iIntros (x) "H↦". iIntros (x) "H↦". iAaccIntro with "H↦"; first by eauto with iFrame.
iApply (aacc_intro (_, _) with "[H↦]"); [solve_ndisj|done|iSplit]. iIntros "$ !> AU !> _".
{ iIntros "$ !> $ !> //". }
iIntros ([]) "$ !> AU !> HQ".
(* Now go on *) (* Now go on *)
wp_let. wp_op. wp_bind (aheap.(cas) _)%I. wp_let. wp_op. wp_bind (CAS _ _ _)%I.
wp_apply (cas_spec with "[HQ]"); first done; first by iAccu. wp_apply cas_spec; [done|iAccu|].
(* Prove the atomic shift for CAS *) (* Prove the atomic update for CAS *)
iAuIntro. iApply (aacc_aupd with "AU"); first done. iAuIntro. iApply (aacc_aupd with "AU"); first done.
iIntros (x') "H↦". iIntros (x') "H↦". iAaccIntro with "H↦"; first by eauto with iFrame.
iApply (aacc_intro with "[H↦]"); [solve_ndisj|done|iSplit]. iIntros "H↦ !>".
{ eauto 10 with iFrame. }
iIntros ([]) "H↦ !>".
destruct (decide (#x' = #x)) as [[= ->]|Hx]. destruct (decide (#x' = #x)) as [[= ->]|Hx].
- iRight. iExists (). iFrame. iIntros "HΦ !> HQ". - iRight. iFrame. iIntros "HΦ !> _".
wp_if. by iApply "HΦ". wp_if. by iApply "HΦ".
- iLeft. iFrame. iIntros "AU !> HQ". - iLeft. iFrame. iIntros "AU !> _".
wp_if. iApply ("IH" with "HQ"). done. wp_if. iApply "IH". done.
Qed.
Definition weak_incr: val :=
rec: "weak_incr" "l" :=
let: "oldv" := !"l" in
"l" <- ("oldv" + #1);;
"oldv" (* return old value *).
(* TODO: Generalize to q and 1-q, based on some theory for a "maybe-mapsto"
connective that works on [option Qp] (the type of 1-q). *)
Lemma weak_incr_spec (l: loc) (v : Z) :
l {1/2} #v -∗
<<< (v' : Z), l {1/2} #v' >>>
weak_incr #l @
<<< v = v' l #(v + 1), RET #v >>>.
Proof.
iIntros "Hl". iApply wp_atomic_intro. iIntros (Φ) "AU". wp_let.
wp_apply (atomic_wp_seq $! (load_spec _) with "Hl").
iIntros "Hl". wp_let. wp_op.
wp_apply store_spec; first by iAccu.
(* Prove the atomic update for store *)
iAuIntro. iApply (aacc_aupd_commit with "AU"); first done.
iIntros (x) "H↦".
iDestruct (mapsto_agree with "Hl H↦") as %[= <-].
iCombine "Hl" "H↦" as "Hl". iAaccIntro with "Hl".
{ iIntros "[$ $]"; eauto. }
iIntros "$ !>". iSplit; first done.
iIntros "HΦ !> _". wp_seq. done.
Qed. Qed.
End increment. End increment.
...@@ -54,10 +76,12 @@ End increment. ...@@ -54,10 +76,12 @@ End increment.
Section increment_client. Section increment_client.
Context `{!heapG Σ, !spawnG Σ}. Context `{!heapG Σ, !spawnG Σ}.
Existing Instance primitive_atomic_heap.
Definition incr_client : val := Definition incr_client : val :=
λ: "x", λ: "x",
let: "l" := ref "x" in let: "l" := ref "x" in
incr primitive_atomic_heap "l" ||| incr primitive_atomic_heap "l". incr "l" ||| incr "l".
Lemma incr_client_safe (x: Z): Lemma incr_client_safe (x: Z):
WP incr_client #x {{ _, True }}%I. WP incr_client #x {{ _, True }}%I.
...@@ -66,12 +90,10 @@ Section increment_client. ...@@ -66,12 +90,10 @@ Section increment_client.
iMod (inv_alloc nroot _ (x':Z, l #x')%I with "[Hl]") as "#Hinv"; first eauto. iMod (inv_alloc nroot _ (x':Z, l #x')%I with "[Hl]") as "#Hinv"; first eauto.
(* FIXME: I am only using persistent stuff, so I should be allowed (* FIXME: I am only using persistent stuff, so I should be allowed
to move this to the persisten context even without the additional □. *) to move this to the persisten context even without the additional □. *)
iAssert ( WP incr primitive_atomic_heap #l {{ _, True }})%I as "#Aupd". iAssert ( WP incr #l {{ _, True }})%I as "#Aupd".
{ iAlways. wp_apply (incr_spec with "[]"); first by iAccu. clear x. { iAlways. wp_apply incr_spec; first by iAccu. clear x.
iAuIntro. iInv nroot as (x) ">H↦". iAuIntro. iInv nroot as (x) ">H↦". iAaccIntro with "H↦"; first by eauto 10.
iApply (aacc_intro with "[H↦]"); [solve_ndisj|done|iSplit]. iIntros "H↦ !>". iSplitL "H↦"; first by eauto 10.
{ by eauto 10. }
iIntros ([]) "H↦ !>". iSplitL "H↦"; first by eauto 10.
(* The continuation: From after the atomic triple to the postcondition of the WP *) (* The continuation: From after the atomic triple to the postcondition of the WP *)
done. done.
} }
......
From stdpp Require Import namespaces.
From iris.program_logic Require Export weakestpre. From iris.program_logic Require Export weakestpre.
From iris.proofmode Require Import tactics classes. From iris.proofmode Require Import tactics classes.
From iris.bi.lib Require Export atomic. From iris.bi.lib Require Export atomic.
From iris.bi Require Import telescopes.
Set Default Proof Using "Type". Set Default Proof Using "Type".
Definition atomic_wp `{irisG Λ Σ} {A B : Type} (* This hard-codes the inner mask to be empty, because we have yet to find an
example where we want it to be anything else. *)
Definition atomic_wp `{irisG Λ Σ} {TA TB : tele}
(e: expr Λ) (* expression *) (e: expr Λ) (* expression *)
(Eo Em : coPset) (* outside/module masks *) (Eo : coPset) (* (outer) mask *)
(α: A iProp Σ) (* atomic pre-condition *) (α: TA iProp Σ) (* atomic pre-condition *)
(β: A B iProp Σ) (* atomic post-condition *) (β: TA TB iProp Σ) (* atomic post-condition *)
(f: A B val Λ) (* Turn the return data into the return value *) (f: TA TB val Λ) (* Turn the return data into the return value *)
: iProp Σ := : iProp Σ :=
( Q Φ, Q -∗ atomic_update Eo Em α β (λ x y, Q -∗ Φ (f x y)) -∗ ( Q (Φ : val Λ iProp Σ), Q -∗
atomic_update Eo α β (λ.. x y, Q -∗ Φ (f x y)) -∗
WP e {{ Φ }})%I. WP e {{ Φ }})%I.
(* Note: To add a private postcondition, use (* Note: To add a private postcondition, use
atomic_update α β Eo Em (λ x y, POST x y -∗ Φ (f x y)) *) atomic_update α β Eo Ei (λ x y, POST x y -∗ Φ (f x y)) *)
Notation "'<<<' ∀ x1 .. xn , α '>>>' e @ Eo '<<<' ∃ y1 .. yn , β , 'RET' v '>>>'" :=
(atomic_wp (TA:=TeleS (λ x1, .. (TeleS (λ xn, TeleO)) .. ))
(TB:=TeleS (λ y1, .. (TeleS (λ yn, TeleO)) .. ))
e%E
Eo
(tele_app (TT:=TeleS (λ x1, .. (TeleS (λ xn, TeleO)) .. )) $
λ x1, .. (λ xn, α%I) ..)
(tele_app (TT:=TeleS (λ x1, .. (TeleS (λ xn, TeleO)) .. )) $
λ x1, .. (λ xn,
tele_app (TT:=TeleS (λ y1, .. (TeleS (λ yn, TeleO)) .. ))
(λ y1, .. (λ yn, β%I) .. )
) .. )
(tele_app (TT:=TeleS (λ x1, .. (TeleS (λ xn, TeleO)) .. )) $
λ x1, .. (λ xn,
tele_app (TT:=TeleS (λ y1, .. (TeleS (λ yn, TeleO)) .. ))
(λ y1, .. (λ yn, v%V) .. )
) .. )
)
(at level 20, Eo, α, β, v at level 200, x1 binder, xn binder, y1 binder, yn binder,
format "'[hv' '<<<' ∀ x1 .. xn , α '>>>' '/ ' e @ Eo '/' '[ ' '<<<' ∃ y1 .. yn , β , '/' 'RET' v '>>>' ']' ']'")
: stdpp_scope.
Notation "'<<<' ∀ x1 .. xn , α '>>>' e @ Eo '<<<' β , 'RET' v '>>>'" :=
(atomic_wp (TA:=TeleS (λ x1, .. (TeleS (λ xn, TeleO)) .. ))
(TB:=TeleO)
e%E
Eo
(tele_app (TT:=TeleS (λ x1, .. (TeleS (λ xn, TeleO)) .. )) $
λ x1, .. (λ xn, α%I) ..)
(tele_app (TT:=TeleS (λ x1, .. (TeleS (λ xn, TeleO)) .. )) $
λ x1, .. (λ xn,
tele_app (TT:=TeleO) β%I
) .. )
(tele_app (TT:=TeleS (λ x1, .. (TeleS (λ xn, TeleO)) .. )) $
λ x1, .. (λ xn,
tele_app (TT:=TeleO) v%V
) .. )
)
(at level 20, Eo, α, β, v at level 200, x1 binder, xn binder,
format "'[hv' '<<<' ∀ x1 .. xn , α '>>>' '/ ' e @ Eo '/' '[ ' '<<<' β , '/' 'RET' v '>>>' ']' ']'")
: stdpp_scope.
Notation "'<<<' α '>>>' e @ Eo '<<<' ∃ y1 .. yn , β , 'RET' v '>>>'" :=
(atomic_wp (TA:=TeleO)
(TB:=TeleS (λ y1, .. (TeleS (λ yn, TeleO)) .. ))
e%E
Eo
(tele_app (TT:=TeleO) α%I)
(tele_app (TT:=TeleO) $
tele_app (TT:=TeleS (λ y1, .. (TeleS (λ yn, TeleO)) .. ))
(λ y1, .. (λ yn, β%I) .. ))
(tele_app (TT:=TeleO) $
tele_app (TT:=TeleS (λ y1, .. (TeleS (λ yn, TeleO)) .. ))
(λ y1, .. (λ yn, v%V) .. ))
)
(at level 20, Eo, α, β, v at level 200, y1 binder, yn binder,
format "'[hv' '<<<' α '>>>' '/ ' e @ Eo '/' '[ ' '<<<' ∃ y1 .. yn , β , '/' 'RET' v '>>>' ']' ']'")
: stdpp_scope.
Notation "'<<<' α '>>>' e @ Eo '<<<' β , 'RET' v '>>>'" :=
(atomic_wp (TA:=TeleO)
(TB:=TeleO)
e%E
Eo
(tele_app (TT:=TeleO) α%I)
(tele_app (TT:=TeleO) $ tele_app (TT:=TeleO) β%I)
(tele_app (TT:=TeleO) $ tele_app (TT:=TeleO) v%V)
)
(at level 20, Eo, α, β, v at level 200,
format "'[hv' '<<<' α '>>>' '/ ' e @ Eo '/' '[ ' '<<<' β , '/' 'RET' v '>>>' ']' ']'")
: stdpp_scope.
(** Theory *)
Section lemmas.
Context `{irisG Λ Σ} {TA TB : tele}.
Notation iProp := (iProp Σ).
Implicit Types (α : TA iProp) (β : TA TB iProp) (f : TA TB val Λ).
Lemma atomic_wp_seq e Eo α β f {HL : .. x, Laterable (α x)} :
atomic_wp e Eo α β f -∗
Φ, .. x, α x -∗ (.. y, β x y -∗ Φ (f x y)) -∗ WP e {{ Φ }}.
Proof.
rewrite ->tforall_forall in HL.
iIntros "Hwp" (Φ x) "Hα HΦ". iApply ("Hwp" with "[HΦ]"); first iAccu.
iAuIntro. iAaccIntro with "Hα"; first by eauto. iIntros (y) "Hβ !>".
(* FIXME: Using ssreflect rewrite does not work, see Coq bug #7773. *)
rewrite ->!tele_app_bind. iIntros "HΦ". iApply "HΦ". done.
Qed.
(* Sequential triples with a persistent precondition and no initial quantifier
are atomic. *)
Lemma seq_wp_atomic e Eo (α : [tele] iProp) (β : [tele] TB iProp)
(f : [tele] TB val Λ) {HP : .. x, Persistent (α x)} :
( Φ, .. x, α x -∗ (.. y, β x y -∗ Φ (f x y)) -∗ WP e {{ Φ }}) -∗
atomic_wp e Eo α β f.
Proof.
simpl in HP. iIntros "Hwp" (Q Φ) "HQ HΦ". iApply fupd_wp.
iMod ("HΦ") as "[#Hα [Hclose _]]". iMod ("Hclose" with "Hα") as "HΦ".
iApply wp_fupd. iApply ("Hwp" with "Hα"). iIntros "!>" (y) "Hβ".
iMod ("HΦ") as "[_ [_ Hclose]]". iMod ("Hclose" with "Hβ") as "HΦ".
(* FIXME: Using ssreflect rewrite does not work, see Coq bug #7773. *)
rewrite ->!tele_app_bind. iApply "HΦ". done.
Qed.
(* Way to prove an atomic triple without seeing the Q *)
Lemma wp_atomic_intro e Eo α β f :
( (Φ : val Λ iProp),
atomic_update Eo α β (λ.. x y, Φ (f x y)) -∗
WP e {{ Φ }}) -∗
atomic_wp e Eo α β f.
Proof.
iIntros "Hwp" (Q Φ) "HQ AU". iApply (wp_wand with "[-HQ]").
{ iApply ("Hwp" $! (λ v, Q -∗ Φ v)%I). done. }
iIntros (v) "HΦ". iApply "HΦ". done.
Qed.
End lemmas.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment