Skip to content
Snippets Groups Projects
Forked from Iris / lambda-rust
1057 commits behind the upstream repository.
heap.v 28.18 KiB
From iris.algebra Require Import cmra_big_op gmap frac agree.
From iris.algebra Require Import csum excl auth.
From iris.base_logic Require Import big_op lib.invariants lib.fractional.
From iris.proofmode Require Export tactics.
From lrust.lang Require Export lifting.
Import uPred.

Definition heapN : namespace := nroot .@ "heap".

Definition lock_stateR : cmraT :=
  csumR (exclR unitC) natR.

Definition heapUR : ucmraT :=
  gmapUR loc (prodR (prodR fracR lock_stateR) (agreeR valC)).

Definition heap_freeableUR : ucmraT :=
  gmapUR block (prodR fracR (gmapR Z (exclR unitC))).

Class heapG Σ := HeapG {
  heapG_iris_inG :> irisG lrust_lang Σ;
  heap_inG :> inG Σ (authR heapUR);
  heap_freeable_inG :> inG Σ (authR heap_freeableUR);
  heap_name : gname;
  heap_freeable_name : gname
}.

Definition to_lock_stateR (x : lock_state) : lock_stateR :=
  match x with RSt n => Cinr n | WSt => Cinl (Excl ()) end.
Definition to_heap : state → heapUR :=
  fmap (λ v, (1%Qp, to_lock_stateR (v.1), to_agree (v.2))).
Definition heap_freeable_rel (σ : state) (hF : heap_freeableUR) : Prop :=
  ∀ blk qs, hF !! blk = Some qs →
  qs.2 ≠ ∅ ∧ ∀ i, is_Some (σ !! (blk, i)) ↔ is_Some (qs.2 !! i).

Section definitions.
  Context `{heapG Σ}.

  Definition heap_mapsto_st (st : lock_state)
             (l : loc) (q : Qp) (v: val) : iProp Σ :=
    own heap_name (◯ {[ l := (q, to_lock_stateR st, to_agree v) ]}).

  Definition heap_mapsto_def (l : loc) (q : Qp) (v: val) : iProp Σ :=
    heap_mapsto_st (RSt 0) l q v.
  Definition heap_mapsto_aux : { x | x = @heap_mapsto_def }. by eexists. Qed.
  Definition heap_mapsto := proj1_sig heap_mapsto_aux.
  Definition heap_mapsto_eq : @heap_mapsto = @heap_mapsto_def :=
    proj2_sig heap_mapsto_aux.

  Definition heap_mapsto_vec (l : loc) (q : Qp) (vl : list val) : iProp Σ :=
    ([∗ list] i ↦ v ∈ vl, heap_mapsto (shift_loc l i) q v)%I.

  Fixpoint inter (i0 : Z) (n : nat) : gmapR Z (exclR unitC) :=
    match n with O => ∅ | S n => <[i0 := Excl ()]>(inter (i0+1) n) end.

  Definition heap_freeable_def (l : loc) (q : Qp) (n: nat) : iProp Σ :=
    own heap_freeable_name (◯ {[ l.1 := (q, inter (l.2) n) ]}).
  Definition heap_freeable_aux : { x | x = @heap_freeable_def }. by eexists. Qed.
  Definition heap_freeable := proj1_sig heap_freeable_aux.
  Definition heap_freeable_eq : @heap_freeable = @heap_freeable_def :=
    proj2_sig heap_freeable_aux.

  Definition heap_inv : iProp Σ :=
    (∃ (σ:state) hF, ownP σ ∗ own heap_name (● to_heap σ)
                            ∗ own heap_freeable_name (● hF)
                            ∗ ⌜heap_freeable_rel σ hF⌝)%I.
  Definition heap_ctx : iProp Σ := inv heapN heap_inv.

  Global Instance heap_ctx_persistent : PersistentP heap_ctx.
  Proof. apply _. Qed.
End definitions.

Typeclasses Opaque heap_ctx heap_mapsto heap_freeable heap_mapsto_vec.
Instance: Params (@heap_mapsto) 4.
Instance: Params (@heap_freeable) 5.

Notation "l ↦{ q } v" := (heap_mapsto l q v)
  (at level 20, q at level 50, format "l  ↦{ q }  v") : uPred_scope.
Notation "l ↦ v" := (heap_mapsto l 1 v) (at level 20) : uPred_scope.

Notation "l ↦∗{ q } vl" := (heap_mapsto_vec l q vl)
  (at level 20, q at level 50, format "l  ↦∗{ q }  vl") : uPred_scope.
Notation "l ↦∗ vl" := (heap_mapsto_vec l 1 vl) (at level 20) : uPred_scope.

Notation "l ↦∗{ q }: P" := (∃ vl, l ↦∗{ q } vl ∗ P vl)%I
  (at level 20, q at level 50, format "l  ↦∗{ q }:  P") : uPred_scope.
Notation "l ↦∗: P " := (∃ vl, l ↦∗ vl ∗ P vl)%I
  (at level 20, format "l  ↦∗:  P") : uPred_scope.

Notation "†{ q } l … n" := (heap_freeable l q n)
  (at level 20, q at level 50, format "†{ q } l … n") : uPred_scope.
Notation "† l … n" := (heap_freeable l 1 n) (at level 20) : uPred_scope.

Section heap.
  Context {Σ : gFunctors}.
  Implicit Types P Q : iProp Σ.
  Implicit Types σ : state.
  Implicit Types E : coPset.

  (** Allocation *)
  Lemma to_heap_valid σ : ✓ to_heap σ.
  Proof. intros l. rewrite lookup_fmap. case (σ !! l)=> [[[|n] v]|] //=. Qed.

  Lemma lookup_to_heap_None σ l : σ !! l = None → to_heap σ !! l = None.
  Proof. by rewrite /to_heap lookup_fmap=> ->. Qed.

  Lemma to_heap_insert σ l x v :
    to_heap (<[l:=(x, v)]> σ)
    = <[l:=(1%Qp, to_lock_stateR x, to_agree v)]> (to_heap σ).
  Proof. by rewrite /to_heap fmap_insert. Qed.

  Lemma to_heap_delete σ l : to_heap (delete l σ) = delete l (to_heap σ).
  Proof. by rewrite /to_heap fmap_delete. Qed.

  Context `{heapG Σ}.

  (** General properties of mapsto and freeable *)
  Global Instance heap_mapsto_timeless l q v : TimelessP (l↦{q}v).
  Proof. rewrite heap_mapsto_eq /heap_mapsto_def. apply _. Qed.

  Global Instance heap_mapsto_fractional l v: Fractional (λ q, l ↦{q} v)%I.
  Proof.
    intros p q.
    by rewrite heap_mapsto_eq -own_op -auth_frag_op op_singleton pair_op agree_idemp.
  Qed.
  Global Instance heap_mapsto_as_fractional l q v:
    AsFractional (l ↦{q} v) (λ q, l ↦{q} v)%I q.
  Proof. done. Qed.

  Global Instance heap_mapsto_vec_timeless l q vl : TimelessP (l ↦∗{q} vl).
  Proof. rewrite /heap_mapsto_vec. apply _. Qed.

  Global Instance heap_mapsto_vec_fractional l vl: Fractional (λ q, l ↦∗{q} vl)%I.
  Proof.
    intros p q. rewrite /heap_mapsto_vec -big_sepL_sepL.
    by setoid_rewrite (fractional (Φ := λ q, _ ↦{q} _)%I).
  Qed.
  Global Instance heap_mapsto_vec_as_fractional l q vl:
    AsFractional (l ↦∗{q} vl) (λ q, l ↦∗{q} vl)%I q.
  Proof. done. Qed.
  Global Instance heap_freeable_timeless q l n : TimelessP (†{q}l…n).
  Proof. rewrite heap_freeable_eq /heap_freeable_def. apply _. Qed.

  Lemma heap_mapsto_agree l q1 q2 v1 v2 :
    l ↦{q1} v1 ∗ l ↦{q2} v2 ⊢ ⌜v1 = v2⌝.
  Proof.
    rewrite heap_mapsto_eq -own_op -auth_frag_op own_valid discrete_valid.
    eapply pure_elim; [done|]=> /auth_own_valid /=.
    rewrite op_singleton pair_op singleton_valid. case.
    rewrite /= to_agree_comp_valid=>? Heq. fold_leibniz. eauto.
  Qed.

  Lemma heap_mapsto_vec_nil l q : l ↦∗{q} [] ⊣⊢ True.
  Proof. by rewrite /heap_mapsto_vec big_sepL_nil. Qed.

  Lemma heap_mapsto_vec_app l q vl1 vl2 :
    l ↦∗{q} (vl1 ++ vl2) ⊣⊢ l ↦∗{q} vl1 ∗ shift_loc l (length vl1) ↦∗{q} vl2.
  Proof.
    rewrite /heap_mapsto_vec big_sepL_app.
    do 2 f_equiv. intros k v. by rewrite shift_loc_assoc_nat.
  Qed.

  Lemma heap_mapsto_vec_singleton l q v : l ↦∗{q} [v] ⊣⊢ l ↦{q} v.
  Proof. by rewrite /heap_mapsto_vec big_sepL_singleton /= shift_loc_0. Qed.

  Lemma heap_mapsto_vec_cons l q v vl:
    l ↦∗{q} (v :: vl) ⊣⊢ l ↦{q} v ∗ shift_loc l 1 ↦∗{q} vl.
  Proof.
    by rewrite (heap_mapsto_vec_app l q [v] vl) heap_mapsto_vec_singleton.
  Qed.

  Lemma heap_mapsto_vec_op l q1 q2 vl1 vl2 :
    length vl1 = length vl2 →
    l ↦∗{q1} vl1 ∗ l ↦∗{q2} vl2 ⊣⊢ ⌜vl1 = vl2⌝ ∧ l ↦∗{q1+q2} vl1.
  Proof.
    intros Hlen%Forall2_same_length. apply (anti_symm (⊢)).
    - revert l. induction Hlen as [|v1 v2 vl1 vl2 _ _ IH]=> l.
      { rewrite !heap_mapsto_vec_nil. iIntros "_"; auto. }
      rewrite !heap_mapsto_vec_cons. iIntros "[[Hv1 Hvl1] [Hv2 Hvl2]]".
      iDestruct (IH (shift_loc l 1) with "[Hvl1 Hvl2]") as "[% $]";
        subst; first by iFrame.
      rewrite (inj_iff (:: vl2)).
      iDestruct (heap_mapsto_agree with "[$Hv1 $Hv2]") as %<-.
      iSplit; first done. iFrame.
    - by iIntros "[% [$ Hl2]]"; subst.
  Qed.

  Lemma heap_mapsto_vec_prop_op l q1 q2 n (Φ : list val → iProp Σ) :
    (∀ vl, Φ vl -∗ ⌜length vl = n⌝) →
    l ↦∗{q1}: Φ ∗ l ↦∗{q2}: (λ vl, ⌜length vl = n⌝) ⊣⊢ l ↦∗{q1+q2}: Φ.
  Proof.
    intros Hlen. iSplit.
    - iIntros "[Hmt1 Hmt2]".
      iDestruct "Hmt1" as (vl) "[Hmt1 Hown]". iDestruct "Hmt2" as (vl') "[Hmt2 %]".
      iDestruct (Hlen with "Hown") as %?.
      iCombine "Hmt1" "Hmt2" as "Hmt". rewrite heap_mapsto_vec_op; last congruence.
      iDestruct "Hmt" as "[_ Hmt]". iExists vl. by iFrame.
    - iIntros "Hmt". iDestruct "Hmt" as (vl) "[[Hmt1 Hmt2] Hown]".
      iDestruct (Hlen with "Hown") as %?.
      iSplitL "Hmt1 Hown"; iExists _; by iFrame.
  Qed.

  Lemma heap_mapsto_vec_combine l q vl :
    vl ≠ [] →
    l ↦∗{q} vl ⊣⊢ own heap_name (◯ [⋅ list] i ↦ v ∈ vl,
      {[shift_loc l i := (q, Cinr 0%nat, to_agree v)]}).
  Proof.
    rewrite /heap_mapsto_vec heap_mapsto_eq /heap_mapsto_def /heap_mapsto_st=>?.
    by rewrite (big_opL_commute (Auth None)) big_opL_commute1 //.
  Qed.

  Lemma inter_lookup_Some i j (n : nat):
    i ≤ j < i+n → inter i n !! j = Excl' ().
  Proof.
    revert i. induction n as [|n IH]=>/= i; first lia.
    rewrite lookup_insert_Some. destruct (decide (i = j)); naive_solver lia.
  Qed.
  Lemma inter_lookup_None i j (n : nat):
    j < i ∨ i+n ≤ j → inter i n !! j = None.
  Proof.
    revert i. induction n as [|n IH]=>/= i; first by rewrite lookup_empty.
    rewrite lookup_insert_None. naive_solver lia.
  Qed.
  Lemma inter_op i n n' : inter i n ⋅ inter (i+n) n' ≡ inter i (n+n').
  Proof.
    intros j. rewrite lookup_op.
    destruct (decide (i ≤ j < i+n)); last destruct (decide (i+n ≤ j < i+n+n')).
    - by rewrite (inter_lookup_None (i+n) j n') ?inter_lookup_Some; try lia.
    - by rewrite (inter_lookup_None i j n) ?inter_lookup_Some; try lia.
    - by rewrite !inter_lookup_None; try lia.
  Qed.
  Lemma inter_valid i n : ✓ inter i n.
  Proof. revert i. induction n as [|n IH]=>i. done. by apply insert_valid. Qed.

  Lemma heap_freeable_op_eq l q1 q2 n n' :
    †{q1}l…n ∗ †{q2}shift_loc l n…n' ⊣⊢ †{q1+q2}l…(n+n').
  Proof.
    by rewrite heap_freeable_eq /heap_freeable_def -own_op -auth_frag_op
      op_singleton pair_op inter_op.
  Qed.

  (** Properties about heap_freeable_rel and heap_freeable *)
  Lemma heap_freeable_rel_None σ l hF :
    (∀ m : Z, σ !! shift_loc l m = None) → heap_freeable_rel σ hF →
    hF !! l.1 = None.
  Proof.
    intros FRESH REL. apply eq_None_not_Some. intros [[q s] [Hsne REL']%REL].
    destruct (map_choose s) as [i []%REL'%not_eq_None_Some]; first done.
    move: (FRESH (i - l.2)). by rewrite /shift_loc Zplus_minus.
  Qed.

  Lemma heap_freeable_is_Some σ hF l n i :
    heap_freeable_rel σ hF →
    hF !! l.1 = Some (1%Qp, inter (l.2) n) →
    is_Some (σ !! shift_loc l i) ↔ 0 ≤ i ∧ i < n.
  Proof.
    destruct l as [b j]; rewrite /shift_loc /=. intros REL Hl.
    destruct (REL b (1%Qp, inter j n)) as [_ ->]; simpl in *; auto.
    destruct (decide (0 ≤ i ∧ i < n)).
    - rewrite is_Some_alt inter_lookup_Some; lia.
    - rewrite is_Some_alt inter_lookup_None; lia.
  Qed.

  Lemma heap_freeable_rel_init_mem l h vl σ:
    vl ≠ [] →
    (∀ m : Z, σ !! shift_loc l m = None) →
    heap_freeable_rel σ h →
    heap_freeable_rel (init_mem l vl σ)
                      (<[l.1 := (1%Qp, inter (l.2) (length vl))]> h).
  Proof.
    move=> Hvlnil FRESH REL blk qs /lookup_insert_Some [[<- <-]|[??]].
    - split.
      { destruct vl as [|v vl]; simpl in *; [done|]. apply: insert_non_empty. }
      intros i. destruct (decide (l.2 ≤ i ∧ i < l.2 + length vl)).
      + rewrite inter_lookup_Some //.
        destruct (lookup_init_mem_Some σ l (l.1, i) vl); naive_solver.
      + rewrite inter_lookup_None; last lia. rewrite lookup_init_mem_ne /=; last lia.
        replace (l.1,i) with (shift_loc l (i - l.2))
          by (rewrite /shift_loc; f_equal; lia).
        by rewrite FRESH !is_Some_alt.
    - destruct (REL blk qs) as [? Hs]; auto.
      split; [done|]=> i. by rewrite -Hs lookup_init_mem_ne; last auto.
  Qed.

  Lemma heap_freeable_rel_free_mem σ hF n l :
    hF !! l.1 = Some (1%Qp, inter (l.2) n) →
    heap_freeable_rel σ hF →
    heap_freeable_rel (free_mem l n σ) (delete (l.1) hF).
  Proof.
    intros Hl REL b qs; rewrite lookup_delete_Some=> -[NEQ ?].
    destruct (REL b qs) as [? REL']; auto.
    split; [done|]=> i. by rewrite -REL' lookup_free_mem_ne.
  Qed.

  Lemma heap_freeable_rel_stable σ h l p :
    heap_freeable_rel σ h → is_Some (σ !! l) →
    heap_freeable_rel (<[l := p]>σ) h.
  Proof.
    intros REL Hσ blk qs Hqs. destruct (REL blk qs) as [? REL']; first done.
    split; [done|]=> i. rewrite -REL' lookup_insert_is_Some.
    destruct (decide (l = (blk, i))); naive_solver.
  Qed.

  (** Weakest precondition *)
  Lemma heap_alloc_vs σ l vl:
    (∀ m : Z, σ !! shift_loc l m = None) →
    own heap_name (● to_heap σ)
    ==∗ own heap_name (● to_heap (init_mem l vl σ))
       ∗ own heap_name (◯ [⋅ list] i ↦ v ∈ vl,
           {[shift_loc l i := (1%Qp, Cinr 0%nat, to_agree v)]}).
  Proof.
    intros FREE. rewrite -own_op. apply own_update, auth_update_alloc.
    revert l FREE. induction vl as [|v vl IH]=> l FRESH; [done|].
    rewrite (big_opL_consZ_l (λ k _, _ (_ k) _ )) /=.
    etrans; first apply (IH (shift_loc l 1)).
    { intros. by rewrite shift_loc_assoc. }
    rewrite shift_loc_0 -insert_singleton_op; last first.
    { rewrite -equiv_None big_opL_commute equiv_None big_opL_None=> l' v' ?.
      rewrite lookup_singleton_None -{2}(shift_loc_0 l). apply not_inj; lia. }
    rewrite to_heap_insert. setoid_rewrite shift_loc_assoc.
    apply alloc_local_update; last done. apply lookup_to_heap_None.
    rewrite lookup_init_mem_ne /=; last lia. by rewrite -(shift_loc_0 l) FRESH.
  Qed.

  Lemma wp_alloc E n:
    ↑heapN ⊆ E → 0 < n →
    {{{ heap_ctx }}} Alloc (Lit $ LitInt n) @ E
    {{{ l vl, RET LitV $ LitLoc l; ⌜n = length vl⌝ ∗ †l…(Z.to_nat n) ∗ l ↦∗ vl }}}.
  Proof.
    iIntros (???) "#Hinv HΦ". rewrite /heap_ctx /heap_inv.
    iInv heapN as (σ hF) ">(Hσ & Hvalσ & HhF & %)" "Hclose".
    iApply (wp_alloc_pst with "[$Hσ]")=> //.
    iNext. iIntros (l σ') "(% & #Hσσ' & Hσ')".
    iDestruct "Hσσ'" as %(vl & HvlLen & Hvl).
    assert (vl ≠ []) by (intros ->; simpl in *; lia).
    iMod (heap_alloc_vs with "[$Hvalσ]") as "[Hvalσ' Hmapsto]"; first done.
    iMod (own_update _ (● hF) with "HhF") as "[HhF Hfreeable]".
    { apply auth_update_alloc,
        (alloc_singleton_local_update _ (l.1) (1%Qp, inter (l.2) (Z.to_nat n))).
      - eauto using heap_freeable_rel_None.
      - split. done. apply inter_valid. }
    iMod ("Hclose" with "[Hσ' Hvalσ' HhF]") as "_".
    - iNext. iExists σ', _. subst σ'. iFrame. iPureIntro.
      rewrite HvlLen Nat2Z.id. auto using heap_freeable_rel_init_mem.
    - rewrite heap_freeable_eq /heap_freeable_def. iApply "HΦ".
      iSplit; first auto. iFrame. by rewrite heap_mapsto_vec_combine.
  Qed.

  Lemma heap_free_vs l vl σ :
    own heap_name (● to_heap σ) ∗ own heap_name (◯ [⋅ list] i ↦ v ∈ vl,
      {[shift_loc l i := (1%Qp, Cinr 0%nat, to_agree v)]})
    ==∗ own heap_name (● to_heap (free_mem l (length vl) σ)).
  Proof.
    rewrite -own_op. apply own_update, auth_update_dealloc.
    revert σ l. induction vl as [|v vl IH]=> σ l; [done|].
    rewrite (big_opL_consZ_l (λ k _, _ (_ k) _ )) /= shift_loc_0.
    apply local_update_total_valid=> _ Hvalid _.
    assert (([⋅ list] k↦y ∈ vl,
      {[shift_loc l (1 + k) := (1%Qp, Cinr 0%nat, to_agree y)]}) !! l
      = @None (frac * lock_stateR * agree valC)).
    { move: (Hvalid l). rewrite lookup_op lookup_singleton.
      by move=> /(cmra_discrete_valid_iff 0) /exclusiveN_Some_l. }
    rewrite -insert_singleton_op //. etrans.
    { apply (delete_local_update _ _ l (1%Qp, Cinr 0%nat, to_agree v)).
      by rewrite lookup_insert. }
    rewrite delete_insert // -to_heap_delete delete_free_mem.
    setoid_rewrite <-shift_loc_assoc. apply IH.
  Qed.

  Lemma wp_free E (n:Z) l vl :
    ↑heapN ⊆ E → n = length vl →
    {{{ heap_ctx ∗ ▷ l ↦∗ vl ∗ ▷ †l…(length vl) }}}
      Free (Lit $ LitInt n) (Lit $ LitLoc l) @ E
    {{{ RET LitV LitUnit; True }}}.
  Proof.
    iIntros (???Φ) "(#Hinv & >Hmt & >Hf) HΦ". rewrite /heap_ctx /heap_inv.
    iInv heapN as (σ hF) ">(Hσ & Hvalσ & HhF & REL)" "Hclose". iDestruct "REL" as %REL.
    rewrite heap_freeable_eq /heap_freeable_def.
    iDestruct (own_valid_2 with "HhF Hf") as % [Hl Hv]%auth_valid_discrete_2.
    move: Hl=> /singleton_included [[q qs] [/leibniz_equiv_iff Hl Hq]].
    apply (Some_included_exclusive _ _) in Hq as [=<-<-]%leibniz_equiv; last first.
    { move: (Hv (l.1)). rewrite Hl. by intros [??]. }
    assert (vl ≠ []).
    { intros ->. by destruct (REL (l.1) (1%Qp, ∅)) as [[] ?]. }
    assert (0 < n) by (subst n; by destruct vl).
    iApply (wp_free_pst _ σ with "[$Hσ]"). by auto.
    { intros i. subst n. eauto using heap_freeable_is_Some. }
    iNext. iIntros "Hσ". iMod (heap_free_vs with "[Hmt Hvalσ]") as "Hvalσ".
    { rewrite heap_mapsto_vec_combine //. iFrame. }
    iMod (own_update_2 with "HhF Hf") as "HhF".
    { apply auth_update_dealloc, (delete_singleton_local_update _ _ _). }
    iMod ("Hclose" with "[-HΦ]") as "_"; last by iApply "HΦ".
    iNext. iExists _, _. subst. rewrite Nat2Z.id. iFrame.
    eauto using heap_freeable_rel_free_mem.
  Qed.

  Lemma heap_mapsto_lookup l ls q v σ:
    own heap_name (● to_heap σ) -∗
    own heap_name (◯ {[ l := (q, to_lock_stateR ls, to_agree v) ]}) -∗
    ⌜∃ n' : nat,
        σ !! l = Some (match ls with RSt n => RSt (n+n') | WSt => WSt end, v)⌝.
  Proof.
    iIntros "H● H◯".
    iDestruct (own_valid_2 with "H● H◯") as %[Hl?]%auth_valid_discrete_2.
    iPureIntro. move: Hl=> /singleton_included [[[q' ls'] dv]].
    rewrite /to_heap lookup_fmap fmap_Some_setoid.
    move=> [[[ls'' v'] [?[[/=??]->]]]]; simplify_eq.
    move=> /Some_pair_included_total_2
      [/Some_pair_included [_ Hincl] /to_agree_included->].
    destruct ls as [|n], ls'' as [|n''],
       Hincl as [[[|n'|]|] [=]%leibniz_equiv]; subst.
    by exists O. eauto. exists O. by rewrite Nat.add_0_r.
  Qed.

  Lemma heap_mapsto_lookup_1 l ls v σ:
    own heap_name (● to_heap σ) -∗
    own heap_name (◯ {[ l := (1%Qp, to_lock_stateR ls, to_agree v) ]}) -∗
    ⌜σ !! l = Some (ls, v)⌝.
  Proof.
    iIntros "H● H◯".
    iDestruct (own_valid_2 with "H● H◯") as %[Hl?]%auth_valid_discrete_2.
    iPureIntro. move: Hl=> /singleton_included [[[q' ls'] dv]].
    rewrite /to_heap lookup_fmap fmap_Some_setoid.
    move=> [[[ls'' v'] [?[[/=??]->]]] Hincl]; simplify_eq.
    apply (Some_included_exclusive _ _) in Hincl as [? Hval]; last by destruct ls''.
    apply (inj to_agree) in Hval. fold_leibniz. subst.
    destruct ls, ls''; rewrite ?Nat.add_0_r; naive_solver.
  Qed.

  Lemma wp_read_sc E l q v :
    ↑heapN ⊆ E →
    {{{ heap_ctx ∗ ▷ l ↦{q} v }}} Read ScOrd (Lit $ LitLoc l) @ E
    {{{ RET v; l ↦{q} v }}}.
  Proof.
    iIntros (??) "[#Hinv >Hv] HΦ".
    rewrite /heap_ctx /heap_inv heap_mapsto_eq /heap_mapsto_def.
    iInv heapN as (σ hF) ">(Hσ & Hvalσ & HhF & %)" "Hclose".
    iDestruct (heap_mapsto_lookup with "Hvalσ Hv") as %[n Hσl].
    iApply (wp_read_sc_pst with "[$Hσ]"); first done. iNext. iIntros "Hσ".
    iMod ("Hclose" with "[-Hv HΦ]"); last by iApply "HΦ".
    iNext. iExists σ, hF. iFrame. eauto.
  Qed.

  Lemma heap_read_vs σ n1 n2 nf l q v:
    σ !! l = Some (RSt (n1 + nf), v) →
    own heap_name (● to_heap σ) ∗ heap_mapsto_st (RSt n1) l q v
    ==∗ own heap_name (● to_heap (<[l:=(RSt (n2 + nf), v)]> σ))
        ∗ heap_mapsto_st (RSt n2) l q v.
  Proof.
    intros Hσv. rewrite -!own_op to_heap_insert.
    eapply own_update, auth_update, singleton_local_update.
    { by rewrite /to_heap lookup_fmap Hσv. }
    apply prod_local_update_1, prod_local_update_2, csum_local_update_r.
    apply nat_local_update; lia.
  Qed.

  Lemma wp_read_na E l q v :
    ↑heapN ⊆ E →
    {{{ heap_ctx ∗ ▷ l ↦{q} v }}} Read Na1Ord (Lit $ LitLoc l) @ E
    {{{ RET v; l ↦{q} v }}}.
  Proof.
    iIntros (??Φ) "[#Hinv >Hv] HΦ".
    rewrite /heap_ctx /heap_inv heap_mapsto_eq /heap_mapsto_def.
    iApply (wp_read_na1_pst E).
    iInv heapN as (σ hF) ">(Hσ & Hvalσ & HhF & %)" "Hclose".
    iDestruct (heap_mapsto_lookup with "Hvalσ Hv") as %[n Hσl].
    iMod (heap_read_vs _ 0 1 with "[$Hvalσ $Hv]") as "[Hvalσ Hv]"; first done.
    iMod (fupd_intro_mask' (E∖↑heapN) ∅) as "Hclose'"; first set_solver.
    iModIntro. iExists σ, n, v. iFrame. iSplit; [done|]. iIntros "!> Hσ".
    iMod "Hclose'" as "_". iMod ("Hclose" with "[Hσ Hvalσ HhF]") as "_".
    { iNext. iExists _, _. iFrame. eauto using heap_freeable_rel_stable. }
    iModIntro. clear dependent n σ hF.
    iInv heapN as (σ hF) ">(Hσ & Hvalσ & HhF & %)" "Hclose".
    iDestruct (heap_mapsto_lookup with "Hvalσ Hv") as %[n Hσl].
    iApply (wp_read_na2_pst with "[$Hσ]"); first done. iNext. iIntros "Hσ".
    iMod (heap_read_vs with "[$Hvalσ $Hv]") as "[Hvalσ Hv]"; first done.
    iMod ("Hclose" with "[-Hv HΦ]") as "_"; last by iApply "HΦ".
    iNext. iExists _, hF. iFrame. eauto using heap_freeable_rel_stable.
  Qed.

  Lemma heap_write_vs σ st1 st2 l v v':
    σ !! l = Some (st1, v) →
    own heap_name (● to_heap σ) ∗ heap_mapsto_st st1 l 1%Qp v
    ==∗ own heap_name (● to_heap (<[l:=(st2, v')]> σ))
        ∗ heap_mapsto_st st2 l 1%Qp v'.
  Proof.
    intros Hσv. rewrite -!own_op to_heap_insert.
    eapply own_update, auth_update, singleton_local_update.
    { by rewrite /to_heap lookup_fmap Hσv. }
    apply exclusive_local_update. by destruct st2.
  Qed.

  Lemma wp_write_sc E l e v v' :
    ↑heapN ⊆ E → to_val e = Some v →
    {{{ heap_ctx ∗ ▷ l ↦ v' }}} Write ScOrd (Lit $ LitLoc l) e @ E
    {{{ RET LitV LitUnit; l ↦ v }}}.
  Proof.
    iIntros (? <-%of_to_val?) "[#Hinv >Hv] HΦ".
    rewrite /heap_ctx /heap_inv heap_mapsto_eq /heap_mapsto_def.
    iInv heapN as (σ hF) ">(Hσ & Hvalσ & HhF & %)" "Hclose".
    iDestruct (heap_mapsto_lookup_1 with "Hvalσ Hv") as %?.
    iApply (wp_write_sc_pst with "[$Hσ]"); [done|]. iNext. iIntros "Hσ".
    iMod (heap_write_vs with "[$Hvalσ $Hv]") as "[Hvalσ Hv]"; first done.
    iMod ("Hclose" with "[-Hv HΦ]") as "_"; last by iApply "HΦ".
    iNext. iExists _, hF. iFrame. eauto using heap_freeable_rel_stable.
  Qed.

  Lemma wp_write_na E l e v v' :
    ↑heapN ⊆ E → to_val e = Some v →
    {{{ heap_ctx ∗ ▷ l ↦ v' }}} Write Na1Ord (Lit $ LitLoc l) e @ E
    {{{ RET LitV LitUnit; l ↦ v }}}.
  Proof.
    iIntros (? <-%of_to_val?) "[#Hinv >Hv] HΦ".
    rewrite /heap_ctx /heap_inv heap_mapsto_eq /heap_mapsto_def.
    iApply (wp_write_na1_pst E).
    iInv heapN as (σ hF) ">(Hσ & Hvalσ & HhF & %)" "Hclose".
    iDestruct (heap_mapsto_lookup_1 with "Hvalσ Hv") as %?.
    iMod (heap_write_vs with "[$Hvalσ $Hv]") as "[Hvalσ Hv]"; first done.
    iMod (fupd_intro_mask' (E∖↑heapN) ∅) as "Hclose'"; first set_solver.
    iModIntro. iExists σ, v'. iSplit; [done|]. iIntros "{$Hσ} !> Hσ".
    iMod "Hclose'" as "_". iMod ("Hclose" with "[Hσ Hvalσ HhF]") as "_".
    { iNext. iExists _, _. iFrame. eauto using heap_freeable_rel_stable. }
    iModIntro. clear dependent σ hF.
    iInv heapN as (σ hF) ">(Hσ & Hvalσ & HhF & %)" "Hclose".
    iDestruct (heap_mapsto_lookup_1 with "Hvalσ Hv") as %?.
    iApply (wp_write_na2_pst with "[$Hσ]"); [done|]. iNext. iIntros "Hσ".
    iMod (heap_write_vs with "[$Hvalσ $Hv]") as "[Hvalσ Hv]"; first done.
    iMod ("Hclose" with "[-Hv HΦ]"); last by iApply "HΦ".
    iNext. iExists _, hF. iFrame. eauto using heap_freeable_rel_stable.
  Qed.

  Lemma wp_cas_int_fail E l q z1 e2 lit2 zl :
    ↑heapN ⊆ E → to_val e2 = Some $ LitV lit2 → z1 ≠ zl →
    {{{ heap_ctx ∗ ▷ l ↦{q} (LitV $ LitInt zl) }}}
      CAS (Lit $ LitLoc l) (Lit $ LitInt z1) e2 @ E
    {{{ RET LitV $ LitInt 0; l ↦{q} (LitV $ LitInt zl) }}}.
  Proof.
    iIntros (? <-%of_to_val ??) "[#Hinv >Hv] HΦ".
    rewrite /heap_ctx /heap_inv heap_mapsto_eq /heap_mapsto_def.
    iInv heapN as (σ hF) ">(Hσ & Hvalσ & HhF & %)" "Hclose".
    iDestruct (heap_mapsto_lookup with "Hvalσ Hv") as %[n Hσl].
    iApply (wp_cas_pst with "[$Hσ]"); eauto.
    { right. by constructor. }
    { inversion 1. done. }
    iNext. iIntros ([|]).
    { iIntros "[Heq _]". iDestruct "Heq" as %Heq. inversion Heq. done. }
    iIntros "[_ Hσ]". iMod ("Hclose" with "[-Hv HΦ]") as "_"; last by iApply "HΦ".
    iNext. iExists _, hF. iFrame. eauto using heap_freeable_rel_stable.
  Qed.

  Lemma wp_cas_int_suc E l z1 e2 lit2 :
    ↑heapN ⊆ E → to_val e2 = Some $ LitV lit2 →
    {{{ heap_ctx ∗ ▷ l ↦ (LitV $ LitInt z1) }}}
      CAS (Lit $ LitLoc l) (Lit $ LitInt z1) e2 @ E
    {{{ RET LitV $ LitInt 1; l ↦ LitV lit2 }}}.
  Proof.
    iIntros (? <-%of_to_val?) "[#Hinv >Hv] HΦ".
    rewrite /heap_ctx /heap_inv heap_mapsto_eq /heap_mapsto_def.
    iInv heapN as (σ hF) ">(Hσ & Hvalσ & HhF & %)" "Hclose".
    iDestruct (heap_mapsto_lookup_1 with "Hvalσ Hv") as %?.
    iApply (wp_cas_pst with "[$Hσ]"); eauto.
    { left. by constructor. }
    iNext. iIntros ([|]); last first.
    { iIntros "[Hneq _]". iDestruct "Hneq" as %Hneq. inversion Hneq. done. }
    iIntros "[_ Hσ]". iMod (heap_write_vs with "[$Hvalσ $Hv]") as "[Hvalσ Hv]"; first done.
    iMod ("Hclose" with "[-Hv HΦ]"); last by iApply "HΦ"; iFrame.
    iNext. iExists _, hF. iFrame. eauto using  heap_freeable_rel_stable.
  Qed.

  Lemma wp_cas_loc_fail E l q q' q1 l1 v1' e2 lit2 l' vl' :
    ↑heapN ⊆ E → to_val e2 = Some $ LitV lit2 → l1 ≠ l' →
    {{{ heap_ctx ∗ ▷ l ↦{q} (LitV $ LitLoc l') ∗ ▷ l' ↦{q'} vl' ∗ ▷ l1 ↦{q1} v1' }}}
      CAS (Lit $ LitLoc l) (Lit $ LitLoc l1) e2 @ E
    {{{ RET LitV $ LitInt 0;
        l ↦{q} (LitV $ LitLoc l') ∗ l' ↦{q'} vl' ∗ l1 ↦{q1} v1' }}}.
  Proof.
    iIntros (? <-%of_to_val ??) "(#Hinv & >Hl & >Hl' & >Hl1) HΦ".
    rewrite /heap_ctx /heap_inv heap_mapsto_eq /heap_mapsto_def.
    iInv heapN as (σ hF) ">(Hσ & Hvalσ & HhF & %)" "Hclose".
    iDestruct (heap_mapsto_lookup with "Hvalσ Hl") as %[n Hσl].
    iDestruct (heap_mapsto_lookup with "Hvalσ Hl'") as %[n' Hσl'].
    iDestruct (heap_mapsto_lookup with "Hvalσ Hl1") as %[n1 Hσl1].
    iApply (wp_cas_pst with "[$Hσ]"); eauto.
    { right. by constructor. }
    { inversion 1; subst; (by destruct (σ !! l1)) || by destruct (σ !! l'). }
    iNext. iIntros ([|]).
    { iIntros "[Heq _]". iDestruct "Heq" as %Heq. inversion Heq; subst.
      done. by destruct (σ !! l1). by destruct (σ !! l'). }
    iIntros "[_ Hσ]". iMod ("Hclose" with "[-Hl Hl' Hl1 HΦ]") as "_"; last by iApply "HΦ"; iFrame.
    iNext. iExists _, hF. by iFrame.
  Qed.

  Lemma wp_cas_loc_suc E l l1 e2 lit2 :
    ↑heapN ⊆ E → to_val e2 = Some $ LitV lit2 →
    {{{ heap_ctx ∗ ▷ l ↦ (LitV $ LitLoc l1) }}}
      CAS (Lit $ LitLoc l) (Lit $ LitLoc l1) e2 @ E
    {{{ RET LitV $ LitInt 1; l ↦ LitV lit2 }}}.
  Proof.
    iIntros (? <-%of_to_val?) "[#Hinv >Hv] HΦ".
    rewrite /heap_ctx /heap_inv heap_mapsto_eq /heap_mapsto_def.
    iInv heapN as (σ hF) ">(Hσ & Hvalσ & HhF & %)" "Hclose".
    iDestruct (heap_mapsto_lookup_1 with "Hvalσ Hv") as %?.
    iApply (wp_cas_pst with "[$Hσ]"); eauto.
    { left. by constructor. }
    iNext. iIntros ([|]); last first.
    { iIntros "[Hneq _]". iDestruct "Hneq" as %Hneq. inversion Hneq. done. }
    iIntros "[_ Hσ]". iMod (heap_write_vs with "[$Hvalσ $Hv]") as "[Hvalσ Hv]"; first done.
    iMod ("Hclose" with "[-Hv HΦ]"); last by iApply "HΦ"; iFrame.
    iNext. iExists _, hF. iFrame. eauto using  heap_freeable_rel_stable.
  Qed.

  Lemma wp_cas_loc_nondet E l l1 e2 l2 ll :
    ↑heapN ⊆ E → to_val e2 = Some $ LitV $ LitLoc l2 →
    {{{ heap_ctx ∗ ▷ l ↦ (LitV $ LitLoc ll) }}}
      CAS (Lit $ LitLoc l) (Lit $ LitLoc l1) e2 @ E
    {{{ b, RET LitV $ lit_of_bool b;
        if b is true then l ↦ LitV (LitLoc l2)
        else ⌜l1 ≠ ll⌝ ∗ l ↦ LitV (LitLoc ll) }}}.
  Proof.
    iIntros (? <-%of_to_val?) "[#Hinv >Hv] HΦ".
    rewrite /heap_ctx /heap_inv heap_mapsto_eq /heap_mapsto_def.
    iInv heapN as (σ hF) ">(Hσ & Hvalσ & HhF & %)" "Hclose".
    iDestruct (heap_mapsto_lookup_1 with "Hvalσ Hv") as %?.
    iApply (wp_cas_pst with "[$Hσ]"); eauto.
    { destruct (decide (l1 = ll)) as [->|Hne].
      - left. by constructor.
      - right. by constructor. }
    iNext. iIntros ([|]).
    - iIntros "[_ Hσ]". iMod (heap_write_vs with "[$Hvalσ $Hv]") as "[Hvalσ Hv]"; first done.
      iMod ("Hclose" with "[-Hv HΦ]"); last by iApply "HΦ"; iFrame.
      iNext. iExists _, hF. iFrame. eauto using  heap_freeable_rel_stable.
    - iIntros "[Hneq Hσ]". iDestruct "Hneq" as %Hneq. inversion_clear Hneq.
      iMod ("Hclose" with "[-Hv HΦ]"); last by iApply ("HΦ" $! false); iFrame.
      iNext. iExists _, hF. iFrame. eauto using  heap_freeable_rel_stable.
  Qed.
End heap.