You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

635 lines
15 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

From iris.proofmode Require Import tactics.
From iris.heap_lang Require Import lang primitive_laws notation.
From iris.base_logic Require Import invariants.
From semantics.pl.heap_lang Require Import adequacy proofmode primitive_laws_nolater.
From semantics.pl Require Import hoare_lib.
From semantics.pl.program_logic Require Import notation.
(** ** Magic is in the air *)
Import hoare.
Check ent_wand_intro.
Check ent_wand_elim.
Section primitive.
Implicit Types (P Q R: iProp).
Lemma ent_or_sep_dist P Q R :
(P Q) R (P R) (Q R).
Proof.
apply ent_wand_elim.
apply ent_or_elim.
- apply ent_wand_intro. apply ent_or_introl.
- apply ent_wand_intro. apply ent_or_intror.
Qed.
(** Exercise 1 *)
Lemma ent_carry_res P Q :
P Q - (P Q).
Proof.
apply ent_wand_intro.
reflexivity.
Qed.
Lemma ent_comm_premise P Q R :
(Q - P - R) P - Q - R.
Proof.
apply ent_wand_intro.
apply ent_wand_intro.
rewrite <-ent_sep_assoc.
rewrite (ent_sep_comm P Q).
rewrite ent_sep_assoc.
apply ent_wand_elim.
apply ent_wand_elim.
reflexivity.
Qed.
Lemma ent_or_comm P Q :
P Q Q P.
Proof.
apply bi.equiv_entails; constructor.
- apply ent_or_elim.
+ apply ent_or_intror.
+ apply ent_or_introl.
- apply ent_or_elim.
+ apply ent_or_intror.
+ apply ent_or_introl.
Qed.
Lemma ent_or_cancel_left P Q Q' :
(Q Q') P Q P Q'.
Proof.
intro H.
apply ent_or_elim.
- apply ent_or_introl.
- etrans; [ apply H | apply ent_or_intror ].
Qed.
Lemma ent_sep_or_disj2 P Q R :
(P R) (Q R) (P Q) R.
Proof.
apply ent_wand_elim.
apply ent_or_elim.
- apply ent_wand_intro.
rewrite ent_sep_comm.
etrans; [ apply ent_or_sep_dist | ].
rewrite ent_sep_comm.
apply ent_or_cancel_left.
apply ent_sep_weaken.
- apply ent_wand_intro.
rewrite ent_sep_comm.
etrans; [ apply ent_or_sep_dist | ].
rewrite ent_sep_comm.
etrans; first apply ent_or_elim; [ apply ent_sep_weaken | apply ent_sep_weaken | apply ent_or_intror ].
Qed.
End primitive.
(** ** Using the IPM *)
Implicit Types
(P Q R: iProp)
(Φ Ψ : val iProp)
.
Lemma or_elim P Q R:
(P R)
(Q R)
(P Q) R.
Proof.
iIntros (H1 H2) "[P|Q]".
- iApply H1.
iExact "P".
- by iApply H2.
Qed.
Lemma or_intro_l P Q:
P P Q.
Proof.
iIntros "P". iLeft. iFrame "P".
(* [iExact] corresponds to Coq's [exact] *)
Restart.
iIntros "P". iLeft. iExact "P".
(* [iAssumption] can solve the goal if there is an exact match in the context *)
Restart.
iIntros "P". iLeft. iAssumption.
(* This directly frames the introduced proposition. The IPM will automatically try to pick a disjunct. *)
Restart.
iIntros "$".
Qed.
Lemma or_sep P Q R: (P Q) R (P R) (Q R).
Proof.
(* we first introduce, destructing the separating conjunction *)
iIntros "[HPQ HR]".
iDestruct "HPQ" as "[HP | HQ]".
- iLeft. iFrame.
- iRight. iFrame.
(* we can also split more explicitly *)
Restart.
iIntros "[HPQ HR]".
iDestruct "HPQ" as "[HP | HQ]".
- iLeft.
(* [iSplitL] uses the given hypotheses to prove the left conjunct and the rest for the right conjunct *)
(* symmetrically, [iSplitR] gives the specified hypotheses to the right *)
iSplitL "HP". all: iAssumption.
- iRight.
(* if we don't give any hypotheses, everything will go to the left. *)
iSplitL. (* now we're stuck... *)
(* alternative: directly destruct the disjunction *)
Restart.
(* iFrame will also directly pick the disjunct *)
iIntros "[[HP | HQ] HR]"; iFrame.
Abort.
(* Using entailments *)
Lemma or_sep P Q R: (P Q) R (P R) (Q R).
Proof.
iIntros "[HPQ HR]". iDestruct "HPQ" as "[HP | HQ]".
- (* this will make the entailment ⊢ into a wand *)
iPoseProof (ent_or_introl (P R) (Q R)) as "-#Hor".
iApply "Hor". iFrame.
- (* we can also directly apply the entailment *)
iApply ent_or_intror.
iFrame.
Abort.
(* Proving pure Coq propositions *)
Lemma prove_pure P : P 42 > 0.
Proof.
iIntros "HP".
(* [iPureIntro] will switch to a Coq goal, of course losing access to the Iris context *)
iPureIntro. lia.
Abort.
(* Destructing assumptions *)
Lemma destruct_ex {X} (p : X Prop) (Φ : X iProp) : ( x : X, p x Φ x) False.
Proof.
(* we can lead the identifier with a [%] to introduce to the Coq context *)
iIntros "[%w Hw]".
iDestruct "Hw" as "[%Hw1 Hw2]".
(* more compactly: *)
Restart.
iIntros "(%w & %Hw1 & Hw2)".
Restart.
(* we cannot introduce an existential to the Iris context *)
Fail iIntros "(w & Hw)".
Restart.
iIntros "(%w & Hw1 & Hw2)".
(* if we first introduce a pure proposition into the Iris context,
we can later move it to the Coq context *)
iDestruct "Hw1" as "%Hw1".
Abort.
(* Specializing assumptions *)
Lemma specialize_assum P Q R :
P -
R -
(P R - (P R) (Q R)) -
(P R) (Q R).
Proof.
iIntros "HP HR Hw".
iSpecialize ("Hw" with "[HR HP]").
{ iFrame. }
iAssumption.
Restart.
iIntros "HP HR Hw".
(* we can also directly frame it *)
iSpecialize ("Hw" with "[$HR $HP]").
Restart.
iIntros "HP HR Hw".
(* we can let it frame all hypotheses *)
iSpecialize ("Hw" with "[$]").
Restart.
(* we can also use [iPoseProof], and introduce the generated hypothesis with [as] *)
iIntros "HP HR Hw".
iPoseProof ("Hw" with "[$HR $HP]") as "$".
Restart.
iIntros "HP HR Hw".
(* [iApply] can similarly be specialized *)
iApply ("Hw" with "[$HP $HR]").
Abort.
(* Nested specialization *)
Lemma specialize_nested P Q R :
P -
(P - R) -
(R - Q) -
Q.
Proof.
iIntros "HP HPR HRQ".
(* we can use the pattern with round parentheses to specialize a hypothesis in a nested way *)
iSpecialize ("HRQ" with "(HPR HP)").
(* can finish the proof with [iExact] *)
iExact "HRQ".
(* of course, this also works for [iApply] *)
Restart.
iIntros "HP HPR HRQ". iApply ("HRQ" with "(HPR HP)").
Abort.
(* Existentials *)
Lemma prove_existential (Φ : nat iProp) :
Φ 1337 - n m, n > 41 Φ m.
Proof.
(* [iExists] can instantiate existentials *)
iIntros "Ha".
iExists 42.
iExists 1337. iSplitR. { iPureIntro. lia. } iFrame.
Restart.
iIntros "Ha". iExists 42, 1337.
(* [iSplit] works if the goal is a conjunction or one of the separating conjuncts is pure.
In that case, the hypotheses will be available for both sides. *)
iSplit.
Restart.
iIntros "Ha". iExists 42, 1337. iSplitR; iFrame; iPureIntro. lia.
Abort.
(* specializing universals *)
Lemma specialize_universal (Φ : nat iProp) :
( n, n = 42 - Φ n) - Φ 42.
Proof.
iIntros "Hn".
(* we can use [$!] to specialize Iris hypotheses with pure Coq terms *)
iSpecialize ("Hn" $! 42).
iApply "Hn". done.
Restart.
iIntros "Hn".
(* we can combine this with [with] patterns. The [%] pattern will generate a pure Coq goal. *)
iApply ("Hn" $! 42 with "[%]").
done.
Restart.
iIntros "Hn".
(* ...and ending the pattern with // will call done *)
iApply ("Hn" $! 42 with "[//]").
Abort.
Section without_ipm.
(** Prove the following entailments without using the IPM. *)
(** Exercise 2 *)
Lemma ent_lem1 P Q :
True P - Q - P Q.
Proof.
repeat apply ent_wand_intro.
rewrite <-ent_sep_assoc.
rewrite ent_sep_comm.
apply ent_sep_weaken.
Qed.
Lemma ent_lem2 P Q :
P (P - Q) Q.
Proof.
rewrite ent_sep_comm.
apply ent_wand_elim.
reflexivity.
Qed.
Lemma ent_lem3 P Q R :
(P Q) R - (P R) (Q R).
Proof.
apply ent_wand_intro.
apply ent_or_sep_dist.
Qed.
End without_ipm.
Lemma ent_lem1_ipm P Q :
True P - Q - P Q.
Proof.
iIntros "_ HP HQ".
iSplitL "HP".
all: iAssumption.
Qed.
Lemma ent_lem2_ipm P Q :
P (P - Q) Q.
Proof.
iIntros "[HP HPQ]".
iSpecialize ("HPQ" with "HP").
iAssumption.
Qed.
Lemma ent_lem3_ipm P Q R :
(P Q) R - (P R) (Q R).
Proof.
iIntros "[HP | HQ] HR".
- iLeft.
iFrame.
- iRight.
iFrame.
Qed.
(** Weakest precondition rules *)
Check ent_wp_value.
Check ent_wp_wand.
Check ent_wp_bind.
Check ent_wp_pure_step.
Check ent_wp_new.
Check ent_wp_load.
Check ent_wp_store.
Lemma ent_wp_pure_steps e e' Φ :
rtc pure_step e e'
WP e' {{ Φ }} WP e {{ Φ }}.
Proof.
iIntros (Hpure) "Hwp".
iInduction Hpure as [|] "IH"; first done.
iApply ent_wp_pure_step; first done. by iApply "IH".
Qed.
Print hoare.
(** Exercise 3 *)
(** We can re-derive the Hoare rules from the weakest pre rules. *)
Lemma hoare_frame' P R Φ e :
{{ P }} e {{ Φ }}
{{ P R }} e {{ v, Φ v R }}.
Proof.
intro HP.
unfold hoare in *.
etrans; first apply ent_sep_split; [ exact HP | reflexivity | ].
etrans; last apply ent_wp_wand.
simpl.
rewrite ent_sep_comm.
apply ent_sep_split; [ apply ent_all_intro; intro v | reflexivity ].
apply ent_wand_intro.
rewrite ent_sep_comm.
reflexivity.
Qed.
(** Exercise 4 *)
Lemma hoare_load l v :
{{ l v }} !#l {{ w, w = v l v }}.
Proof.
unfold hoare.
etrans; last apply ent_wp_load; simpl.
etrans; [ apply ent_sep_true | rewrite ent_sep_comm; apply ent_sep_split ].
1: reflexivity.
apply ent_wand_intro; rewrite ent_sep_comm.
etrans; [ apply ent_sep_weaken | etrans; first apply ent_sep_true ].
apply ent_sep_split; [ | reflexivity ].
apply ent_prove_pure.
reflexivity.
Qed.
Lemma hoare_store l (v w : val) :
{{ l v }} #l <- w {{ _, l w }}.
Proof.
unfold hoare.
etrans; last apply ent_wp_store; simpl.
etrans; first apply ent_sep_true.
rewrite ent_sep_comm; apply ent_sep_split; first reflexivity.
apply ent_wand_intro.
rewrite ent_sep_comm; apply ent_sep_weaken.
Qed.
Lemma hoare_new (v : val) :
{{ True }} ref v {{ w, l : loc, w = #l l v }}.
Proof.
unfold hoare.
etrans; last apply ent_wp_new; simpl.
apply ent_all_intro; intro l.
apply ent_wand_intro.
etrans; first apply ent_sep_split; [ apply ent_prove_pure | reflexivity | ].
exact (eq_refl #l).
eapply ent_exist_intro.
reflexivity.
Qed.
(** Exercise 5 *)
(** Linked lists using the IPM *)
Fixpoint is_ll (xs : list val) (v : val) : iProp :=
match xs with
| [] => v = NONEV
| x :: xs =>
(l : loc) (w : val),
v = SOMEV #l l (x, w) is_ll xs w
end.
Definition new_ll : val :=
λ: <>, NONEV.
Definition cons_ll : val :=
λ: "h" "l", SOME (ref ("h", "l")).
Definition head_ll : val :=
λ: "x", match: "x" with NONE => #() | SOME "r" => Fst (!"r") end.
Definition tail_ll : val :=
λ: "x", match: "x" with NONE => #() | SOME "r" => Snd (!"r") end.
Definition len_ll : val :=
rec: "len" "x" := match: "x" with NONE => #0 | SOME "r" => #1 + "len" (Snd !"r") end.
Definition app_ll : val :=
rec: "app" "x" "y" :=
match: "x" with NONE => "y" | SOME "r" =>
let: "rs" := !"r" in
"r" <- (Fst "rs", "app" (Snd "rs") "y");;
SOME "r"
end.
Lemma app_ll_correct xs ys v w :
{{ is_ll xs v is_ll ys w }} app_ll v w {{ u, is_ll (xs ++ ys) u }}.
Proof.
iIntros "[Hv Hw]".
iRevert (v) "Hv Hw".
(* We use the [iInduction] tactic which lifts Coq's induction into Iris.
["IH"] is the name the inductive hypothesis should get in the Iris context.
Note that the inductive hypothesis is printed above another line [-----□].
This is another kind of context which you will learn about soon; for now, just
treat it the same as any other elements of the Iris context.
*)
iInduction xs as [ | x xs] "IH"; simpl; iIntros (v) "Hv Hw".
Restart.
(* simpler: use the [forall] clause of [iInduction] to let it quantify over [v] *)
iIntros "[Hv Hw]".
iInduction xs as [ | x xs] "IH" forall (v); simpl.
- iDestruct "Hv" as "->". unfold app_ll. wp_pures. iApply wp_value. done.
- iDestruct "Hv" as "(%l & %w' & -> & Hl & Hv)".
(* Note: [wp_pures] does not unfold the definition *)
wp_pures.
unfold app_ll. wp_pures. fold app_ll. wp_load. wp_pures.
wp_bind (app_ll _ _). iApply (ent_wp_wand' with "[Hl] [Hv Hw]"); first last.
{ iApply ("IH" with "Hv Hw"). }
simpl. iIntros (v) "Hv". wp_store. wp_pures. iApply wp_value. eauto with iFrame.
Qed.
Lemma new_ll_correct :
{{ True }} new_ll #() {{ v, is_ll [] v }}.
Proof.
unfold new_ll.
unfold is_ll.
iIntros "_".
wp_pures.
iApply (ent_wp_value).
iPureIntro.
reflexivity.
Qed.
Lemma cons_ll_correct (v x : val) xs :
{{ is_ll xs v }} cons_ll x v {{ u, is_ll (x :: xs) u }}.
Proof.
unfold hoare, cons_ll, is_ll.
fold is_ll.
etrans; last eapply ent_wp_pure_steps.
2: {
eapply rtc_l.
{
apply (pure_step_fill [AppLCtx _]).
apply pure_step_beta.
}
simpl.
etrans.
{
apply (rtc_pure_step_fill [AppLCtx _]).
apply pure_step_val; eauto.
}
simpl.
eapply rtc_l; last reflexivity.
apply pure_step_beta.
}
simpl.
etrans; last eapply (hoare_bind [InjRCtx]); [
apply ent_sep_true
| apply hoare_frame
| intro w
].
eapply hoare_pure_steps.
{
apply (rtc_pure_step_fill [AllocNRCtx _]).
apply pure_step_val.
eauto.
}
simpl.
1: apply hoare_new.
simpl.
unfold hoare.
etrans; first apply ent_exists_sep.
apply ent_exist_elim; intro l.
etrans; last eapply hoare_pure_steps; [ | apply pure_step_val; eauto | apply ent_wp_value ].
apply (ent_exist_intro l).
apply (ent_exist_intro v).
(* My patience is up. *)
iIntros "[[%w_eq HL] HV]".
rewrite w_eq.
iSplitR.
{ iPureIntro; reflexivity. }
iFrame.
Qed.
Lemma head_ll_correct (v x : val) xs :
{{ is_ll (x :: xs) v }} head_ll v {{ w, w = x }}.
Proof.
unfold head_ll, is_ll; fold is_ll.
iIntros "(%l & %w & %eq & HL & HW)".
subst.
wp_pures.
iApply (ent_wp_bind _ [FstCtx]).
iApply ent_wp_load.
iFrame.
simpl.
iIntros "_".
wp_pures.
iApply wp_value.
iPureIntro; reflexivity.
Qed.
Lemma tail_ll_correct v x xs :
{{ is_ll (x :: xs) v }} tail_ll v {{ w, is_ll xs w }}.
Proof.
unfold tail_ll, is_ll; fold is_ll.
iIntros "(%l & %w & %eq & HL & HW)".
subst.
wp_pures.
iApply (ent_wp_bind _ [SndCtx]).
iApply ent_wp_load.
iFrame.
simpl.
iIntros "_".
wp_pures.
iApply wp_value.
iAssumption.
Qed.
Lemma len_ll_correct v xs :
{{ is_ll xs v }} len_ll v {{ w, w = #(length xs) is_ll xs v }}.
Proof.
iInduction xs as [ | w xs ] "IH" forall (v); simpl.
- iIntros "%eq".
subst.
unfold len_ll.
wp_pures.
iApply wp_value.
iSplit; iPureIntro; reflexivity.
- iIntros "(%l & %x & %eq & HL & HX)".
subst.
unfold len_ll.
wp_pures.
iApply (ent_wp_bind _ [BinOpRCtx _ #1]).
simpl.
iApply (ent_wp_bind _ [SndCtx; AppRCtx _]).
iApply ent_wp_load.
iSplitL "HL"; [ iExact "HL" | iIntros "HL"; simpl ].
iApply (ent_wp_bind _ [AppRCtx _]).
wp_pures.
iApply (ent_wp_value).
simpl.
iSpecialize ("IH" $! x).
iSpecialize ("IH" with "HX").
iApply (ent_wp_wand' with "[HL]"); last iExact "IH".
iIntros "%v [%eq HX]".
subst.
wp_pures.
iApply (ent_wp_value).
iSplitR.
{
iPureIntro.
have h : (1 + length xs = S (length xs))%Z.
lia.
rewrite h.
reflexivity.
}
iExists l, x.
iSplitR.
{ iPureIntro; reflexivity. }
iFrame.
Qed.