Commit ec399a74 authored by Robbert Krebbers's avatar Robbert Krebbers

Make distinction between malloced memory and local variables.

- Have a version of bind that allocates a local variable, and
  automatically frees it at the end.
- Only malloced memory can be freed manually.
parent 2224eaf2
......@@ -67,7 +67,7 @@ Section a_wp.
Definition flock_resources (γ : flock_name) (I : gmap prop_id lock_res) :=
([ map] i X I, flock_res amonadN γ i X)%I.
(** DF: The outer `WP` here is needed to be able to preform some reductions inside a heap_lang context.
(** DF: The outer `WP` here is needed to be able to perform some reductions inside a heap_lang context.
Without this, the `a_wp_awp` rule is not provable.
My intuitive explanation: we want to preform some reductions to `e` until it is actually a value that is a monadic computation.
......
......@@ -12,7 +12,7 @@ Definition a_alloc : val := λ: "x1" "x2",
let: "n" := Fst "vv" in
let: "v" := Snd "vv" in
assert: (#0 < "n");;
a_atomic_env (λ: <>, SOME (ref (SOME (lreplicate "n" "v")), #0)).
a_atomic_env (λ: <>, SOME (ref (SOME (#true, lreplicate "n" "v")), #0)).
Notation "'allocᶜ' ( e1 , e2 )" := (a_alloc e1%E e2%E) (at level 80) : expr_scope.
Definition a_free_check : val :=
......@@ -32,7 +32,11 @@ Definition a_free : val := λ: "x",
let: "i" := Snd "li" in
match: !"l" with
NONE => assert: #false (* location already freed, double free *)
| SOME "ll" =>
| SOME "kll" =>
let: "k" := Fst "kll" in
let: "ll" := Snd "kll" in
(* Make sure its not a block scoped variable *)
assert: ("k" = #true);;
(* We need to make sure `i = 0` and that all `0 ... length of block`
are unlocked. *)
assert: ("i" = #0);;
......@@ -56,7 +60,7 @@ Definition a_store : val := λ: "x1" "x2",
mset_add ("l", "i") "env" ;;
match: !"l" with
NONE => assert: #false (* store after free *)
| SOME "ll" => "l" <- SOME (linsert "i" "v" "ll") ;; "v"
| SOME "kll" => "l" <- SOME (Fst "kll", linsert "i" "v" (Snd "kll")) ;; "v"
end
end
).
......@@ -73,7 +77,7 @@ Definition a_load : val := λ: "x",
assert: (mset_member ("l", "i") "env" = #false);;
match: !"l" with
NONE => assert: #false (* load after free *)
| SOME "ll" => llookup "i" "ll"
| SOME "kll" => llookup "i" (Snd "kll")
end
end
).
......@@ -94,6 +98,16 @@ Notation "e1 ;ᶜ e2" :=
(at level 100, e2 at level 200,
format "'[' '[hv' '[' e1 ']' ;ᶜ ']' '/' e2 ']'") : expr_scope.
Definition a_mut_bind : val := λ: "x" "f",
"v" ←ᶜ "x" ;
"l" ←ᶜ a_atomic_env (λ: <>, ref (SOME (#false, lreplicate #1 "v"))) ;;
"b" ←ᶜ "f" (SOME ("l", #0)) ;;
a_atomic_env (λ: <>, "l" <- NONE) ;;
a_ret "b".
Notation "x ←mutᶜ e1 ;ᶜ e2" :=
(a_mut_bind e1 (λ: x, e2))%E
(at level 100, e1 at next level, e2 at level 200, right associativity) : expr_scope.
Definition a_if : val := λ: "cnd" "e1" "e2",
(* sequenced binds needed here; there should be sequence point after the conditional *)
"c" ←ᶜ "cnd" ;
......@@ -208,7 +222,7 @@ Section proofs.
AWP e2 @ R {{ Ψ2 }} -
( v1 v2, Ψ1 v1 - Ψ2 v2 - n : nat,
v1 = #n n 0%nat
cl, block_info cl n - cl C replicate n v2 - Φ (cloc_to_val cl)) -
cl, block_info cl true n - cl C replicate n v2 - Φ (cloc_to_val cl)) -
AWP alloc(e1, e2) @ R {{ Φ }}.
Proof.
iIntros "H1 H2 HΦ".
......@@ -237,7 +251,7 @@ Section proofs.
Lemma a_free_spec R Φ e :
AWP e @ R {{ v, cl ws, v = cloc_to_val cl
block_info cl (length ws) cl C ws Φ #() }} -
block_info cl true (length ws) cl C ws Φ #() }} -
AWP free(e) @ R {{ Φ }}.
Proof.
iIntros "H".
......@@ -254,8 +268,9 @@ Section proofs.
iMod (full_locking_heap_free_upd with "Hσ Hinfo Hcl")
as (ll Hoff Hl) "[Hl Hclose]".
wp_load. wp_pures. rewrite Hoff.
wp_apply wp_assert; wp_pures; iSplit; first done.
iNext; wp_pures. wp_apply (llength_spec with "[//]"); iIntros "_"; wp_pures.
wp_apply wp_assert; wp_pures; iSplit; first done. iNext.
wp_apply wp_assert; wp_pures; iSplit; first done. iNext.
wp_pures. wp_apply (llength_spec with "[//]"); iIntros "_"; wp_pures.
iAssert ( Ψ (n : nat), n length ws
(is_mset env X - Ψ #()) - WP a_free_check env #(cloc_base cl) #n {{ Ψ }})%I
with "[Hlocks]" as "Hcheck".
......@@ -300,11 +315,11 @@ Section proofs.
iDestruct (mapsto_offset_non_neg with "Hl") as %?.
assert ((#(cloc_base cl), #(cloc_offset cl))%V X) as HclX.
{ intros Hcl. destruct (HX _ Hcl) as (cl'&[=]%cloc_to_of_val&?). naive_solver. }
iMod (full_locking_heap_store_upd with "Hσ Hl") as (ll vs Hl Hi) "[Hl Hclose]".
iMod (full_locking_heap_store_upd with "Hσ Hl") as (k ll vs Hl Hi) "[Hl Hclose]".
wp_pures. rewrite cloc_to_val_eq. wp_pures.
wp_apply (mset_add_spec with "[$]"); first done.
iIntros "Hlocks /="; wp_pures.
wp_load; wp_match.
wp_load; wp_pures.
iEval (rewrite -(Z2Nat.id (cloc_offset cl)) //).
wp_apply (linsert_spec with "[//]"); [eauto|]. iIntros (ll' Hl').
iApply wp_fupd. wp_store.
......@@ -329,7 +344,7 @@ Section proofs.
iDestruct (full_locking_heap_unlocked with "Hl Hσ") as %Hv.
assert ((#(cloc_base cl), #(cloc_offset cl))%V X) as HclX.
{ intros Hcl. destruct (HX _ Hcl) as (?&[=]%cloc_to_of_val&?); naive_solver. }
iMod (full_locking_heap_load_upd with "Hσ Hl") as (ll vs Hl Hi) "[Hl Hclose]".
iMod (full_locking_heap_load_upd with "Hσ Hl") as (k ll vs Hl Hi) "[Hl Hclose]".
wp_pures. rewrite cloc_to_val_eq. wp_pures.
wp_apply wp_assert. wp_apply (mset_member_spec with "Hlocks"); iIntros "Hlocks /=".
rewrite bool_decide_false //.
......@@ -384,6 +399,53 @@ Section proofs.
iApply (awp_wand with "H"); iIntros (v) "H !>". by awp_lam.
Qed.
(* Internal spec: breaks the abstraction/notation *)
Lemma a_mut_bind_spec' R Φ e (f: val) :
AWP e @ R {{ v, U ( cl,
cl C v -
AWP f (cloc_to_val cl) @ R {{ w, v', cl C v' Φ w }}) }} -
AWP a_mut_bind e f @ R {{ Φ }}.
Proof.
iIntros "H".
awp_apply (a_wp_awp with "H"); iIntros (ev) "H". awp_lam. awp_pures.
iApply a_seq_bind_spec'; iApply (awp_wand with "H"); iIntros (v) "H !>".
awp_pures. iApply awp_bind.
awp_apply awp_atomic_env; iIntros (env) "Henv $". iApply wp_fupd.
iDestruct "Henv" as (X σ HX) "[Hlocks Hσ]". wp_pures.
wp_apply (lreplicate_spec 1 with "[//]"); iIntros (ll Hll).
wp_alloc l as "Hl".
iMod (full_locking_heap_alloc_upd with "Hσ Hl") as (?) "(Hσ & Hinfo & Hl)"=> //=.
iDestruct "Hl" as "[Hl _]".
iIntros "!> !>". iSplitL "Hlocks Hσ".
{ iExists X, _. iFrame.
iIntros "!%". intros w Hw. destruct (HX _ Hw) as (cl&Hcl&Hin).
exists cl; split; first done. by rewrite locked_locs_alloc_unlocked. }
iSpecialize ("H" with "Hl"). rewrite cloc_to_val_eq /=.
awp_pures. iApply awp_bind. awp_pures. iApply (awp_wand with "H").
iIntros (w). iDestruct 1 as (v') "[Hl H]". awp_pures. iApply awp_bind.
awp_apply awp_atomic_env; iIntros (env') "Henv $". iApply wp_fupd.
iDestruct "Henv" as (X' σ' HX') "[Hlock Hσ]". wp_pures.
iDestruct (full_locking_heap_unlocked with "Hl Hσ") as %?.
iMod (full_locking_heap_free_upd _ _ [_] with "Hσ Hinfo [$Hl //]")
as (ll' _ _) "[Hll Hclose] /=".
wp_store. iIntros "!> !>". iSplitL "Hlock Hll Hclose".
{ iExists X', _. iFrame "Hlock". iSplit; last by iApply "Hclose".
iPureIntro; intros w' Hw'. destruct (HX' _ Hw') as (cl'&Hcl&Hin).
exists cl'; split; first done. rewrite locked_locs_delete. set_solver. }
awp_pures. iApply awp_ret. by iApply wp_value.
Qed.
Lemma a_mut_bind_spec R Φ x e1 e2 :
AWP e1 @ R {{ v, U ( cl,
cl C v -
AWP subst' x (cloc_to_val cl) e2 @ R {{ w, v', cl C v' Φ w }}) }} -
AWP x mut e1 ; e2 @ R {{ Φ }}.
Proof.
iIntros "H". awp_pures. iApply a_mut_bind_spec'.
iApply (awp_wand with "H"); iIntros (v) "H !>"; iIntros (cl) "Hcl".
awp_lam. by iApply "H".
Qed.
Lemma a_if_spec R Φ c e1 e2 :
AWP c @ R {{ v, (v = #true U (AWP e1 @ R {{ Φ }}))
(v = #false U (AWP e2 @ R {{ Φ }})) }} -
......
This diff is collapsed.
From iris_c.vcgen Require Import proofmode.
Definition memcpy : val := λ: "arg",
"p" a_alloc 1 (a_ret (Fst "arg"));
"q" a_alloc 1 (a_ret (Fst (Snd "arg")));
"p" mut a_ret (Fst "arg");
"q" mut a_ret (Fst (Snd "arg"));
"n" ←ᶜ a_ret (Snd (Snd "arg"));
"pend" ←ᶜ ∗ᶜ (a_ret "p") +∗ᶜ (a_ret "n");
while (∗ᶜ (a_ret "p") <∗ᶜ a_ret "pend") {
......@@ -15,15 +15,15 @@ Lemma memcpy_spec `{amonadG Σ} lxs lys xs ys n R :
lxs C xs - lys C ys -
AWP memcpy (cloc_to_val lxs, (cloc_to_val lys, #n))%V @ R {{ _, lxs C ys lys C ys }}.
Proof.
iIntros (Hlen <-) "Hxs Hys". awp_lam. vcg.
iIntros (p) "_ _"; iIntros (q) "_ _ Hp Hq".
iApply (awp_wand _ (λ _, lxs C ys lys C ys)%I with "[-]");
[|iIntros (v) "H"; by vcg_continue].
iIntros (Hlen <-) "Hxs Hys". awp_lam. vcg. iIntros (p q) "Hp Hq".
iApply (awp_wand _ (λ _, p' q', p C p' q C q'
lxs C ys lys C ys)%I with "[-]"); last first.
{ iIntros (v). iDestruct 1 as (p' q') "[??]". by vcg_continue. }
iInduction xs as [|x xs] "IH" forall (lxs lys ys Hlen);
destruct ys as [|y ys]; simplify_eq/=; first by vcg; eauto.
destruct ys as [|y ys]; simplify_eq/=; first by vcg; eauto 10 with iFrame.
vcg; iIntros "!> !> !> Hx Hy Hp Hq".
iSpecialize ("IH" $! (lxs + 1) (lys + 1) with "[//] [$] [$] [$] [$]").
rewrite !cloc_plus_plus -(Nat2Z.inj_add 1).
iApply (awp_wand with "IH").
iIntros (v) "[??]"; vcg_continue; eauto with iFrame.
iIntros (v). iDestruct 1 as (p' q') "[??]". vcg_continue; eauto with iFrame.
Qed.
......@@ -63,6 +63,7 @@ Inductive dexpr : Type :=
Inductive dcexpr : Type :=
| dCRet : dexpr dcexpr
| dCSeqBind : binder dcexpr dcexpr dcexpr
| dCMutBind : binder dcexpr dcexpr dcexpr
| dCAlloc : dcexpr dcexpr dcexpr
| dCLoad : dcexpr dcexpr
| dCStore : dcexpr dcexpr dcexpr
......@@ -78,7 +79,7 @@ Inductive dcexpr : Type :=
Fixpoint dcexpr_size (de : dcexpr) : nat :=
match de with
| dCRet _ | dCUnknown _ => 1
| dCSeqBind _ de1 de2 | dCAlloc de1 de2 | dCStore de1 de2
| dCSeqBind _ de1 de2 | dCMutBind _ de1 de2 |dCAlloc de1 de2 | dCStore de1 de2
| dCBinOp _ de1 de2 | dCPreBinOp _ de1 de2 | dCPar de1 de2
| dCWhile de1 de2 | dCWhileV de1 de2 => S (dcexpr_size de1 + dcexpr_size de2)
| dCLoad de | dCUnOp _ de | dCCall _ de => S (dcexpr_size de)
......@@ -142,7 +143,7 @@ Fixpoint dexpr_wf (E : known_locs) (de : dexpr) : bool :=
Fixpoint dcexpr_wf (E : known_locs) (de : dcexpr) : bool :=
match de with
| dCRet de => dexpr_wf E de
| dCSeqBind x de1 de2 => dcexpr_wf E de1 && dcexpr_wf E de2
| dCSeqBind _ de1 de2 | dCMutBind _ de1 de2 => dcexpr_wf E de1 && dcexpr_wf E de2
| dCLoad de1 | dCUnOp _ de1 | dCCall _ de1 => dcexpr_wf E de1
| dCAlloc de1 de2 | dCStore de1 de2 | dCBinOp _ de1 de2
| dCPreBinOp _ de1 de2 | dCWhile de1 de2 | dCWhileV de1 de2 | dCPar de1 de2 =>
......@@ -243,6 +244,7 @@ Fixpoint dcexpr_interp (E : known_locs) (de : dcexpr) : expr :=
match de with
| dCRet de => a_ret (dexpr_interp E de)
| dCSeqBind x de1 de2 => x ←ᶜ (dcexpr_interp E de1) ; (dcexpr_interp E de2)
| dCMutBind x de1 de2 => x mut (dcexpr_interp E de1) ; (dcexpr_interp E de2)
| dCAlloc de1 de2 => a_alloc (dcexpr_interp E de1) (dcexpr_interp E de2)
| dCLoad de1 => a_load (dcexpr_interp E de1)
| dCStore de1 de2 => a_store (dcexpr_interp E de1) (dcexpr_interp E de2)
......@@ -765,6 +767,10 @@ Fixpoint dce_subst (E: known_locs) (x: string) (dv : dval) (dce : dcexpr) : dcex
if decide (BNamed x = y)
then dCSeqBind y (dce_subst E x dv de1) de2
else dCSeqBind y (dce_subst E x dv de1) (dce_subst E x dv de2)
| dCMutBind y de1 de2 =>
if decide (BNamed x = y)
then dCMutBind y (dce_subst E x dv de1) de2
else dCMutBind y (dce_subst E x dv de1) (dce_subst E x dv de2)
| dCAlloc de1 de2 => dCAlloc (dce_subst E x dv de1) (dce_subst E x dv de2)
| dCLoad de1 => dCLoad (dce_subst E x dv de1)
| dCStore de1 de2 => dCStore (dce_subst E x dv de1) (dce_subst E x dv de2)
......@@ -798,8 +804,8 @@ Lemma dcexpr_interp_subst' E mx de dv :
dcexpr_interp E (dce_subst' E mx dv de) = subst' mx (dval_interp E dv) (dcexpr_interp E de).
Proof. destruct mx; simpl; auto using dcexpr_interp_subst. Qed.
Lemma de_subst_wf E s dv1 de2 :
dval_wf E dv1 dexpr_wf E de2 dexpr_wf E (de_subst E s dv1 de2).
Lemma de_subst_wf E x dv1 de2 :
dval_wf E dv1 dexpr_wf E de2 dexpr_wf E (de_subst E x dv1 de2).
Proof.
induction de2; intros; repeat (simplify_eq /= || case_decide); naive_solver.
Qed.
......@@ -814,3 +820,24 @@ Qed.
Lemma dce_subst_wf' E mx dv1 de2 :
dval_wf E dv1 dcexpr_wf E de2 dcexpr_wf E (dce_subst' E mx dv1 de2).
Proof. destruct mx; simpl; auto using dce_subst_wf. Qed.
Lemma de_subst_mono E E' x dv1 de2 :
dval_wf E dv1 dexpr_wf E de2 E `prefix_of` E'
de_subst E x dv1 de2 = de_subst E' x dv1 de2.
Proof.
induction de2; intros; repeat (simplify_eq /= || case_decide);
naive_solver eauto using dval_interp_mono with f_equal.
Qed.
Lemma dce_subst_mono E E' x dv1 de2 :
dval_wf E dv1 dcexpr_wf E de2 E `prefix_of` E'
dce_subst E x dv1 de2 = dce_subst E' x dv1 de2.
Proof.
induction de2; intros; repeat (simplify_eq /= || case_decide);
naive_solver eauto using de_subst_mono, dval_interp_mono with f_equal.
Qed.
Lemma dce_subst_mono' E E' mx dv1 de2 :
dval_wf E dv1 dcexpr_wf E de2 E `prefix_of` E'
dce_subst' E mx dv1 de2 = dce_subst' E' mx dv1 de2.
Proof. destruct mx; simpl; auto using dce_subst_mono. Qed.
......@@ -181,6 +181,10 @@ Instance into_dcexpr_seq_bind E E' E'' x e1 e2 de1 de2 :
IntoDCExpr E E' e1 de1 IntoDCExpr E' E'' e2 de2
IntoDCExpr E E'' (x ←ᶜ e1 ; e2) (dCSeqBind x de1 de2).
Proof. solve_into_dcexpr2. Qed.
Instance into_dcexpr_mut_bind E E' E'' x e1 e2 de1 de2 :
IntoDCExpr E E' e1 de1 IntoDCExpr E' E'' e2 de2
IntoDCExpr E E'' (x mut e1 ; e2) (dCMutBind x de1 de2).
Proof. solve_into_dcexpr2. Qed.
Instance into_dcexpr_alloc E E' E'' e1 e2 de1 de2 :
IntoDCExpr E E' e1 de1 IntoDCExpr E' E'' e2 de2
IntoDCExpr E E'' (a_alloc e1 e2) (dCAlloc de1 de2).
......
......@@ -68,7 +68,7 @@ Section vcg.
''(ms1, mNew1, dv1) vcg_sp_aux E n ms de1;
''(ms2, mNew2, dv2) vcg_sp_aux E n ms1 de2;
Some (ms2, denv_merge mNew1 mNew2, (dPairV dv1 dv2))
| dCAlloc _ _, _ | dCWhile _ _, _ | dCWhileV _ _, _
| dCMutBind _ _ _, _ | dCAlloc _ _, _ | dCWhile _ _, _ | dCWhileV _ _, _
| dCCall _ _, _ | dCUnknown _, _ => None
end.
......@@ -99,13 +99,13 @@ Section vcg.
| Some di =>
cl,
let i := length E in
block_info cl (S (Z.to_nat (dint_interp di))) -
block_info cl true (S (Z.to_nat (dint_interp di))) -
(cl + 1) C replicate (Z.to_nat (dint_interp di)) (dval_interp E dv2) -
Φ (E ++ [cl]) (denv_insert i ULvl 1 dv2 m) (dLocV (dLoc (dLocKnown i) (dInt 0 None)))
| _ =>
wand_denv_interp E m ( n : nat,
dval_interp E dv1 = #n n O cl,
block_info cl n -
block_info cl true n -
cl C replicate n (dval_interp E dv2) -
vcg_wp_continuation E Φ (cloc_to_val cl))
end%I.
......@@ -206,7 +206,20 @@ Section vcg.
end
| dCSeqBind mx de1 de2, S n =>
vcg_wp E n m de1 R $ λ E m dv1,
let i := length E in
vcg_wp E n (denv_unlock m) (dce_subst' E mx dv1 de2) R Φ
| dCMutBind mx de1 de2, S n =>
vcg_wp E n m de1 R $ λ E m dv1, cl,
let i := length E in
let dlv := dLocV (dLoc (dLocKnown i) (dInt 0 None)) in
vcg_wp (E ++ [cl]) n
(denv_insert i ULvl 1 dv1 (denv_unlock m))
(dce_subst' (E ++ [cl]) mx dlv de2) R $ λ E m dv2,
match denv_delete_full i m with
| Some (m', _) => Φ E m' dv2
| _ => wand_denv_interp E m ( v,
cl C v vcg_wp_continuation E Φ (dval_interp E dv2))
end
| dCAlloc de1 de2, S n =>
match vcg_sp E n m de1 with
| Some (m', mNew, dv1) =>
......@@ -565,7 +578,7 @@ Section vcg_spec.
denv_interp E m -
vcg_wp_alloc E dv1 dv2 m Φ -
n : nat, dval_interp E dv1 = #n n O%nat
( cl, block_info cl n -
( cl, block_info cl true n -
cl C replicate n (dval_interp E dv2) -
vcg_wp_continuation E Φ (cloc_to_val cl)).
Proof.
......@@ -705,6 +718,37 @@ Section vcg_spec.
iDestruct ("IH" with "[] [] Hm H") as "H"; eauto using dcexpr_wf_mono.
iApply (awp_wand with "H"); iIntros (v) "H".
by iApply (vcg_wp_continuation_mono with "H").
- (* mut bind *)
iDestruct ("IH" with "[] [] Hm H") as "H"; eauto.
iApply a_mut_bind_spec. iApply (awp_wand with "H").
iIntros (v) "H"; iDestruct "H" as (E' dv m' -> ???) "[Hm H]".
iDestruct (denv_unlock_interp with "Hm") as "Hm"; iModIntro.
iIntros (cl) "Hcl". iSpecialize ("H" $! cl).
assert (E' `prefix_of` E' ++ [cl]) by eauto using prefix_app_r.
set (i := length E').
set (dlv := dLocV (dLoc (dLocKnown i) (dInt 0 None))).
assert (dval_wf (E' ++ [cl]) dlv).
{ rewrite /dlv /= /dloc_wf /dbase_loc_wf /= /dloc_var_wf.
by rewrite (list_lookup_middle _ []). }
iDestruct ("IH" with "[%] [%] [Hm Hcl] H") as "H".
{ eauto using dcexpr_wf_mono. }
{ eauto using denv_wf_insert_extend. }
{ iApply denv_insert_interp; eauto using denv_wf_mono.
rewrite /dloc_var_interp (list_lookup_middle _ []) //=.
rewrite -(denv_interp_mono E'); eauto.
rewrite -(dval_interp_mono E'); eauto. iFrame. }
rewrite dcexpr_interp_subst' /= /dloc_var_interp (list_lookup_middle _ []) //=.
rewrite -(dcexpr_interp_mono E); eauto.
iApply (awp_wand with "H").
iIntros (w) "H"; iDestruct "H" as (E'' dw m'' -> ???) "[Hm H]".
destruct (denv_delete_full i m'') as [[m''' dw']|] eqn:?.
+ iDestruct (denv_delete_full_interp with "Hm") as "[Hm Hl]"; first done.
rewrite -(dloc_var_interp_mono (E' ++ [cl])) /dloc_var_interp /=; eauto.
rewrite (list_lookup_middle _ []) //=. iExists _; iFrame "Hl".
iApply vcg_wp_continuation_mono;
[|iApply (vcg_wp_continuation_correct with "H")]; eauto.
+ iDestruct (wand_denv_interp_spec with "H Hm") as (v) "[Hl H]".
iExists _; iFrame "Hl". iApply (vcg_wp_continuation_mono with "H"); eauto.
- (* alloc *)
destruct (vcg_sp _ _ m _) as [[[m' mNew] dv1]|] eqn:?; last first.
+ destruct (vcg_sp _ _ m de2) as [[[m' mNew] dv2]|] eqn:?; last first.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment