Heaps and heap assertions for separation logics.
From Coq Require Import ZArith Lia Bool String List.
From Coq Require Import FunctionalExtensionality PropExtensionality.
Local Open Scope Z_scope.
1. Memory heaps
A memory heap is a partial function from addresses (memory locations)
to values, with a finite domain.
Definition addr :=
Z.
Record heap :
Type := {
contents :>
addr ->
option Z;
isfinite :
exists i,
forall j,
i <=
j ->
contents j =
None
}.
Lemma heap_extensionality:
forall (
h1 h2:
heap),
(
forall l,
h1 l =
h2 l) ->
h1 =
h2.
Proof.
The empty heap.
Program Definition hempty :
heap :=
{|
contents :=
fun l =>
None |}.
Next Obligation.
exists 0; auto.
Qed.
The heap that contains v at address l and is equal to h on
all other addresses.
Program Definition hupdate (
l:
addr) (
v:
Z) (
h:
heap) :
heap :=
{|
contents :=
fun l' =>
if Z.eq_dec l l'
then Some v else h l' |}.
Next Obligation.
destruct (
isfinite h)
as (
i &
fin).
exists (
Z.max i (
l + 1));
intros.
destruct (
Z.eq_dec l j).
lia.
apply fin;
lia.
Qed.
Lemma hupdate_same:
forall l v h, (
hupdate l v h)
l =
Some v.
Proof.
intros;
cbn.
destruct (
Z.eq_dec l l);
congruence.
Qed.
Lemma hupdate_other:
forall l v h l',
l <>
l' -> (
hupdate l v h)
l' =
h l'.
Proof.
intros;
cbn.
destruct (
Z.eq_dec l l');
congruence.
Qed.
The heap h after deallocating address l.
Program Definition hfree (
l:
addr) (
h:
heap) :
heap :=
{|
contents :=
fun l' =>
if Z.eq_dec l l'
then None else h l' |}.
Next Obligation.
destruct (
isfinite h)
as (
i &
fin).
exists i;
intros.
destruct (
Z.eq_dec l j);
auto.
Qed.
The heap h where addresses l, ..., l + sz - 1 are initialized to 0.
Fixpoint hinit (
l:
addr) (
sz:
nat) (
h:
heap) :
heap :=
match sz with O =>
h |
S sz =>
hupdate l 0 (
hinit (
l + 1)
sz h)
end.
Lemma hinit_inside:
forall h sz l l',
l <=
l' <
l +
Z.of_nat sz ->
hinit l sz h l' =
Some 0.
Proof.
induction sz;
intros;
cbn.
-
lia.
-
destruct (
Z.eq_dec l l');
auto.
apply IHsz.
lia.
Qed.
Lemma hinit_outside:
forall h sz l l',
l' <
l \/
l +
Z.of_nat sz <=
l' ->
hinit l sz h l' =
h l'.
Proof.
induction sz;
intros;
cbn.
-
auto.
-
destruct (
Z.eq_dec l l').
lia.
apply IHsz;
lia.
Qed.
The disjoint union of two heaps.
Definition hdisjoint (
h1 h2:
heap) :
Prop :=
forall l,
h1 l =
None \/
h2 l =
None.
Lemma hdisjoint_sym:
forall h1 h2,
hdisjoint h1 h2 <->
hdisjoint h2 h1.
Proof.
unfold hdisjoint;
intros;
split;
intros H l;
specialize (
H l);
tauto.
Qed.
Program Definition hunion (
h1 h2:
heap) :
heap :=
{|
contents :=
fun l =>
if h1 l then h1 l else h2 l |}.
Next Obligation.
destruct (
isfinite h1)
as (
i1 &
fin1), (
isfinite h2)
as (
i2 &
fin2).
exists (
Z.max i1 i2);
intros.
rewrite fin1,
fin2 by lia.
auto.
Qed.
Lemma hunion_comm:
forall h1 h2,
hdisjoint h1 h2 ->
hunion h2 h1 =
hunion h1 h2.
Proof.
intros;
apply heap_extensionality;
intros;
cbn.
specialize (
H l).
destruct (
h1 l), (
h2 l);
intuition congruence.
Qed.
Lemma hunion_assoc:
forall h1 h2 h3,
hunion (
hunion h1 h2)
h3 =
hunion h1 (
hunion h2 h3).
Proof.
Lemma hunion_empty:
forall h,
hunion hempty h =
h.
Proof.
Lemma hdisjoint_union_l:
forall h1 h2 h3,
hdisjoint (
hunion h1 h2)
h3 <->
hdisjoint h1 h3 /\
hdisjoint h2 h3.
Proof.
unfold hdisjoint,
hunion;
cbn;
intros.
split.
-
intros D;
split;
intros l;
destruct (
D l);
auto;
destruct (
h1 l);
auto.
discriminate.
-
intros [
D1 D2]
l.
destruct (
D2 l);
auto.
destruct (
h1 l)
eqn:
H1;
auto.
destruct (
D1 l);
auto.
congruence.
Qed.
Lemma hdisjoint_union_r:
forall h1 h2 h3,
hdisjoint h1 (
hunion h2 h3) <->
hdisjoint h1 h2 /\
hdisjoint h1 h3.
Proof.
Disjointness between three heaps.
Definition hdisj3 (
h1 h2 h3:
heap) :=
hdisjoint h1 h2 /\
hdisjoint h1 h3 /\
hdisjoint h2 h3.
A tactic to prove disjointness statements.
Ltac HDISJ :=
match goal with
| [
H:
hdisj3 _ _ _ |-
_ ] =>
destruct H as (? & ? & ?);
HDISJ
| [
H:
hdisjoint (
hunion _ _)
_ |-
_ ] =>
apply hdisjoint_union_l in H;
destruct H;
HDISJ
| [
H:
hdisjoint _ (
hunion _ _) |-
_ ] =>
apply hdisjoint_union_r in H;
destruct H;
HDISJ
| [ |-
hdisj3 _ _ _ ] =>
split; [|
split];
HDISJ
| [ |-
hdisjoint (
hunion _ _)
_ ] =>
apply hdisjoint_union_l;
split;
HDISJ
| [ |-
hdisjoint _ (
hunion _ _) ] =>
apply hdisjoint_union_r;
split;
HDISJ
| [ |-
hdisjoint hempty _ ] =>
red;
auto
| [ |-
hdisjoint _ hempty ] =>
red;
auto
| [ |-
hdisjoint _ _ ] =>
assumption || (
apply hdisjoint_sym;
assumption) ||
idtac
|
_ =>
idtac
end.
Lemma hunion_invert_r:
forall h1 h2 h,
hunion h h1 =
hunion h h2 ->
hdisjoint h h1 ->
hdisjoint h h2 ->
h1 =
h2.
Proof.
intros.
apply heap_extensionality;
intros l.
assert (
hunion h h1 l =
hunion h h2 l)
by congruence.
cbn in H2.
specialize (
H0 l);
specialize (
H1 l).
destruct (
h l);
intuition congruence.
Qed.
Lemma hunion_invert_l:
forall h1 h2 h,
hunion h1 h =
hunion h2 h ->
hdisjoint h1 h ->
hdisjoint h2 h ->
h1 =
h2.
Proof.
2. Assertions for separation logic
Definition assertion :
Type :=
heap ->
Prop.
Implication (entailment).
Definition aimp (
P Q:
assertion) :
Prop :=
forall h,
P h ->
Q h.
Notation "
P -->>
Q" := (
aimp P Q) (
at level 95,
no associativity).
Quantification.
Definition aexists {
A:
Type} (
P:
A ->
assertion) :
assertion :=
fun h =>
exists a:
A,
P a h.
Definition aforall {
A:
Type} (
P:
A ->
assertion) :
assertion :=
fun h =>
forall a:
A,
P a h.
The assertion "the heap is empty".
Definition emp :
assertion :=
fun h =>
h =
hempty.
The pure assertion: "the heap is empty and P holds".
Definition pure (
P:
Prop) :
assertion :=
fun h =>
P /\
h =
hempty.
The assertion "address l contains value v".
The domain of the heap must be the singleton {l}.
Definition contains (
l:
addr) (
v:
Z) :
assertion :=
fun h =>
h =
hupdate l v hempty.
The assertion "address l is valid" (i.e. in the domain of the heap).
Definition valid (
l:
addr) :
assertion :=
aexists (
contains l).
The separating conjunction.
Definition sepconj (
P Q:
assertion) :
assertion :=
fun h =>
exists h1 h2,
P h1
/\
Q h2
/\
hdisjoint h1 h2 /\
h =
hunion h1 h2.
Notation "
P **
Q" := (
sepconj P Q) (
at level 60,
right associativity).
The conjunction of a simple assertion and a general assertion.
Definition pureconj (
P:
Prop) (
Q:
assertion) :
assertion :=
fun h =>
P /\
Q h.
Notation "
P //\\
Q" := (
pureconj P Q) (
at level 60,
right associativity).
Plain conjunction and disjunction.
Definition aand (
P Q:
assertion) :
assertion :=
fun h =>
P h /\
Q h.
Definition aor (
P Q:
assertion) :
assertion :=
fun h =>
P h \/
Q h.
Extensional equality between assertions.
Lemma assertion_extensionality:
forall (
P Q:
assertion),
(
forall h,
P h <->
Q h) ->
P =
Q.
Proof.
Properties of separating conjunction
Lemma sepconj_comm:
forall P Q,
P **
Q =
Q **
P.
Proof.
assert (
forall P Q h, (
P **
Q)
h -> (
Q **
P)
h).
{
intros P Q h (
h1 &
h2 &
P1 &
Q2 &
EQ &
DISJ);
subst h.
exists h2,
h1;
intuition auto.
apply hdisjoint_sym;
auto.
symmetry;
apply hunion_comm;
auto. }
intros;
apply assertion_extensionality;
intros;
split;
auto.
Qed.
Lemma sepconj_assoc:
forall P Q R, (
P **
Q) **
R =
P ** (
Q **
R).
Proof.
Lemma sepconj_emp:
forall P,
emp **
P =
P.
Proof.
Lemma sepconj_imp_l:
forall P Q R,
(
P -->>
Q) -> (
P **
R -->>
Q **
R).
Proof.
intros P Q R IMP h (h1 & h2 & P1 & Q2 & D & U).
exists h1, h2; intuition auto.
Qed.
Lemma sepconj_imp_r:
forall P Q R,
(
P -->>
Q) -> (
R **
P -->>
R **
Q).
Proof.
Other useful logical properties
Lemma pure_sep:
forall P Q,
pure (
P /\
Q) =
pure P **
pure Q.
Proof.
Lemma pureconj_sepconj:
forall P Q,
pure P **
Q =
P //\\
Q.
Proof.
Lemma lift_pureconj:
forall P Q R, (
P //\\
Q) **
R =
P //\\ (
Q **
R).
Proof.
Lemma lift_aexists:
forall (
A:
Type) (
P:
A ->
assertion)
Q,
aexists P **
Q =
aexists (
fun x =>
P x **
Q).
Proof.
intros;
apply assertion_extensionality;
intros;
split.
-
intros (
h1 &
h2 & (
a &
P1) &
Q2 &
DISJ &
EQ).
exists a,
h1,
h2;
auto.
-
intros (
a &
h1 &
h2 &
P1 &
Q2 &
DISJ &
EQ).
exists h1,
h2;
intuition auto.
exists a;
auto.
Qed.
Lemma sepconj_swap3:
forall R P Q,
P **
Q **
R =
R **
P **
Q.
Proof.
Lemma sepconj_swap4:
forall S P Q R,
P **
Q **
R **
S =
S **
P **
Q **
R.
Proof.
Lemma sepconj_pick2:
forall Q P R,
P **
Q **
R =
Q **
P **
R.
Proof.
Lemma sepconj_pick3:
forall R P Q S,
P **
Q **
R **
S =
R **
P **
Q **
S.
Proof.
Magic wand
Definition wand (
P Q:
assertion) :
assertion :=
fun h =>
forall h',
hdisjoint h h' ->
P h' ->
Q (
hunion h h').
Notation "
P --*
Q" := (
wand P Q) (
at level 70,
right associativity).
Lemma wand_intro:
forall P Q R,
P **
Q -->>
R ->
P -->>
Q --*
R.
Proof.
intros P Q R IMP h Ph h' DISJ Qh'.
apply IMP. exists h, h'; auto.
Qed.
Lemma wand_cancel:
forall P Q,
P ** (
P --*
Q) -->>
Q.
Proof.
Lemma wand_charact:
forall P Q,
(
P --*
Q) = (
aexists (
fun R => (
P **
R -->>
Q) //\\
R)).
Proof.
Lemma wand_equiv:
forall P Q R,
(
P -->> (
Q --*
R)) <-> (
P **
Q -->>
R).
Proof.
intros;
split;
intros H.
-
intros h (
h1 &
h2 &
Ph1 &
Wh2 &
D &
U).
subst h.
apply H;
auto.
-
apply wand_intro;
auto.
Qed.
Lemma wand_imp_l:
forall P P'
Q,
(
P' -->>
P) -> (
P --*
Q -->>
P' --*
Q).
Proof.
intros. intros h W h' DISJ P'h'. apply W; auto.
Qed.
Lemma wand_imp_r:
forall P Q Q',
(
Q -->>
Q') -> (
P --*
Q -->>
P --*
Q').
Proof.
intros. intros h W h' DISJ Ph'. apply H; apply W; auto.
Qed.
Lemma wand_idem:
forall P,
emp -->>
P --*
P.
Proof.
intros P h E.
rewrite E.
red.
intros.
rewrite hunion_empty.
auto.
Qed.
Lemma wand_pure_l:
forall (
P:
Prop)
Q,
P -> (
pure P --*
Q) =
Q.
Proof.
Lemma wand_curry:
forall P Q R,
(
P **
Q --*
R) = (
P --*
Q --*
R).
Proof.
intros;
apply assertion_extensionality;
intros h;
split.
-
intros W h1 D1 Ph1 h2 D2 Qh2.
rewrite hunion_assoc.
apply W.
HDISJ.
exists h1,
h2;
intuition auto.
HDISJ.
-
intros W h'
D (
h1 &
h2 &
Ph1 &
Qh2 &
D12 &
U12).
subst h'.
rewrite <-
hunion_assoc.
apply W.
HDISJ.
auto.
HDISJ.
auto.
Qed.
Lemma wand_star:
forall P Q R,
((
P --*
Q) **
R ) -->> (
P --* (
Q **
R)).
Proof.
intros;
intros h (
h1 &
h2 &
W1 &
R2 &
D &
U).
subst h.
intros h'
D'
Ph'.
exists (
hunion h1 h'),
h2;
intuition auto.
apply W1;
auto.
HDISJ.
HDISJ.
rewrite !
hunion_assoc.
f_equal.
apply hunion_comm.
HDISJ.
Qed.
Precise assertions
An assertion is precise if "it unambiguously carves out an area of the heap"
(in the words of Gotsman, Berdine, Cook, 2011).
Definition precise (
P:
assertion) :
Prop :=
forall h1 h2 h1'
h2',
hdisjoint h1 h2 ->
hdisjoint h1'
h2' ->
hunion h1 h2 =
hunion h1'
h2' ->
P h1 ->
P h1' ->
h1 =
h1'.
A parameterized assertion is precise if, in addition, the parameter
is uniquely determined as well.
Definition param_precise {
X:
Type} (
P:
X ->
assertion) :
Prop :=
forall x1 x2 h1 h2 h1'
h2',
hdisjoint h1 h2 ->
hdisjoint h1'
h2' ->
hunion h1 h2 =
hunion h1'
h2' ->
P x1 h1 ->
P x2 h1' ->
x1 =
x2 /\
h1 =
h1'.
Remark param_precise_precise:
forall (
X:
Type) (
P:
X ->
assertion),
param_precise P ->
forall x,
precise (
P x).
Proof.
intros; red; intros. edestruct (H x x h1 h2 h1' h2'); eauto.
Qed.
Remark precise_param_precise:
forall P,
precise P ->
param_precise (
fun _ :
unit =>
P).
Proof.
intros; red; intros. split. destruct x1, x2; auto. eauto.
Qed.
Lemma pure_precise:
forall P,
precise (
pure P).
Proof.
unfold pure;
intros;
red;
intros.
destruct H2,
H3.
congruence.
Qed.
Lemma pure_param_precise:
forall (
X:
Type) (
P:
X ->
Prop),
(
forall x1 x2,
P x1 ->
P x2 ->
x1 =
x2) ->
param_precise (
fun x =>
pure (
P x)).
Proof.
unfold pure;
intros;
red;
intros.
destruct H3,
H4.
split.
auto.
congruence.
Qed.
Lemma contains_param_precise:
forall l,
param_precise (
fun v =>
contains l v).
Proof.
unfold contains;
intros;
red;
intros.
assert (
E:
hunion h1 h2 l =
hunion h1'
h2'
l)
by congruence.
cbn in E.
subst h1 h1'.
rewrite !
hupdate_same in E.
replace x2 with x1 by congruence.
auto.
Qed.
Lemma contains_precise:
forall l v,
precise (
contains l v).
Proof.
Lemma aexists_precise:
forall (
X:
Type) (
P:
X ->
assertion),
param_precise P ->
precise (
aexists P).
Proof.
intros; red; intros. destruct H3 as (x1 & P1), H4 as (x2 & P2).
eapply H; eauto.
Qed.
Lemma valid_precise:
forall l,
precise (
valid l).
Proof.
Lemma sepconj_param_precise:
forall (
X:
Type) (
P Q:
X ->
assertion),
param_precise P -> (
forall x,
precise (
Q x)) ->
param_precise (
fun x =>
P x **
Q x).
Proof.
intros;
red;
intros.
destruct H4 as (
h3 &
h4 &
P3 &
Q4 &
D &
E).
destruct H5 as (
h3' &
h4' &
P3' &
Q4' &
D' &
E').
subst h1 h1'.
assert (
x1 =
x2 /\
h3 =
h3').
{
apply H with (
hunion h4 h2) (
hunion h4'
h2');
auto.
HDISJ.
HDISJ.
rewrite <- !
hunion_assoc.
auto. }
destruct H4.
subst x2.
assert (
h4 =
h4').
{
apply (
H0 x1)
with (
hunion h3 h2) (
hunion h3'
h2');
auto.
HDISJ.
HDISJ.
rewrite <- !
hunion_assoc.
rewrite (
hunion_comm h3)
by HDISJ.
rewrite (
hunion_comm h3')
by HDISJ.
auto.
}
subst;
auto.
Qed.
Lemma sepconj_precise:
forall P Q,
precise P ->
precise Q ->
precise (
P **
Q).
Proof.
Distributivity laws for precise assertions.
Lemma sepconj_and_distr_1:
forall P1 P2 Q,
aand P1 P2 **
Q -->>
aand (
P1 **
Q) (
P2 **
Q).
Proof.
intros P1 P2 Q h (h1 & h2 & (P11 & P21) & Q2 & D & E); split; exists h1, h2; auto.
Qed.
Lemma sepconj_and_distr_2:
forall P1 P2 Q,
precise Q ->
aand (
P1 **
Q) (
P2 **
Q) -->>
aand P1 P2 **
Q.
Proof.
intros P1 P2 Q PQ.
rewrite (
sepconj_comm P1), (
sepconj_comm P2).
intros h ((
h1 &
h2 &
Q1 &
P12 &
D &
E) & (
h1' &
h2' &
Q1' &
P22 &
D' &
E')).
assert (
h1 =
h1').
{
apply PQ with h2 h2';
auto.
HDISJ.
HDISJ.
congruence. }
subst h1'.
assert (
h2 =
h2').
{
apply hunion_invert_r with h1;
auto;
congruence. }
subst h2'.
unfold aand;
exists h2,
h1;
intuition auto.
HDISJ.
rewrite hunion_comm by HDISJ;
auto.
Qed.
Self-conjunction law for precise assertions.
Lemma sepconj_self:
forall P,
precise P ->
P **
P -->>
P.
Proof.
intros.
intros h (
h1 &
h2 &
P1 &
P2 &
D &
E).
assert (
h1 =
h2). {
apply H with h2 h1;
auto.
HDISJ.
apply hunion_comm;
HDISJ. }
subst h2.
assert (
h =
h1). {
apply heap_extensionality;
intros l.
rewrite E;
cbn.
destruct (
h1 l);
auto. }
congruence.
Qed.