intensional.examples.well_bracketed

From iris.proofmode Require Import tactics.
From iris.algebra Require Import auth excl.
From iris.base_logic.lib Require Import invariants.
From intensional.heap_lang Require Import lifting proofmode notation.
From intensional.heap_lang Require Import adequacy.
From intensional.examples Require Import stdpp_extra tactics.
Set Default Proof Using "Type".
Implicit Types t : list val.

Enforcing a well-bracketing protocol (Section 5.3)

Separation logic specification


Definition withRes_spec `{!heapG Σ} (locked: iProp Σ) (unlocked: val → iProp Σ) (withRes: val): iProp Σ :=
  ∀ P Q (f: val),
    {{{ locked ∗ P ∗
        (∀ (y x: val), {{{ unlocked y ∗ P }}} f x {{{ RET #(); unlocked y ∗ Q }}})
    }}}
      withRes f
    {{{ RET #(); locked ∗ Q }}}.

Definition op_spec `{!heapG Σ} (locked: iProp Σ) (unlocked: val → iProp Σ) (op: val): iProp Σ :=
  ∀ (x y: val), {{{ unlocked y }}} op x {{{ RET #(); unlocked y }}}.

Definition bfilelib_spec `{!heapG Σ} (P0: iProp Σ) (lib: val): iProp Σ :=
  ∃ (locked: iProp Σ) (unlocked: val → iProp Σ),
  □ (P0 -∗ locked) ∗
  match lib with
  | (withRes, op)%V =>
    withRes_spec locked unlocked withRes ∗
    op_spec locked unlocked op
  | _ => False
  end.

The trace property bfile_trace: "clients only use the operation after having acquired the resource and before releasing it".


Section Trace.

Inductive op_trace : list val → Prop :=
| op_trace_nil : op_trace []
| op_trace_call t :
    op_trace t →
    op_trace (t ++ [(#"call:op", #())%V; (#"ret:op", #())%V]).

Inductive withRes_trace : list val → Prop :=
| withRes_trace_nil : withRes_trace []
| withRes_trace_call t t_op f :
    withRes_trace t →
    op_trace t_op →
    withRes_trace (t ++ [(#"call:withRes", f)%V; (#"call", f)%V]
                     ++ t_op
                     ++ [(#"ret", f)%V; (#"ret:withRes", f)%V]).

Definition bfile_trace t :=
  ∃ t', t `prefix_of` t' ∧ withRes_trace t'.

(* *** *)

Definition trace1 t f :=
  ∃ t', withRes_trace t' ∧ t = t' ++ [(#"call:withRes", f)%V].

Definition trace2 t (f:val) :=
  ∃ t' t_op, withRes_trace t'
           ∧ op_trace t_op
           ∧ t = t' ++ [(#"call:withRes", f)%V; (#"call", f)%V] ++ t_op.

Definition trace3 t (f: val) :=
  ∃ t' t_op, withRes_trace t'
           ∧ op_trace t_op
           ∧ t = t' ++ [(#"call:withRes", f)%V; (#"call", f)%V]
                    ++ t_op
                    ++ [(#"ret", f)%V].

End Trace.

Definition and correctness of the wrapper code


Module Wrap.
Section S.
Context {Σ: gFunctors}.
Context `{heapG Σ}.
Context (N: namespace).

Context (locked_impl: iProp Σ) (unlocked_impl: val → iProp Σ).
Context (withRes_impl op_impl : val).

Definition withRes : val :=
  λ: "f",
    Emit (#"call:withRes", "f") ;;
    withRes_impl (λ: "x",
      Emit (#"call", "f") ;; "f" "x" ;; Emit (#"ret", "f")
    ) ;;
    Emit (#"ret:withRes", "f").

Definition op : val :=
  λ: "x",
    Emit (#"call:op", #()) ;;
    op_impl "x" ;;
    Emit (#"ret:op", #()).

Definition T0 : iProp Σ :=
  ∃ t, trace_is t ∗ trace_inv N bfile_trace ∗ ⌜ withRes_trace t ⌝.

Definition T1 f : iProp Σ :=
  ∃ t, trace_is t ∗ trace_inv N bfile_trace ∗ ⌜ trace1 t f ⌝.

Definition T2 f : iProp Σ :=
  ∃ t, trace_is t ∗ trace_inv N bfile_trace ∗ ⌜ trace2 t f ⌝.

Definition T3 f : iProp Σ :=
  ∃ t, trace_is t ∗ trace_inv N bfile_trace ∗ ⌜ trace3 t f ⌝.

Definition unlocked (x: val) : iProp Σ :=
  ∃ (y z:val), ⌜x = (y, z)%V⌝ ∗ unlocked_impl y ∗ T2 z.

Definition locked : iProp Σ :=
  locked_impl ∗ T0.

Lemma withRes_correct :
  withRes_spec locked_impl unlocked_impl withRes_impl -∗
  withRes_spec locked unlocked withRes.
Proof.
  iIntros "#spec" (P Q f φ) "!> (Hl & HP & #HS) Hφ".
  iDestruct "Hl" as "(Hl & Ht0)". iDestruct "Ht0" as (t) "(Ht & #Hi & %)".
  iMod (trace_is_inv with "Ht Hi") as "[Ht %]".
  unfold withRes. wp_pures. wp_bind (Emit _).
  iApply (wp_emit with "[$Ht $Hi]"); eauto.
  { eexists. split. 2: eapply withRes_trace_call.
    by apply prefix_app, prefix_cons, prefix_nil.
    auto. constructor. }
  iIntros "!> Ht". wp_pures. wp_bind (withRes_impl _).
  iApply ("spec" $! (P ∗ T1 f)%I (Q ∗ T3 f)%I with "[$Hl $HP Ht]").
  { iSplitL "Ht".
    { iExists _. iFrame "Hi ∗". iPureIntro. unfold trace1. go. }
    iIntros (y x ψ) "!> (Hu & [HP Ht1]) Hψ". wp_pures. wp_bind (Emit _).
    iDestruct "Ht1" as (t') "(Ht' & _ & Ht1)". iDestruct "Ht1" as %Ht1.
    iApply (wp_emit with "[$Ht' $Hi]"); eauto.
    { destruct Ht1 as [t'' [? ->]]. eexists. split. 2: eapply withRes_trace_call.
      rewrite -app_assoc. apply prefix_app, prefix_cons, prefix_cons, prefix_nil.
      eauto. constructor. }
    iIntros "!> Ht'". wp_pures. wp_bind (f _).
    iApply ("HS" $! (y, f)%V with "[Hu Ht' $HP]").
    { iExists _, _. iSplitR. done. iFrame. iExists _. iFrame "Hi ∗". iPureIntro.
      destruct Ht1 as [t'' [? ->]]. exists t'', []. repeat split; eauto. constructor.
      by list_simplifier. }
    iIntros "!> [Hu HQ]". wp_pures. iDestruct "Hu" as (y' z) "(% & Hy & Ht2)".
    simplify_eq. iDestruct "Ht2" as (t'') "(Ht'' & _ & Ht2)". iDestruct "Ht2" as %Ht2.
    destruct Ht2 as [t''' [t_op (? & ? & ->)]].
    iApply (wp_emit with "[$Ht'' $Hi]"); eauto.
    { eexists. split. 2: eapply withRes_trace_call. rewrite -app_assoc. cbn.
      apply prefix_app, prefix_cons, prefix_cons, prefix_app, prefix_cons, prefix_nil.
      all: eauto. }
    iIntros "!> Ht'''". iApply "Hψ". iFrame. iExists _. iFrame "Hi ∗".
    iPureIntro. exists t''', t_op. repeat split; eauto. by list_simplifier. }
  iIntros "!> (Hl & HQ & Ht3)". iDestruct "Ht3" as (t') "(Ht' & _ & Ht3)".
  iDestruct "Ht3" as %Ht3. destruct Ht3 as [t'' [t_op (? & ? & ->)]].
  wp_pures. iApply (wp_emit with "[$Ht' $Hi]"); eauto.
  { eexists. split. reflexivity. rewrite -!app_assoc. constructor; eauto. }
  iIntros "!> Ht". iApply "Hφ". iFrame. iExists _. iFrame "Hi ∗". iPureIntro.
  rewrite -!app_assoc. constructor; eauto.
Qed.

Lemma op_correct :
  op_spec locked_impl unlocked_impl op_impl -∗
  op_spec locked unlocked op.
Proof.
  iIntros "#spec" (x y φ) "!> Hu Hφ". iDestruct "Hu" as (y' z ->) "(Hu & Ht2)".
  iDestruct "Ht2" as (t) "(Ht & #Hi & Ht2)". iDestruct "Ht2" as %Ht2.
  unfold op. wp_pures. wp_bind (Emit _).
  destruct Ht2 as [t' [t_op (? & ? & ->)]].
  iApply (wp_emit with "[$Ht $Hi]"); eauto.
  { eexists. split. 2: eapply withRes_trace_call. rewrite -!app_assoc.
    apply prefix_app, prefix_cons, prefix_cons.
    3: apply op_trace_call. rewrite -app_assoc.
    apply prefix_app, prefix_cons, prefix_nil. all: eauto. }
  iIntros "!> Ht". wp_pures. wp_bind (op_impl _).
  iApply ("spec" with "Hu"). iIntros "!> Hu". wp_pures.
  iApply (wp_emit with "[$Ht $Hi]"); eauto.
  { eexists. split. 2: eapply withRes_trace_call.
    3: apply op_trace_call; eauto. rewrite -!app_assoc.
    repeat first [ apply prefix_app | apply prefix_cons | apply prefix_nil ].
    eauto. }
  iIntros "!> Ht". iApply "Hφ". iExists _, _. iSplitR. done. iFrame.
  iExists _. iFrame "Hi ∗". iPureIntro. exists t'. eexists. split; [eauto|].
  split. apply op_trace_call; eauto. by list_simplifier.
Qed.

End S.

Wrapping code for an entire library
Definition lib (lib_impl: val): val :=
  match lib_impl with
  | (withRes_impl, op_impl)%V =>
    (withRes withRes_impl, op op_impl)
  | _ => #()
  end.

Correctness of the library wrapper
Lemma correct `{!heapG Σ} N P0 (lib_impl: val):
  bfilelib_spec P0 lib_impl -∗
  bfilelib_spec (P0 ∗ trace_is [] ∗ trace_inv N bfile_trace) (lib lib_impl).
Proof.
  iIntros "S". iDestruct "S" as (locked_impl unlocked_impl) "(#H0 & S)".
  repeat case_match; eauto. iDestruct "S" as "(? & ?)".
  unfold bfilelib_spec.
  iExists (locked N locked_impl), (unlocked N unlocked_impl). repeat iSplit.
  { iIntros "!> (HP0 & ? & #Hi)". iDestruct ("H0" with "HP0") as "?". iFrame.
    iExists _. iFrame "Hi ∗". iPureIntro. constructor. }
  iApply withRes_correct; eauto.
  iApply op_correct; eauto.
Qed.

End Wrap.

Adequacy


Definition bfilelibN := nroot .@ "bfilelib".
Definition empty_state : state := Build_state ∅ [] ∅.

The trace property bfile_trace is satisfied at every step of the execution at the level of the operational semantics.
Lemma wrap_bfilelib_correct (e: val → expr) (lib: val):
  (∀ `(heapG Σ), ⊢ bfilelib_spec True lib) →
  (∀ `(heapG Σ), ⊢ ∀ P lib, bfilelib_spec P lib -∗ {{{ P }}} e lib {{{ v, RET v; True }}}) →
  ∀ σ' e',
    rtc erased_step ([(#();; e (Wrap.lib lib))%E], empty_state) (e', σ') →
    bfile_trace (trace σ').
Proof.
  set (Σ := #[invΣ; gen_heapΣ loc val; traceΣ; proph_mapΣ proph_id (val * val)]).
  intros Hlib Hctx σ' e' Hsteps.
  eapply (@module_invariance Σ (HeapPreG Σ _ _ _ _)
                             bfilelibN (@bfilelib_spec Σ) True e #() (Wrap.lib lib)
                             bfile_trace empty_state).
  { cbn. exists []. split; eauto; constructor. }
  { iIntros (? ? ?) "?". by iApply Hctx. }
  { iIntros (? _) "!>". iApply wp_value; eauto. }
  { iIntros (?). iApply Wrap.correct. iApply Hlib. }
  eauto.
Qed.