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.