From 452372fa3b88e42ac48259269e5fc0ce8334c7de Mon Sep 17 00:00:00 2001 From: Robbert Krebbers <mail@robbertkrebbers.nl> Date: Tue, 30 Apr 2019 23:51:33 +0200 Subject: [PATCH] Attempt at a more efficient `iDestruct` tactic that avoids context duplication. --- tests/proofmode.v | 12 +-- theories/base_logic/lib/boxes.v | 2 +- theories/proofmode/coq_tactics.v | 119 ++++++++++++++++++++++++++++++ theories/proofmode/environments.v | 90 +++++++++++++++++++++- theories/proofmode/ltac_tactics.v | 101 +++++++++++++++++++++++-- theories/proofmode/reduction.v | 1 + 6 files changed, 311 insertions(+), 14 deletions(-) diff --git a/tests/proofmode.v b/tests/proofmode.v index c5689c2eb..f0ce1226c 100644 --- a/tests/proofmode.v +++ b/tests/proofmode.v @@ -540,18 +540,18 @@ Proof. iIntros "HP" (Q) "_ #HPQ". by iApply "HPQ". Qed. Lemma test_and_sep (P Q R : PROP) : P ∧ (Q ∗ â–¡ R) ⊢ (P ∧ Q) ∗ â–¡ R. Proof. iIntros "H". repeat iSplit. - - iDestruct "H" as "[$ _]". - - iDestruct "H" as "[_ [$ _]]". - - iDestruct "H" as "[_ [_ #$]]". + - by iDestruct "H" as "[$ _]". + - by iDestruct "H" as "[_ [$ _]]". + - by iDestruct "H" as "[_ [_ #$]]". Qed. Lemma test_and_sep_2 (P Q R : PROP) `{!Persistent R, !Affine R} : P ∧ (Q ∗ R) ⊢ (P ∧ Q) ∗ R. Proof. iIntros "H". repeat iSplit. - - iDestruct "H" as "[$ _]". - - iDestruct "H" as "[_ [$ _]]". - - iDestruct "H" as "[_ [_ #$]]". + - by iDestruct "H" as "[$ _]". + - by iDestruct "H" as "[_ [$ _]]". + - by iDestruct "H" as "[_ [_ #$]]". Qed. Check "test_and_sep_affine_bi". diff --git a/theories/base_logic/lib/boxes.v b/theories/base_logic/lib/boxes.v index 4f67cb512..d2b91fab2 100644 --- a/theories/base_logic/lib/boxes.v +++ b/theories/base_logic/lib/boxes.v @@ -212,7 +212,7 @@ Proof. iCombine "Hf" "HP" as "Hf". rewrite -big_opM_opM big_opM_fmap; iApply (fupd_big_sepM _ _ f). iApply (@big_sepM_impl with "Hf"). - iIntros "!#" (γ b' ?) "[(Hγ' & #$ & #$) HΦ]". + iIntros "!#" (γ b' ?) "[(Hγ' & #? & #?) HΦ]". iFrame "#". iInv N as (b) "[>Hγ _]". iMod (box_own_auth_update γ with "[Hγ Hγ']") as "[Hγ $]"; first by iFrame. iModIntro. iSplitL; last done. iNext; iExists true. iFrame. diff --git a/theories/proofmode/coq_tactics.v b/theories/proofmode/coq_tactics.v index 6291971cb..2a9d282a7 100644 --- a/theories/proofmode/coq_tactics.v +++ b/theories/proofmode/coq_tactics.v @@ -623,6 +623,125 @@ Proof. - setoid_rewrite <-(right_id emp%I _ (Pout _)). auto. Qed. +(** * Covfefe *) +Definition covfefe (p : bool) (P : PROP) (Q : PROP) + (acts : list (envs_action PROP)) (Q' : PROP) := + â–¡?p P ∗ (([∗ list] act ∈ acts, envs_action_to_prop act) -∗ Q') ⊢ Q. + +Lemma covfefe_continue {A} (x : A) p P Q : covfefe p P Q [AContinue x p P] Q. +Proof. by rewrite /covfefe /= right_id wand_elim_r. Qed. +Lemma covfefe_name i p P Q : covfefe p P Q [ARename i p P] Q. +Proof. by rewrite /covfefe /= right_id wand_elim_r. Qed. +Lemma covfefe_fresh p P Q : covfefe p P Q [AFresh p P] Q. +Proof. by rewrite /covfefe /= right_id wand_elim_r. Qed. +Lemma covfefe_clear (p : bool) P Q : + (if p then TCTrue else TCOr (Affine P) (Absorbing Q)) → + covfefe p P Q [] Q. +Proof. + intros. rewrite /covfefe /= left_id. + destruct p; simpl; by rewrite sep_elim_r. +Qed. +Lemma covfefe_pure (p : bool) P Q φ : + IntoPure P φ → + (if p then TCTrue else TCOr (Affine P) (Absorbing Q)) → + covfefe p P Q [APure φ] Q. +Proof. + intros ? HPQ. rewrite /covfefe /= right_id. destruct p; simpl. + - by rewrite (into_pure P) intuitionistically_affinely wand_elim_r. + - destruct HPQ. + + by rewrite -(affine_affinely P) (into_pure P) wand_elim_r. + + rewrite (into_pure P) -{1}(persistent_absorbingly_affinely ⌜ _ âŒ)%I. + by rewrite absorbingly_sep_l wand_elim_r. +Qed. +Lemma covfefe_frame p P Q Q' : + Frame p P Q Q' → + covfefe p P Q [] Q'. +Proof. intros. by rewrite /covfefe /= left_id. Qed. + +Lemma covfefe_and_l (p : bool) P Q P1 P2 res Q' : + (if p then IntoAnd p P P1 P2 : Type else + TCOr (IntoAnd p P P1 P2) (TCAnd (IntoSep P P1 P2) + (TCOr (Affine P2) (Absorbing Q)))) → + covfefe p P1 Q res Q' → + covfefe p P Q res Q'. +Proof. + rewrite /covfefe /IntoAnd /IntoSep=> HP HP1. destruct p; simpl in *. + { by rewrite HP and_elim_l. } + destruct HP as [HP|[HP [?|?]]]. + - by rewrite HP and_elim_l. + - by rewrite HP sep_elim_l. + - by rewrite HP (comm _ P1) -assoc HP1 sep_elim_r. +Qed. +Lemma covfefe_and_r (p : bool) P Q P1 P2 res Q' : + (if p then IntoAnd p P P1 P2 : Type else + TCOr (IntoAnd p P P1 P2) (TCAnd (IntoSep P P1 P2) + (TCOr (Affine P1) (Absorbing Q)))) → + covfefe p P2 Q res Q' → + covfefe p P Q res Q'. +Proof. + rewrite /covfefe /IntoAnd /IntoSep=> HP HP1. destruct p; simpl in *. + { by rewrite HP and_elim_r. } + destruct HP as [HP|[HP [?|?]]]. + - by rewrite HP and_elim_r. + - by rewrite HP sep_elim_r. + - by rewrite HP -assoc HP1 sep_elim_r. +Qed. + +Lemma covfefe_and (p : bool) P Q P1 P2 res1 res2 Q' Q'' : + (if p then IntoAnd true P P1 P2 else IntoSep P P1 P2) → + covfefe p P1 Q res1 Q' → + covfefe p P2 Q' res2 Q'' → + covfefe p P Q (pm_app res1 res2) Q''. +Proof. + rewrite (_ : pm_app = app) // /covfefe /IntoAnd /IntoSep /=. destruct p; simpl. + - intros -> HP1 HP2. rewrite intuitionistically_and persistent_and_sep_1. + rewrite -HP1 -assoc. apply sep_mono; first done. + apply wand_intro_l. rewrite assoc -(comm _ (â–¡ _))%I -assoc. + by rewrite big_sepL_app -wand_curry wand_elim_r. + - intros -> HP1 HP2. rewrite -HP1 -assoc. apply sep_mono; first done. + apply wand_intro_l. rewrite assoc -(comm _ P2)%I -assoc. + by rewrite big_sepL_app -wand_curry wand_elim_r. +Qed. + +Lemma covfefe_intuitionistic p P Q P' res Q' : + IntoPersistent p P P' → + (if p then TCTrue else TCOr (Affine P) (Absorbing Q)) → + covfefe true P' Q res Q' → + covfefe p P Q res Q'. +Proof. + rewrite /covfefe /IntoPersistent /=. destruct p; simpl. + - rewrite /bi_intuitionistically. by intros -> _. + - intros HP HPQ HP'. destruct HPQ. + + by rewrite -(affine_affinely P) HP. + + rewrite HP -absorbingly_intuitionistically_into_persistently. + by rewrite absorbingly_sep_l HP'. +Qed. + +Lemma covfefe_modal p P Q φ p' P' res Q' Q'' : + ElimModal φ p p' P P' Q Q' → + φ → + covfefe p' P' Q' res Q'' → + covfefe p P Q res Q''. +Proof. + rewrite /ElimModal /covfefe /= => HP Hφ HP'. + rewrite -HP //. apply sep_mono; first done. by apply wand_intro_l. +Qed. + +Lemma tac_covfefe Δ i p P Q Q' acts : + envs_lookup i Δ = Some (p, P) → + covfefe p P Q acts Q' → + match envs_replace_actions i p acts Δ with + | Some (Δ', φs, cs) => pm_foldr (λ P1 P2 : Prop, P1 → P2) (envs_entails Δ' Q') φs + | None => False + end → + envs_entails Δ Q. +Proof. + rewrite envs_entails_eq /covfefe => ? <- HQ'. + destruct (envs_replace_actions _ _ _ _) as [[[Δ' φs] cs]|] eqn:Hact; last done. + rewrite envs_replace_actions_sound //. apply sep_mono, wand_mono=> //. + rewrite -persistent_and_affinely_sep_l. apply pure_elim_l=> Hφs. + move: HQ'. clear -Hφs. induction Hφs; simpl; auto. +Qed. End bi_tactics. (** The following _private_ classes are used internally by [tac_modal_intro] / diff --git a/theories/proofmode/environments.v b/theories/proofmode/environments.v index fd6705348..7dd60dd6e 100644 --- a/theories/proofmode/environments.v +++ b/theories/proofmode/environments.v @@ -344,9 +344,60 @@ Definition env_to_prop {PROP : bi} (Γ : env PROP) : PROP := in match Γ with Enil => emp%I | Esnoc Γ _ P => aux Γ P end. +Inductive envs_action (PROP : bi) := + | AFresh (p : bool) (P : PROP) + | ARename (j : ident) (p : bool) (P : PROP) + | AContinue {A} (x : A) (p : bool) (P : PROP) + | APure (φ : Prop). +Arguments AFresh {_} _ _. +Arguments ARename {_} _ _ _. +Arguments AContinue {_ _} _ _ _. +Arguments APure {_} _. + +Definition envs_action_to_prop {PROP} (act : envs_action PROP) : PROP := + match act with + | AFresh p P | ARename _ p P | AContinue _ p P => â–¡?p P + | APure φ => <affine> ⌜ φ ⌠+ end%I. + +Fixpoint envs_replace_actions_filter {PROP} + (c : positive) (acts : list (envs_action PROP)) + (Γp Γs : env PROP) (φs : list Prop) (cont : list (ident * sigT id)) : + positive * env PROP * env PROP * list Prop * list (ident * sigT id) := + match acts with + | [] => (c,Γp,Γs,pm_reverse φs,pm_reverse cont) + | act :: acts => + match act with + | AFresh q P => + if q + then envs_replace_actions_filter (Pos_succ c) acts (Esnoc Γp (IAnon c) P) Γs φs cont + else envs_replace_actions_filter (Pos_succ c) acts Γp (Esnoc Γs (IAnon c) P) φs cont + | ARename j q P => + if q + then envs_replace_actions_filter c acts (Esnoc Γp j P) Γs φs cont + else envs_replace_actions_filter c acts Γp (Esnoc Γs j P) φs cont + | AContinue x q P => + if q + then envs_replace_actions_filter (Pos_succ c) + acts (Esnoc Γp (IAnon c) P) Γs φs ((IAnon c,existT _ x) :: cont) + else envs_replace_actions_filter (Pos_succ c) + acts Γp (Esnoc Γs (IAnon c) P) φs ((IAnon c,existT _ x) :: cont) + | APure φ => envs_replace_actions_filter c acts Γp Γs (φ :: φs) cont + end + end. + +Definition envs_replace_actions {PROP} (i : ident) (p : bool) + (acts : list (envs_action PROP)) (Δ : envs PROP) : + option (envs PROP * list Prop * list (ident * sigT id)) := + let '(c,Γp,Γs,φs,cont) := + envs_replace_actions_filter (env_counter Δ) acts Enil Enil [] [] in + Δ ↠envs_simple_replace i p (if p then Γp else Γs) Δ; + Δ ↠envs_app (beq p false) (if p then Γs else Γp) Δ; + Some (envs_set_counter c Δ, φs, cont). + Section envs. Context {PROP : bi}. -Implicit Types Γ : env PROP. +Implicit Types Γ Γs Γp : env PROP. Implicit Types Δ : envs PROP. Implicit Types P Q : PROP. @@ -716,4 +767,41 @@ Proof. - by rewrite right_id. - rewrite /= IH (comm _ Q _) assoc. done. Qed. + +Lemma envs_replace_actions_filter_sound c acts Γp Γs φs cont c' Γp' Γs' φs' cont' : + envs_replace_actions_filter c acts Γp Γs φs cont = (c', Γp', Γs', φs', cont') → + ([∗ list] act ∈ acts, envs_action_to_prop act) + ∗ <affine> ⌜ Forall id φs ⌠∗ â–¡ [∧] Γp ∗ [∗] Γs + ⊢ <affine> ⌜ Forall id φs' ⌠∗ â–¡ [∧] Γp' ∗ [∗] Γs'. +Proof. + revert c Γp Γs φs cont c' Γp' Γs' φs' cont'. + induction acts as [|act acts IH]; + intros c Γp Γs φs cont c' Γp' Γs' φs' cont' Hact; simplify_eq/=. + { by rewrite left_id (_ : pm_reverse = reverse) // Forall_reverse. } + destruct act as [[] P|j [] P|A x [] P|φ] eqn:?; (etrans; [|by eapply IH]); + rewrite /= ?intuitionistically_and ?and_sep_intuitionistically; try solve_sep_entails. + rewrite Forall_cons /= pure_and affinely_and -sep_and. solve_sep_entails. +Qed. + +Lemma envs_replace_actions_sound Δ Δ' i p acts P φs cs : + envs_lookup i Δ = Some (p, P) → + envs_replace_actions i p acts Δ = Some (Δ', φs, cs) → + of_envs Δ ⊢ â–¡?p P ∗ + (([∗ list] act ∈ acts, envs_action_to_prop act) -∗ + <affine> ⌜ Forall id φs ⌠∗ of_envs Δ'). +Proof. + rewrite /envs_replace_actions=> ? Hact. + destruct (envs_replace_actions_filter _ _ _ _ _ _) + as [[[[c Γp] Γs] φs'] cont] eqn:Hfilter. + destruct (envs_simple_replace _ _ _ _) as [Δ1|] eqn:?; simplify_eq/=. + destruct (envs_app _ _ _) as [Δ2|] eqn:?; simplify_eq/=. + move: Hfilter=> /envs_replace_actions_filter_sound /=. rewrite Forall_nil. + rewrite intuitionistically_True_emp affinely_True_emp affinely_emp !right_id. + intros ->. rewrite envs_simple_replace_sound // envs_app_sound //. + apply sep_mono; first done. apply wand_intro_l. + rewrite -!assoc. apply sep_mono; first done. + destruct p; simpl. + - by rewrite wand_curry assoc wand_elim_r envs_set_counter_sound. + - by rewrite !wand_elim_r envs_set_counter_sound. +Qed. End envs. diff --git a/theories/proofmode/ltac_tactics.v b/theories/proofmode/ltac_tactics.v index 30c42ee1f..e5688c21e 100644 --- a/theories/proofmode/ltac_tactics.v +++ b/theories/proofmode/ltac_tactics.v @@ -1217,6 +1217,96 @@ Tactic Notation "iModCore" constr(H) := |pm_prettify(* subgoal *)]. (** * Basic destruct tactic *) +Ltac iSolveCovfefe pat := + lazymatch pat with + | IFresh => + apply covfefe_fresh + | IDrop => + apply covfefe_clear; + [pm_reduce; iSolveTC || + let P := match goal with |- TCOr (Affine ?P) _ => P end in + fail "iDestruct: cannot clear " P "; not affine and the goal not absorbing"] + | IFrame => + apply covfefe_frame; + [iSolveTC || + let R := match goal with |- Frame _ ?R _ _ => R end in + fail "iDestruct: cannot frame" R] + | IIdent ?i => + apply (covfefe_name i) + | IPureElim => + eapply covfefe_pure; + [iSolveTC || + let P := match goal with |- IntoPure ?P _ => P end in + fail "iDestruct:" P "not pure" + |pm_reduce; iSolveTC || + let P := match goal with |- TCOr (Affine ?P) _ => P end in + fail "iDestruct:" P "not affine and the goal not absorbing"] + | IList [[?pat; IDrop]] => + eapply covfefe_and_l; + [pm_reduce; iSolveTC || + let P := match goal with |- context [ IntoAnd _ ?P _ _ ] => P end in + fail "iDestruct: cannot destruct" P + |iSolveCovfefe pat] + | IList [[IDrop; ?pat]] => + eapply covfefe_and_r; + [pm_reduce; iSolveTC || + let P := match goal with |- context [ IntoAnd _ ?P _ _ ] => P end in + fail "iDestruct: cannot destruct" P + |iSolveCovfefe pat] + | IList [[?pat1; ?pat2]] => + eapply covfefe_and; + [pm_reduce; iSolveTC || + let P := + lazymatch goal with + | |- IntoSep ?P _ _ => P + | |- IntoAnd _ ?P _ _ => P + end in + fail "iDestruct: cannot destruct" P + |iSolveCovfefe pat1 + |iSolveCovfefe pat2] + | IAlwaysElim ?pat => + eapply covfefe_intuitionistic; + [iSolveTC || + let P := match goal with |- IntoPersistent _ ?P _ => P end in + fail "iDestruct:" P "not persistent" + |pm_reduce; iSolveTC || + let P := match goal with |- TCOr (Affine ?P) _ => P end in + fail "iDestruct:" P "not affine and the goal not absorbing" + |iSolveCovfefe pat] + | IModalElim ?pat => + eapply covfefe_modal; + [iSolveTC || + let P := match goal with |- ElimModal _ _ _ ?P _ _ _ => P end in + let Q := match goal with |- ElimModal _ _ _ _ _ ?Q _ => Q end in + fail "iMod: cannot eliminate modality" P "in" Q + |solve [iSolveSideCondition] || + fail "iDestruct: `>`-pattern only supported with trivial side-conditions" + |iSolveCovfefe pat] + | _ => apply (covfefe_continue pat) + end. + +Tactic Notation "iCovfefe" constr(H) "as" constr(pat) "with" tactic(tac) := + let rec go_cont cs := + lazymatch cs with + | [] => idtac + | (?i, existT intro_pat ?pat) :: ?cs => tac i pat; go_cont cs + end in + let pat := intro_pat.parse_one pat in + notypeclasses refine (tac_covfefe _ H _ _ _ _ _ _ _ _); + [pm_reflexivity || fail "iDestruct:" H "not found" + |iSolveCovfefe pat + |lazymatch goal with + | |- context K [ envs_replace_actions ?i ?p ?acts ?Δ ] => + lazymatch eval pm_eval in (envs_replace_actions i p acts Δ) with + | None => fail "iDestruct: name clash" + | Some (?Δ, ?φs, ?cs) => + let res := context K [Some (Δ, φs, cs)] in + convert_concl_no_check res; pm_reduce; + repeat intro; + go_cont cs + end + end]. + Local Ltac iDestructHypGo Hz pat := lazymatch pat with | IFresh => @@ -1228,16 +1318,15 @@ Local Ltac iDestructHypGo Hz pat := | IFrame => iFrameHyp Hz | IIdent ?y => iRename Hz into y | IList [[]] => iExFalso; iExact Hz - | IList [[?pat1; IDrop]] => iAndDestructChoice Hz as Left Hz; iDestructHypGo Hz pat1 - | IList [[IDrop; ?pat2]] => iAndDestructChoice Hz as Right Hz; iDestructHypGo Hz pat2 - | IList [[?pat1; ?pat2]] => - let Hy := iFresh in iAndDestruct Hz as Hz Hy; iDestructHypGo Hz pat1; iDestructHypGo Hy pat2 | IList [[?pat1];[?pat2]] => iOrDestruct Hz as Hz Hz; [iDestructHypGo Hz pat1|iDestructHypGo Hz pat2] | IPureElim => iPure Hz as ? | IRewrite Right => iPure Hz as -> | IRewrite Left => iPure Hz as <- - | IAlwaysElim ?pat => iIntuitionistic Hz; iDestructHypGo Hz pat - | IModalElim ?pat => iModCore Hz; iDestructHypGo Hz pat + | IList [[_; IDrop]] => iCovfefe Hz as pat with iDestructHypGo + | IList [[IDrop; _]] => iCovfefe Hz as pat with iDestructHypGo + | IList [[_; _]] => iCovfefe Hz as pat with iDestructHypGo + | IAlwaysElim _ => iCovfefe Hz as pat with iDestructHypGo + | IModalElim _ => iCovfefe Hz as pat with iDestructHypGo | _ => fail "iDestruct:" pat "invalid" end. Local Ltac iDestructHypFindPat Hgo pat found pats := diff --git a/theories/proofmode/reduction.v b/theories/proofmode/reduction.v index 2fece8671..799df4102 100644 --- a/theories/proofmode/reduction.v +++ b/theories/proofmode/reduction.v @@ -14,6 +14,7 @@ Declare Reduction pm_eval := cbv [ envs_simple_replace envs_replace envs_split envs_clear_spatial envs_clear_intuitionistic envs_set_counter envs_incr_counter envs_split_go envs_split env_to_prop + envs_replace_actions_filter envs_replace_actions (* PM list and option combinators *) pm_app pm_rev_append pm_reverse pm_foldr pm_option_bind pm_from_option pm_option_fun -- GitLab