|
|
|
|
(** This file extends the HeapLang program logic with some derived laws (not
|
|
|
|
|
using the lifting lemmas) about arrays and prophecies.
|
|
|
|
|
|
|
|
|
|
For utility functions on arrays (e.g., freeing/copying an array), see
|
|
|
|
|
[heap_lang.lib.array]. *)
|
|
|
|
|
|
|
|
|
|
From stdpp Require Import fin_maps.
|
|
|
|
|
From iris.bi Require Import lib.fractional.
|
|
|
|
|
From iris.proofmode Require Import proofmode.
|
|
|
|
|
From iris.heap_lang Require Import tactics notation.
|
|
|
|
|
From semantics.pl.heap_lang Require Export primitive_laws.
|
|
|
|
|
From iris.prelude Require Import options.
|
|
|
|
|
|
|
|
|
|
(** The [array] connective is a version of [mapsto] that works
|
|
|
|
|
with lists of values. *)
|
|
|
|
|
|
|
|
|
|
Definition array `{!heapGS Σ} (l : loc) (dq : dfrac) (vs : list val) : iProp Σ :=
|
|
|
|
|
[∗ list] i ↦ v ∈ vs, (l +ₗ i) ↦{dq} v.
|
|
|
|
|
|
|
|
|
|
(** FIXME: Refactor these notations using custom entries once Coq bug #13654
|
|
|
|
|
has been fixed. *)
|
|
|
|
|
Notation "l ↦∗{ dq } vs" := (array l dq vs)
|
|
|
|
|
(at level 20, format "l ↦∗{ dq } vs") : bi_scope.
|
|
|
|
|
Notation "l ↦∗□ vs" := (array l DfracDiscarded vs)
|
|
|
|
|
(at level 20, format "l ↦∗□ vs") : bi_scope.
|
|
|
|
|
Notation "l ↦∗{# q } vs" := (array l (DfracOwn q) vs)
|
|
|
|
|
(at level 20, format "l ↦∗{# q } vs") : bi_scope.
|
|
|
|
|
Notation "l ↦∗ vs" := (array l (DfracOwn 1) vs)
|
|
|
|
|
(at level 20, format "l ↦∗ vs") : bi_scope.
|
|
|
|
|
|
|
|
|
|
(** We have [FromSep] and [IntoSep] instances to split the fraction (via the
|
|
|
|
|
[AsFractional] instance below), but not for splitting the list, as that would
|
|
|
|
|
lead to overlapping instances. *)
|
|
|
|
|
|
|
|
|
|
Section lifting.
|
|
|
|
|
Context `{!heapGS Σ}.
|
|
|
|
|
Implicit Types P Q : iProp Σ.
|
|
|
|
|
Implicit Types Φ : val → iProp Σ.
|
|
|
|
|
Implicit Types σ : state.
|
|
|
|
|
Implicit Types v : val.
|
|
|
|
|
Implicit Types vs : list val.
|
|
|
|
|
Implicit Types l : loc.
|
|
|
|
|
Implicit Types sz off : nat.
|
|
|
|
|
|
|
|
|
|
Global Instance array_timeless l q vs : Timeless (array l q vs) := _.
|
|
|
|
|
|
|
|
|
|
Global Instance array_fractional l vs : Fractional (λ q, l ↦∗{#q} vs)%I := _.
|
|
|
|
|
Global Instance array_as_fractional l q vs :
|
|
|
|
|
AsFractional (l ↦∗{#q} vs) (λ q, l ↦∗{#q} vs)%I q.
|
|
|
|
|
Proof. split; done || apply _. Qed.
|
|
|
|
|
|
|
|
|
|
Lemma array_nil l dq : l ↦∗{dq} [] ⊣⊢ emp.
|
|
|
|
|
Proof. by rewrite /array. Qed.
|
|
|
|
|
|
|
|
|
|
Lemma array_singleton l dq v : l ↦∗{dq} [v] ⊣⊢ l ↦{dq} v.
|
|
|
|
|
Proof. by rewrite /array /= right_id Loc.add_0. Qed.
|
|
|
|
|
|
|
|
|
|
Lemma array_app l dq vs ws :
|
|
|
|
|
l ↦∗{dq} (vs ++ ws) ⊣⊢ l ↦∗{dq} vs ∗ (l +ₗ length vs) ↦∗{dq} ws.
|
|
|
|
|
Proof.
|
|
|
|
|
rewrite /array big_sepL_app.
|
|
|
|
|
setoid_rewrite Nat2Z.inj_add.
|
|
|
|
|
by setoid_rewrite Loc.add_assoc.
|
|
|
|
|
Qed.
|
|
|
|
|
|
|
|
|
|
Lemma array_cons l dq v vs : l ↦∗{dq} (v :: vs) ⊣⊢ l ↦{dq} v ∗ (l +ₗ 1) ↦∗{dq} vs.
|
|
|
|
|
Proof.
|
|
|
|
|
rewrite /array big_sepL_cons Loc.add_0.
|
|
|
|
|
setoid_rewrite Loc.add_assoc.
|
|
|
|
|
setoid_rewrite Nat2Z.inj_succ.
|
|
|
|
|
by setoid_rewrite Z.add_1_l.
|
|
|
|
|
Qed.
|
|
|
|
|
|
|
|
|
|
Global Instance array_cons_frame l dq v vs R Q :
|
|
|
|
|
Frame false R (l ↦{dq} v ∗ (l +ₗ 1) ↦∗{dq} vs) Q →
|
|
|
|
|
Frame false R (l ↦∗{dq} (v :: vs)) Q | 2.
|
|
|
|
|
Proof. by rewrite /Frame array_cons. Qed.
|
|
|
|
|
|
|
|
|
|
Lemma update_array l dq vs off v :
|
|
|
|
|
vs !! off = Some v →
|
|
|
|
|
⊢ l ↦∗{dq} vs -∗ ((l +ₗ off) ↦{dq} v ∗ ∀ v', (l +ₗ off) ↦{dq} v' -∗ l ↦∗{dq} <[off:=v']>vs).
|
|
|
|
|
Proof.
|
|
|
|
|
iIntros (Hlookup) "Hl".
|
|
|
|
|
rewrite -[X in (l ↦∗{_} X)%I](take_drop_middle _ off v); last done.
|
|
|
|
|
iDestruct (array_app with "Hl") as "[Hl1 Hl]".
|
|
|
|
|
iDestruct (array_cons with "Hl") as "[Hl2 Hl3]".
|
|
|
|
|
assert (off < length vs) as H by (apply lookup_lt_is_Some; by eexists).
|
|
|
|
|
rewrite take_length min_l; last by lia. iFrame "Hl2".
|
|
|
|
|
iIntros (w) "Hl2".
|
|
|
|
|
clear Hlookup. assert (<[off:=w]> vs !! off = Some w) as Hlookup.
|
|
|
|
|
{ apply list_lookup_insert. lia. }
|
|
|
|
|
rewrite -[in (l ↦∗{_} <[off:=w]> vs)%I](take_drop_middle (<[off:=w]> vs) off w Hlookup).
|
|
|
|
|
iApply array_app. rewrite take_insert; last by lia. iFrame.
|
|
|
|
|
iApply array_cons. rewrite take_length min_l; last by lia. iFrame.
|
|
|
|
|
rewrite drop_insert_gt; last by lia. done.
|
|
|
|
|
Qed.
|
|
|
|
|
|
|
|
|
|
(** * Rules for allocation *)
|
|
|
|
|
Lemma mapsto_seq_array l dq v n :
|
|
|
|
|
([∗ list] i ∈ seq 0 n, (l +ₗ (i : nat)) ↦{dq} v) -∗
|
|
|
|
|
l ↦∗{dq} replicate n v.
|
|
|
|
|
Proof.
|
|
|
|
|
rewrite /array. iInduction n as [|n'] "IH" forall (l); simpl.
|
|
|
|
|
{ done. }
|
|
|
|
|
iIntros "[$ Hl]". rewrite -fmap_S_seq big_sepL_fmap.
|
|
|
|
|
setoid_rewrite Nat2Z.inj_succ. setoid_rewrite <-Z.add_1_l.
|
|
|
|
|
setoid_rewrite <-Loc.add_assoc. iApply "IH". done.
|
|
|
|
|
Qed.
|
|
|
|
|
|
|
|
|
|
Lemma wp_allocN s E v n Φ :
|
|
|
|
|
(0 < n)%Z →
|
|
|
|
|
▷ (∀ l, l ↦∗ replicate (Z.to_nat n) v -∗ Φ (LitV $ LitLoc l)) -∗
|
|
|
|
|
WP AllocN (Val $ LitV $ LitInt $ n) (Val v) @ s; E; E {{ Φ }}.
|
|
|
|
|
Proof.
|
|
|
|
|
iIntros (Hzs) "HΦ". iApply wp_allocN_seq; [done..|].
|
|
|
|
|
iNext. iIntros (l) "Hlm". iApply "HΦ".
|
|
|
|
|
by iApply mapsto_seq_array.
|
|
|
|
|
Qed.
|
|
|
|
|
|
|
|
|
|
Lemma wp_allocN_vec s E v n Φ :
|
|
|
|
|
(0 < n)%Z →
|
|
|
|
|
▷ (∀ l, l ↦∗ vreplicate (Z.to_nat n) v -∗ Φ (#l)) -∗
|
|
|
|
|
WP AllocN #n v @ s ; E; E {{ Φ }}.
|
|
|
|
|
Proof.
|
|
|
|
|
iIntros (Hzs) "HΦ". iApply wp_allocN; [ lia | .. ].
|
|
|
|
|
iNext. iIntros (l) "Hl". iApply "HΦ". rewrite vec_to_list_replicate. iFrame.
|
|
|
|
|
Qed.
|
|
|
|
|
|
|
|
|
|
(** * Rules for accessing array elements *)
|
|
|
|
|
Lemma wp_load_offset s E l dq off vs v Φ :
|
|
|
|
|
vs !! off = Some v →
|
|
|
|
|
l ↦∗{dq} vs -∗
|
|
|
|
|
▷ (l ↦∗{dq} vs -∗ Φ v) -∗
|
|
|
|
|
WP ! #(l +ₗ off) @ s; E; E {{ Φ }}.
|
|
|
|
|
Proof.
|
|
|
|
|
iIntros (Hlookup) "Hl HΦ".
|
|
|
|
|
iDestruct (update_array l _ _ _ _ Hlookup with "Hl") as "[Hl1 Hl2]".
|
|
|
|
|
iApply (wp_load with "Hl1"). iIntros "!> Hl1". iApply "HΦ".
|
|
|
|
|
iDestruct ("Hl2" $! v) as "Hl2". rewrite list_insert_id; last done.
|
|
|
|
|
iApply "Hl2". iApply "Hl1".
|
|
|
|
|
Qed.
|
|
|
|
|
|
|
|
|
|
Lemma wp_load_offset_vec s E l dq sz (off : fin sz) (vs : vec val sz) Φ :
|
|
|
|
|
l ↦∗{dq} vs -∗
|
|
|
|
|
▷ (l ↦∗{dq} vs -∗ Φ (vs !!! off)) -∗
|
|
|
|
|
WP ! #(l +ₗ off) @ s; E; E {{ Φ }}.
|
|
|
|
|
Proof. apply wp_load_offset. by apply vlookup_lookup. Qed.
|
|
|
|
|
|
|
|
|
|
Lemma wp_store_offset s E l off vs v Φ :
|
|
|
|
|
is_Some (vs !! off) →
|
|
|
|
|
l ↦∗ vs -∗
|
|
|
|
|
▷ (l ↦∗ <[off:=v]> vs -∗ Φ #()) -∗
|
|
|
|
|
WP #(l +ₗ off) <- v @ s; E; E {{ Φ }}.
|
|
|
|
|
Proof.
|
|
|
|
|
iIntros ([w Hlookup]) "Hl HΦ".
|
|
|
|
|
iDestruct (update_array l _ _ _ _ Hlookup with "Hl") as "[Hl1 Hl2]".
|
|
|
|
|
iApply (wp_store with "Hl1"). iIntros "!> Hl1".
|
|
|
|
|
iApply "HΦ". iApply "Hl2". iApply "Hl1".
|
|
|
|
|
Qed.
|
|
|
|
|
|
|
|
|
|
Lemma wp_store_offset_vec s E l sz (off : fin sz) (vs : vec val sz) v Φ :
|
|
|
|
|
l ↦∗ vs -∗
|
|
|
|
|
▷ (l ↦∗ vinsert off v vs -∗ Φ #()) -∗
|
|
|
|
|
WP #(l +ₗ off) <- v @ s; E; E {{ Φ }}.
|
|
|
|
|
Proof.
|
|
|
|
|
setoid_rewrite vec_to_list_insert. apply wp_store_offset.
|
|
|
|
|
eexists. by apply vlookup_lookup.
|
|
|
|
|
Qed.
|
|
|
|
|
|
|
|
|
|
End lifting.
|
|
|
|
|
|
|
|
|
|
#[global] Typeclasses Opaque array.
|