diff --git a/_CoqProject b/_CoqProject index ef9ce69e3574f0f9b6b5e265c949f4fae273eae3..aa2bb70a727766892573925f427a02973e09aead 100644 --- a/_CoqProject +++ b/_CoqProject @@ -95,6 +95,7 @@ theories/heap_lang/lib/increment.v theories/proofmode/base.v theories/proofmode/tokens.v theories/proofmode/coq_tactics.v +theories/proofmode/ltac_tactics.v theories/proofmode/environments.v theories/proofmode/intro_patterns.v theories/proofmode/spec_patterns.v diff --git a/theories/proofmode/ltac_tactics.v b/theories/proofmode/ltac_tactics.v new file mode 100644 index 0000000000000000000000000000000000000000..49999e5b61006220a477eabb52f4430a30eb3ee8 --- /dev/null +++ b/theories/proofmode/ltac_tactics.v @@ -0,0 +1,2001 @@ +From iris.proofmode Require Import coq_tactics. +From iris.proofmode Require Import base intro_patterns spec_patterns sel_patterns. +From iris.bi Require Export bi. +From stdpp Require Import namespaces. +From iris.proofmode Require Export classes notation. +From stdpp Require Import hlist pretty. +Set Default Proof Using "Type". +Export ident. + +Declare Reduction env_cbv := cbv [ + option_bind + beq ascii_beq string_beq positive_beq Pos.succ ident_beq + env_lookup env_lookup_delete env_delete env_app env_replace env_dom + env_intuitionistic env_spatial env_counter env_spatial_is_nil envs_dom + envs_lookup envs_lookup_delete envs_delete envs_snoc envs_app + envs_simple_replace envs_replace envs_split + envs_clear_spatial envs_clear_persistent envs_incr_counter + envs_split_go envs_split prop_of_env]. +Ltac env_cbv := + match goal with |- ?u => let v := eval env_cbv in u in change v end. +Ltac env_reflexivity := env_cbv; exact eq_refl. + +(** For most of the tactics, we want to have tight control over the order and +way in which type class inference is performed. To that end, many tactics make +use of [notypeclasses refine] and the [iSolveTC] tactic to manually invoke type +class inference. + +The tactic [iSolveTC] does not use [apply _], as that often leads to issues +because it will try to solve all evars whose type is a typeclass, in +dependency order (according to Matthieu). If one fails, it aborts. However, we +generally rely on progress on the main goal to be solved to make progress +elsewhere. With [typeclasses eauto], that seems to work better. + +A drawback of [typeclasses eauto] is that it is multi-success, i.e. whenever +subsequent tactics fail, it will backtrack to [typeclasses eauto] to try the +next type class instance. This is almost always undesired and leads to poor +performance and horrible error messages, so we wrap it in a [once]. *) +Ltac iSolveTC := + solve [once (typeclasses eauto)]. + +(** * Misc *) + +Ltac iMissingHyps Hs := + let Δ := + lazymatch goal with + | |- envs_entails ?Δ _ => Δ + | |- context[ envs_split _ _ ?Δ ] => Δ + end in + let Hhyps := eval env_cbv in (envs_dom Δ) in + eval vm_compute in (list_difference Hs Hhyps). + +Ltac iTypeOf H := + let Δ := match goal with |- envs_entails ?Δ _ => Δ end in + eval env_cbv in (envs_lookup H Δ). + +Tactic Notation "iMatchHyp" tactic1(tac) := + match goal with + | |- context[ environments.Esnoc _ ?x ?P ] => tac x P + end. + +(** * Start a proof *) +Tactic Notation "iStartProof" := + lazymatch goal with + | |- envs_entails _ _ => idtac + | |- ?φ => notypeclasses refine (as_emp_valid_2 φ _ _); + [apply _ || fail "iStartProof: not a Bi entailment" + |apply tac_adequate] + end. + +(* Same as above, with 2 differences : + - We can specify a BI in which we want the proof to be done + - If the goal starts with a let or a ∀, they are automatically + introduced. *) +Tactic Notation "iStartProof" uconstr(PROP) := + lazymatch goal with + | |- @envs_entails ?PROP' _ _ => + (* This cannot be shared with the other [iStartProof], because + type_term has a non-negligeable performance impact. *) + let x := type_term (eq_refl : @eq Type PROP PROP') in idtac + + (* We eta-expand [as_emp_valid_2], in order to make sure that + [iStartProof PROP] works even if [PROP] is the carrier type. In + this case, typing this expression will end up unifying PROP with + [bi_car _], and hence trigger the canonical structures mechanism + to find the corresponding bi. *) + | |- ?φ => notypeclasses refine ((λ P : PROP, @as_emp_valid_2 φ _ P) _ _ _); + [apply _ || fail "iStartProof: not a Bi entailment" + |apply tac_adequate] + end. + +(** * Generate a fresh identifier *) +(* Tactic Notation tactics cannot return terms *) +Ltac iFresh := + (* We need to increment the environment counter using [tac_fresh]. + But because [iFresh] returns a value, we have to let bind + [tac_fresh] wrapped under a match to force evaluation of this + side-effect. See https://stackoverflow.com/a/46178884 *) + let do_incr := + lazymatch goal with + | _ => iStartProof; eapply tac_fresh; first by (env_reflexivity) + end in + lazymatch goal with + |- envs_entails ?Δ _ => + let n := eval env_cbv in (env_counter Δ) in + constr:(IAnon n) + end. + +(** * Simplification *) +Tactic Notation "iEval" tactic(t) := + iStartProof; + eapply tac_eval; + [let x := fresh in intros x; t; unfold x; reflexivity + |]. + +Tactic Notation "iEval" tactic(t) "in" constr(H) := + iStartProof; + eapply tac_eval_in with _ H _ _ _; + [env_reflexivity || fail "iEval:" H "not found" + |let x := fresh in intros x; t; unfold x; reflexivity + |env_reflexivity + |]. + +Tactic Notation "iSimpl" := iEval simpl. +Tactic Notation "iSimpl" "in" constr(H) := iEval simpl in H. + +(* It would be nice to also have an `iSsrRewrite`, however, for this we need to +pass arguments to Ssreflect's `rewrite` like `/= foo /bar` in Ltac, see: + + https://sympa.inria.fr/sympa/arc/coq-club/2018-01/msg00000.html + +PMP told me (= Robbert) in person that this is not possible today, but may be +possible in Ltac2. *) + +(** * Context manipulation *) +Tactic Notation "iRename" constr(H1) "into" constr(H2) := + eapply tac_rename with _ H1 H2 _ _; (* (i:=H1) (j:=H2) *) + [env_reflexivity || fail "iRename:" H1 "not found" + |env_reflexivity || fail "iRename:" H2 "not fresh"|]. + +Local Inductive esel_pat := + | ESelPure + | ESelIdent : bool → ident → esel_pat. + +Ltac iElaborateSelPat pat := + let rec go pat Δ Hs := + lazymatch pat with + | [] => eval cbv in Hs + | SelPure :: ?pat => go pat Δ (ESelPure :: Hs) + | SelPersistent :: ?pat => + let Hs' := eval env_cbv in (env_dom (env_intuitionistic Δ)) in + let Δ' := eval env_cbv in (envs_clear_persistent Δ) in + go pat Δ' ((ESelIdent true <$> Hs') ++ Hs) + | SelSpatial :: ?pat => + let Hs' := eval env_cbv in (env_dom (env_spatial Δ)) in + let Δ' := eval env_cbv in (envs_clear_spatial Δ) in + go pat Δ' ((ESelIdent false <$> Hs') ++ Hs) + | SelIdent ?H :: ?pat => + lazymatch eval env_cbv in (envs_lookup_delete false H Δ) with + | Some (?p,_,?Δ') => go pat Δ' (ESelIdent p H :: Hs) + | None => fail "iElaborateSelPat:" H "not found" + end + end in + lazymatch goal with + | |- envs_entails ?Δ _ => + let pat := sel_pat.parse pat in go pat Δ (@nil esel_pat) + end. + +Local Ltac iClearHyp H := + eapply tac_clear with _ H _ _; (* (i:=H) *) + [env_reflexivity || fail "iClear:" H "not found" + |env_cbv; apply _ || + let P := match goal with |- TCOr (Affine ?P) _ => P end in + fail "iClear:" H ":" P "not affine and the goal not absorbing" + |]. + +Tactic Notation "iClear" constr(Hs) := + let rec go Hs := + lazymatch Hs with + | [] => idtac + | ESelPure :: ?Hs => clear; go Hs + | ESelIdent _ ?H :: ?Hs => iClearHyp H; go Hs + end in + let Hs := iElaborateSelPat Hs in iStartProof; go Hs. + +Tactic Notation "iClear" "(" ident_list(xs) ")" constr(Hs) := + iClear Hs; clear xs. + +(** * Assumptions *) +Tactic Notation "iExact" constr(H) := + eapply tac_assumption with _ H _ _; (* (i:=H) *) + [env_reflexivity || fail "iExact:" H "not found" + |apply _ || + let P := match goal with |- FromAssumption _ ?P _ => P end in + fail "iExact:" H ":" P "does not match goal" + |env_cbv; apply _ || + fail "iExact:" H "not absorbing and the remaining hypotheses not affine"]. + +Tactic Notation "iAssumptionCore" := + let rec find Γ i P := + lazymatch Γ with + | Esnoc ?Γ ?j ?Q => first [unify P Q; unify i j|find Γ i P] + end in + match goal with + | |- envs_lookup ?i (Envs ?Γp ?Γs _) = Some (_, ?P) => + first [is_evar i; fail 1 | env_reflexivity] + | |- envs_lookup ?i (Envs ?Γp ?Γs _) = Some (_, ?P) => + is_evar i; first [find Γp i P | find Γs i P]; env_reflexivity + | |- envs_lookup_delete _ ?i (Envs ?Γp ?Γs _) = Some (_, ?P, _) => + first [is_evar i; fail 1 | env_reflexivity] + | |- envs_lookup_delete _ ?i (Envs ?Γp ?Γs _) = Some (_, ?P, _) => + is_evar i; first [find Γp i P | find Γs i P]; env_reflexivity + end. + +Tactic Notation "iAssumption" := + let Hass := fresh in + let rec find p Γ Q := + lazymatch Γ with + | Esnoc ?Γ ?j ?P => first + [pose proof (_ : FromAssumption p P Q) as Hass; + eapply (tac_assumption _ _ j p P); + [env_reflexivity + |apply Hass + |env_cbv; apply _ || + fail 1 "iAssumption:" j "not absorbing and the remaining hypotheses not affine"] + |assert (P = False%I) as Hass by reflexivity; + apply (tac_false_destruct _ j p P); + [env_reflexivity + |exact Hass] + |find p Γ Q] + end in + lazymatch goal with + | |- envs_entails (Envs ?Γp ?Γs _) ?Q => + first [find true Γp Q | find false Γs Q + |fail "iAssumption:" Q "not found"] + end. + +(** * False *) +Tactic Notation "iExFalso" := apply tac_ex_falso. + +(** * Making hypotheses persistent or pure *) +Local Tactic Notation "iPersistent" constr(H) := + eapply tac_persistent with _ H _ _ _; (* (i:=H) *) + [env_reflexivity || fail "iPersistent:" H "not found" + |apply _ || + let P := match goal with |- IntoPersistent _ ?P _ => P end in + fail "iPersistent:" P "not persistent" + |env_cbv; apply _ || + let P := match goal with |- TCOr (Affine ?P) _ => P end in + fail "iPersistent:" P "not affine and the goal not absorbing" + |env_reflexivity|]. + +Local Tactic Notation "iPure" constr(H) "as" simple_intropattern(pat) := + eapply tac_pure with _ H _ _ _; (* (i:=H1) *) + [env_reflexivity || fail "iPure:" H "not found" + |apply _ || + let P := match goal with |- IntoPure ?P _ => P end in + fail "iPure:" P "not pure" + |env_cbv; apply _ || + let P := match goal with |- TCOr (Affine ?P) _ => P end in + fail "iPure:" P "not affine and the goal not absorbing" + |intros pat]. + +Tactic Notation "iEmpIntro" := + iStartProof; + eapply tac_emp_intro; + [env_cbv; apply _ || + fail "iEmpIntro: spatial context contains non-affine hypotheses"]. + +Tactic Notation "iPureIntro" := + iStartProof; + eapply tac_pure_intro; + [env_reflexivity + |apply _ || + let P := match goal with |- FromPure _ ?P _ => P end in + fail "iPureIntro:" P "not pure" + |]. + +(** Framing *) +Local Ltac iFrameFinish := + lazy iota beta; + try match goal with + | |- envs_entails _ True => by iPureIntro + | |- envs_entails _ emp => iEmpIntro + end. + +Local Ltac iFramePure t := + iStartProof; + let φ := type of t in + eapply (tac_frame_pure _ _ _ _ t); + [apply _ || fail "iFrame: cannot frame" φ + |iFrameFinish]. + +Local Ltac iFrameHyp H := + iStartProof; + eapply tac_frame with _ H _ _ _; + [env_reflexivity || fail "iFrame:" H "not found" + |apply _ || + let R := match goal with |- Frame _ ?R _ _ => R end in + fail "iFrame: cannot frame" R + |iFrameFinish]. + +Local Ltac iFrameAnyPure := + repeat match goal with H : _ |- _ => iFramePure H end. + +Local Ltac iFrameAnyPersistent := + iStartProof; + let rec go Hs := + match Hs with [] => idtac | ?H :: ?Hs => repeat iFrameHyp H; go Hs end in + match goal with + | |- envs_entails ?Δ _ => + let Hs := eval cbv in (env_dom (env_intuitionistic Δ)) in go Hs + end. + +Local Ltac iFrameAnySpatial := + iStartProof; + let rec go Hs := + match Hs with [] => idtac | ?H :: ?Hs => try iFrameHyp H; go Hs end in + match goal with + | |- envs_entails ?Δ _ => + let Hs := eval cbv in (env_dom (env_spatial Δ)) in go Hs + end. + +Tactic Notation "iFrame" := iFrameAnySpatial. + +Tactic Notation "iFrame" "(" constr(t1) ")" := + iFramePure t1. +Tactic Notation "iFrame" "(" constr(t1) constr(t2) ")" := + iFramePure t1; iFrame ( t2 ). +Tactic Notation "iFrame" "(" constr(t1) constr(t2) constr(t3) ")" := + iFramePure t1; iFrame ( t2 t3 ). +Tactic Notation "iFrame" "(" constr(t1) constr(t2) constr(t3) constr(t4) ")" := + iFramePure t1; iFrame ( t2 t3 t4 ). +Tactic Notation "iFrame" "(" constr(t1) constr(t2) constr(t3) constr(t4) + constr(t5) ")" := + iFramePure t1; iFrame ( t2 t3 t4 t5 ). +Tactic Notation "iFrame" "(" constr(t1) constr(t2) constr(t3) constr(t4) + constr(t5) constr(t6) ")" := + iFramePure t1; iFrame ( t2 t3 t4 t5 t6 ). +Tactic Notation "iFrame" "(" constr(t1) constr(t2) constr(t3) constr(t4) + constr(t5) constr(t6) constr(t7) ")" := + iFramePure t1; iFrame ( t2 t3 t4 t5 t6 t7 ). +Tactic Notation "iFrame" "(" constr(t1) constr(t2) constr(t3) constr(t4) + constr(t5) constr(t6) constr(t7) constr(t8)")" := + iFramePure t1; iFrame ( t2 t3 t4 t5 t6 t7 t8 ). + +Tactic Notation "iFrame" constr(Hs) := + let rec go Hs := + lazymatch Hs with + | [] => idtac + | SelPure :: ?Hs => iFrameAnyPure; go Hs + | SelPersistent :: ?Hs => iFrameAnyPersistent; go Hs + | SelSpatial :: ?Hs => iFrameAnySpatial; go Hs + | SelIdent ?H :: ?Hs => iFrameHyp H; go Hs + end + in let Hs := sel_pat.parse Hs in go Hs. +Tactic Notation "iFrame" "(" constr(t1) ")" constr(Hs) := + iFramePure t1; iFrame Hs. +Tactic Notation "iFrame" "(" constr(t1) constr(t2) ")" constr(Hs) := + iFramePure t1; iFrame ( t2 ) Hs. +Tactic Notation "iFrame" "(" constr(t1) constr(t2) constr(t3) ")" constr(Hs) := + iFramePure t1; iFrame ( t2 t3 ) Hs. +Tactic Notation "iFrame" "(" constr(t1) constr(t2) constr(t3) constr(t4) ")" + constr(Hs) := + iFramePure t1; iFrame ( t2 t3 t4 ) Hs. +Tactic Notation "iFrame" "(" constr(t1) constr(t2) constr(t3) constr(t4) + constr(t5) ")" constr(Hs) := + iFramePure t1; iFrame ( t2 t3 t4 t5 ) Hs. +Tactic Notation "iFrame" "(" constr(t1) constr(t2) constr(t3) constr(t4) + constr(t5) constr(t6) ")" constr(Hs) := + iFramePure t1; iFrame ( t2 t3 t4 t5 t6 ) Hs. +Tactic Notation "iFrame" "(" constr(t1) constr(t2) constr(t3) constr(t4) + constr(t5) constr(t6) constr(t7) ")" constr(Hs) := + iFramePure t1; iFrame ( t2 t3 t4 t5 t6 t7 ) Hs. +Tactic Notation "iFrame" "(" constr(t1) constr(t2) constr(t3) constr(t4) + constr(t5) constr(t6) constr(t7) constr(t8)")" constr(Hs) := + iFramePure t1; iFrame ( t2 t3 t4 t5 t6 t7 t8 ) Hs. + +(** * Basic introduction tactics *) +Local Tactic Notation "iIntro" "(" simple_intropattern(x) ")" := + (* In the case the goal starts with an [let x := _ in _], we do not + want to unfold x and start the proof mode. Instead, we want to + use intros. So [iStartProof] has to be called only if [intros] + fails *) + intros x || + (iStartProof; + lazymatch goal with + | |- envs_entails _ _ => + eapply tac_forall_intro; + [apply _ || + let P := match goal with |- FromForall ?P _ => P end in + fail "iIntro: cannot turn" P "into a universal quantifier" + |lazy beta; intros x] + end). + +Local Tactic Notation "iIntro" constr(H) := + iStartProof; + first + [ (* (?Q → _) *) + eapply tac_impl_intro with _ H _ _ _; (* (i:=H) *) + [apply _ + |env_cbv; apply _ || + let P := lazymatch goal with |- Persistent ?P => P end in + fail 1 "iIntro: introducing non-persistent" H ":" P + "into non-empty spatial context" + |env_reflexivity || fail 1 "iIntro:" H "not fresh" + |apply _ + |] + | (* (_ -∗ _) *) + eapply tac_wand_intro with _ H _ _; (* (i:=H) *) + [apply _ + | env_reflexivity || fail 1 "iIntro:" H "not fresh" + |] + | fail "iIntro: nothing to introduce" ]. + +Local Tactic Notation "iIntro" "#" constr(H) := + iStartProof; + first + [ (* (?P → _) *) + eapply tac_impl_intro_persistent with _ H _ _ _; (* (i:=H) *) + [apply _ + |apply _ || + let P := match goal with |- IntoPersistent _ ?P _ => P end in + fail 1 "iIntro:" P "not persistent" + |env_reflexivity || fail 1 "iIntro:" H "not fresh" + |] + | (* (?P -∗ _) *) + eapply tac_wand_intro_persistent with _ H _ _ _; (* (i:=H) *) + [ apply _ + | apply _ || + let P := match goal with |- IntoPersistent _ ?P _ => P end in + fail 1 "iIntro:" P "not persistent" + |apply _ || + let P := match goal with |- TCOr (Affine ?P) _ => P end in + fail 1 "iIntro:" P "not affine and the goal not absorbing" + |env_reflexivity || fail 1 "iIntro:" H "not fresh" + |] + | fail "iIntro: nothing to introduce" ]. + +Local Tactic Notation "iIntro" "_" := + first + [ (* (?Q → _) *) + iStartProof; eapply tac_impl_intro_drop; + [ apply _ | ] + | (* (_ -∗ _) *) + iStartProof; eapply tac_wand_intro_drop; + [ apply _ + | apply _ || + let P := match goal with |- TCOr (Affine ?P) _ => P end in + fail 1 "iIntro:" P "not affine and the goal not absorbing" + |] + | (* (∀ _, _) *) iIntro (_) + | fail 1 "iIntro: nothing to introduce" ]. + +Local Tactic Notation "iIntroForall" := + lazymatch goal with + | |- ∀ _, ?P => fail (* actually an →, this is handled by iIntro below *) + | |- ∀ _, _ => intro + | |- let _ := _ in _ => intro + | |- _ => + iStartProof; + lazymatch goal with + | |- envs_entails _ (∀ x : _, _) => let x' := fresh x in iIntro (x') + end + end. +Local Tactic Notation "iIntro" := + lazymatch goal with + | |- _ → ?P => intro + | |- _ => + iStartProof; + lazymatch goal with + | |- envs_entails _ (_ -∗ _) => iIntro (?) || let H := iFresh in iIntro #H || iIntro H + | |- envs_entails _ (_ → _) => iIntro (?) || let H := iFresh in iIntro #H || iIntro H + end + end. + +(** * Specialize *) +Record iTrm {X As S} := + ITrm { itrm : X ; itrm_vars : hlist As ; itrm_hyps : S }. +Arguments ITrm {_ _ _} _ _ _. + +Notation "( H $! x1 .. xn )" := + (ITrm H (hcons x1 .. (hcons xn hnil) ..) "") (at level 0, x1, xn at level 9). +Notation "( H $! x1 .. xn 'with' pat )" := + (ITrm H (hcons x1 .. (hcons xn hnil) ..) pat) (at level 0, x1, xn at level 9). +Notation "( H 'with' pat )" := (ITrm H hnil pat) (at level 0). + +(** There is some hacky stuff going on here: because of Coq bug #6583, unresolved +type classes in the arguments `xs` are resolved at arbitrary moments. Tactics +like `apply`, `split` and `eexists` wrongly trigger type class search to resolve +these holes. To avoid TC being triggered too eagerly, this tactic uses `refine` +at most places instead of `apply`. *) +Local Tactic Notation "iSpecializeArgs" constr(H) open_constr(xs) := + let rec go xs := + lazymatch xs with + | hnil => idtac + | hcons ?x ?xs => + notypeclasses refine (tac_forall_specialize _ _ H _ _ _ _ _ _ _); + [env_reflexivity || fail "iSpecialize:" H "not found" + |iSolveTC || + let P := match goal with |- IntoForall ?P _ => P end in + fail "iSpecialize: cannot instantiate" P "with" x + |lazymatch goal with (* Force [A] in [ex_intro] to deal with coercions. *) + | |- ∃ _ : ?A, _ => + notypeclasses refine (@ex_intro A _ x (conj _ _)) + end; [shelve..|env_reflexivity|go xs]] + end in + go xs. + +Local Tactic Notation "iSpecializePat" open_constr(H) constr(pat) := + let solve_to_wand H1 := + iSolveTC || + let P := match goal with |- IntoWand _ _ ?P _ _ => P end in + fail "iSpecialize:" P "not an implication/wand" in + let solve_done d := + lazymatch d with + | true => + done || + let Q := match goal with |- envs_entails _ ?Q => Q end in + fail "iSpecialize: cannot solve" Q "using done" + | false => idtac + end in + let rec go H1 pats := + lazymatch pats with + | [] => idtac + | SForall :: ?pats => + idtac "[IPM] The * specialization pattern is deprecated because it is applied implicitly."; + go H1 pats + | SIdent ?H2 :: ?pats => + notypeclasses refine (tac_specialize _ _ _ H2 _ H1 _ _ _ _ _ _ _ _ _ _); + [env_reflexivity || fail "iSpecialize:" H2 "not found" + |env_reflexivity || fail "iSpecialize:" H1 "not found" + |iSolveTC || + let P := match goal with |- IntoWand _ _ ?P ?Q _ => P end in + let Q := match goal with |- IntoWand _ _ ?P ?Q _ => Q end in + fail "iSpecialize: cannot instantiate" P "with" Q + |env_reflexivity|go H1 pats] + | SPureGoal ?d :: ?pats => + notypeclasses refine (tac_specialize_assert_pure _ _ H1 _ _ _ _ _ _ _ _ _ _ _ _); + [env_reflexivity || fail "iSpecialize:" H1 "not found" + |solve_to_wand H1 + |iSolveTC || + let Q := match goal with |- FromPure _ ?Q _ => Q end in + fail "iSpecialize:" Q "not pure" + |env_reflexivity + |solve_done d (*goal*) + |go H1 pats] + | SGoal (SpecGoal GPersistent false ?Hs_frame [] ?d) :: ?pats => + notypeclasses refine (tac_specialize_assert_persistent _ _ _ H1 _ _ _ _ _ _ _ _ _ _ _ _ _); + [env_reflexivity || fail "iSpecialize:" H1 "not found" + |solve_to_wand H1 + |iSolveTC || + let Q := match goal with |- Persistent ?Q => Q end in + fail "iSpecialize:" Q "not persistent" + |iSolveTC + |env_reflexivity + |iFrame Hs_frame; solve_done d (*goal*) + |go H1 pats] + | SGoal (SpecGoal GPersistent _ _ _ _) :: ?pats => + fail "iSpecialize: cannot select hypotheses for persistent premise" + | SGoal (SpecGoal ?m ?lr ?Hs_frame ?Hs ?d) :: ?pats => + let Hs' := eval cbv in (if lr then Hs else Hs_frame ++ Hs) in + notypeclasses refine (tac_specialize_assert _ _ _ _ H1 _ lr Hs' _ _ _ _ _ _ _ _ _ _ _); + [env_reflexivity || fail "iSpecialize:" H1 "not found" + |solve_to_wand H1 + |lazymatch m with + | GSpatial => notypeclasses refine (add_modal_id _ _) + | GModal => iSolveTC || fail "iSpecialize: goal not a modality" + end + |env_reflexivity || + let Hs' := iMissingHyps Hs' in + fail "iSpecialize: hypotheses" Hs' "not found" + |iFrame Hs_frame; solve_done d (*goal*) + |go H1 pats] + | SAutoFrame GPersistent :: ?pats => + notypeclasses refine (tac_specialize_assert_persistent _ _ _ H1 _ _ _ _ _ _ _ _ _ _ _ _ _); + [env_reflexivity || fail "iSpecialize:" H1 "not found" + |solve_to_wand H1 + |iSolveTC || + let Q := match goal with |- Persistent ?Q => Q end in + fail "iSpecialize:" Q "not persistent" + |env_reflexivity + |solve [iFrame "∗ #"] + |go H1 pats] + | SAutoFrame ?m :: ?pats => + notypeclasses refine (tac_specialize_frame _ _ H1 _ _ _ _ _ _ _ _ _ _ _ _); + [env_reflexivity || fail "iSpecialize:" H1 "not found" + |solve_to_wand H1 + |lazymatch m with + | GSpatial => notypeclasses refine (add_modal_id _ _) + | GModal => iSolveTC || fail "iSpecialize: goal not a modality" + end + |first + [notypeclasses refine (tac_unlock_emp _ _ _) + |notypeclasses refine (tac_unlock_True _ _ _) + |iFrame "∗ #"; notypeclasses refine (tac_unlock _ _ _) + |fail "iSpecialize: premise cannot be solved by framing"] + |exact eq_refl]; iIntro H1; go H1 pats + end in let pats := spec_pat.parse pat in go H pats. + +(* The argument [p] denotes whether the conclusion of the specialized term is +persistent. If so, one can use all spatial hypotheses for both proving the +premises and the remaning goal. The argument [p] can either be a Boolean or an +introduction pattern, which will be coerced into [true] when it solely contains +`#` or `%` patterns at the top-level. + +In case the specialization pattern in [t] states that the modality of the goal +should be kept for one of the premises (i.e. [>[H1 .. Hn]] is used) then [p] +defaults to [false] (i.e. spatial hypotheses are not preserved). *) +Tactic Notation "iSpecializeCore" open_constr(H) + "with" open_constr(xs) open_constr(pat) "as" constr(p) := + let p := intro_pat_persistent p in + let pat := spec_pat.parse pat in + let H := + lazymatch type of H with + | string => constr:(INamed H) + | _ => H + end in + iSpecializeArgs H xs; [..| + lazymatch type of H with + | ident => + (* The lemma [tac_specialize_persistent_helper] allows one to use all + spatial hypotheses for both proving the premises of the lemma we + specialize as well as those of the remaining goal. We can only use it when + the result of the specialization is persistent, and no modality is + eliminated. As an optimization, we do not use this when only universal + quantifiers are instantiated. *) + let pat := spec_pat.parse pat in + lazymatch eval compute in + (p && bool_decide (pat ≠[]) && negb (existsb spec_pat_modal pat)) with + | true => + (* FIXME: do something reasonable when the BI is not affine *) + notypeclasses refine (tac_specialize_persistent_helper _ _ H _ _ _ _ _ _ _ _ _ _ _); + [env_reflexivity || fail "iSpecialize:" H "not found" + |iSpecializePat H pat; + [.. + |refine (tac_specialize_persistent_helper_done _ H _ _ _); + env_reflexivity] + |iSolveTC || + let Q := match goal with |- IntoPersistent _ ?Q _ => Q end in + fail "iSpecialize:" Q "not persistent" + |env_cbv; iSolveTC || + let Q := match goal with |- TCAnd _ (Affine ?Q) => Q end in + fail "iSpecialize:" Q "not affine" + |env_reflexivity + |(* goal *)] + | false => iSpecializePat H pat + end + | _ => fail "iSpecialize:" H "should be a hypothesis, use iPoseProof instead" + end]. + +Tactic Notation "iSpecializeCore" open_constr(t) "as" constr(p) := + lazymatch type of t with + | string => iSpecializeCore t with hnil "" as p + | ident => iSpecializeCore t with hnil "" as p + | _ => + lazymatch t with + | ITrm ?H ?xs ?pat => iSpecializeCore H with xs pat as p + | _ => fail "iSpecialize:" t "should be a proof mode term" + end + end. + +Tactic Notation "iSpecialize" open_constr(t) := + iSpecializeCore t as false. +Tactic Notation "iSpecialize" open_constr(t) "as" "#" := + iSpecializeCore t as true. + +(** * Pose proof *) +(* The tactic [iIntoEmpValid] tactic solves a goal [bi_emp_valid Q]. The +argument [t] must be a Coq term whose type is of the following shape: + +[∀ (x_1 : A_1) .. (x_n : A_n), φ] + +and so that we have an instance `AsValid φ Q`. + +Examples of such [φ]s are + +- [bi_emp_valid P], in which case [Q] should be [P] +- [P1 ⊢ P2], in which case [Q] should be [P1 -∗ P2] +- [P1 ⊣⊢ P2], in which case [Q] should be [P1 ↔ P2] + +The tactic instantiates each dependent argument [x_i] with an evar and generates +a goal [R] for each non-dependent argument [x_i : R]. For example, if the +original goal was [Q] and [t] has type [∀ x, P x → Q], then it generates an evar +[?x] for [x] and a subgoal [P ?x]. *) +Tactic Notation "iIntoEmpValid" open_constr(t) := + let rec go t := + (* We try two reduction tactics for the type of t before trying to + specialize it. We first try the head normal form in order to + unfold all the definition that could hide an entailment. Then, + we try the much weaker [eval cbv zeta], because entailment is + not necessarilly opaque, and could be unfolded by [hnf]. + + However, for calling type class search, we only use [cbv zeta] + in order to make sure we do not unfold [bi_emp_valid]. *) + let tT := type of t in + first + [ let tT' := eval hnf in tT in go_specialize t tT' + | let tT' := eval cbv zeta in tT in go_specialize t tT' + | let tT' := eval cbv zeta in tT in + notypeclasses refine (as_emp_valid_1 tT _ _); + [iSolveTC || fail "iPoseProof: not a BI assertion" + |exact t]] + with go_specialize t tT := + lazymatch tT with (* We do not use hnf of tT, because, if + entailment is not opaque, then it would + unfold it. *) + | ?P → ?Q => let H := fresh in assert P as H; [|go uconstr:(t H); clear H] + | ∀ _ : ?T, _ => + (* Put [T] inside an [id] to avoid TC inference from being invoked. *) + (* This is a workarround for Coq bug #6583. *) + let e := fresh in evar (e:id T); + let e' := eval unfold e in e in clear e; go (t e') + end + in + go t. + +(* The tactic [tac] is called with a temporary fresh name [H]. The argument +[lazy_tc] denotes whether type class inference on the premises of [lem] should +be performed before (if false) or after (if true) [tac H] is called. + +The tactic [iApply] uses laxy type class inference, so that evars can first be +instantiated by matching with the goal, whereas [iDestruct] does not, because +eliminations may not be performed when type classes have not been resolved. +*) +Tactic Notation "iPoseProofCore" open_constr(lem) + "as" constr(p) constr(lazy_tc) tactic(tac) := + iStartProof; + let Htmp := iFresh in + let t := lazymatch lem with ITrm ?t ?xs ?pat => t | _ => lem end in + let t := lazymatch type of t with string => constr:(INamed t) | _ => t end in + let spec_tac _ := + lazymatch lem with + | ITrm ?t ?xs ?pat => iSpecializeCore (ITrm Htmp xs pat) as p + | _ => idtac + end in + let go goal_tac := + lazymatch type of t with + | ident => + eapply tac_pose_proof_hyp with _ _ t _ Htmp _; + [env_reflexivity || fail "iPoseProof:" t "not found" + |env_reflexivity || fail "iPoseProof:" Htmp "not fresh" + |goal_tac ()] + | _ => + eapply tac_pose_proof with _ Htmp _; (* (j:=H) *) + [iIntoEmpValid t + |env_reflexivity || fail "iPoseProof:" Htmp "not fresh" + |goal_tac ()] + end; + try iSolveTC in + lazymatch eval compute in lazy_tc with + | true => go ltac:(fun _ => spec_tac (); last (tac Htmp)) + | false => go spec_tac; last (tac Htmp) + end. + +(** * Apply *) +Tactic Notation "iApplyHyp" constr(H) := + let rec go H := first + [eapply tac_apply with _ H _ _ _; + [env_reflexivity + |iSolveTC + |lazy beta (* reduce betas created by instantiation *)] + |iSpecializePat H "[]"; last go H] in + iExact H || + go H || + lazymatch iTypeOf H with + | Some (_,?Q) => fail "iApply: cannot apply" Q + end. + +Tactic Notation "iApply" open_constr(lem) := + iPoseProofCore lem as false true (fun H => iApplyHyp H). + +(** * Revert *) +Local Tactic Notation "iForallRevert" ident(x) := + let err x := + intros x; + iMatchHyp (fun H P => + lazymatch P with + | context [x] => fail 2 "iRevert:" x "is used in hypothesis" H + end) in + iStartProof; + let A := type of x in + lazymatch type of A with + | Prop => revert x; first [apply tac_pure_revert|err x] + | _ => revert x; first [apply tac_forall_revert|err x] + end. + +Tactic Notation "iRevert" constr(Hs) := + let rec go Hs := + lazymatch Hs with + | [] => idtac + | ESelPure :: ?Hs => + repeat match goal with x : _ |- _ => revert x end; + go Hs + | ESelIdent _ ?H :: ?Hs => + eapply tac_revert with _ H _ _; (* (i:=H2) *) + [env_reflexivity || fail "iRevert:" H "not found" + |env_cbv; go Hs] + end in + let Hs := iElaborateSelPat Hs in iStartProof; go Hs. + +Tactic Notation "iRevert" "(" ident(x1) ")" := + iForallRevert x1. +Tactic Notation "iRevert" "(" ident(x1) ident(x2) ")" := + iForallRevert x2; iRevert ( x1 ). +Tactic Notation "iRevert" "(" ident(x1) ident(x2) ident(x3) ")" := + iForallRevert x3; iRevert ( x1 x2 ). +Tactic Notation "iRevert" "(" ident(x1) ident(x2) ident(x3) ident(x4) ")" := + iForallRevert x4; iRevert ( x1 x2 x3 ). +Tactic Notation "iRevert" "(" ident(x1) ident(x2) ident(x3) ident(x4) + ident(x5) ")" := + iForallRevert x5; iRevert ( x1 x2 x3 x4 ). +Tactic Notation "iRevert" "(" ident(x1) ident(x2) ident(x3) ident(x4) + ident(x5) ident(x6) ")" := + iForallRevert x6; iRevert ( x1 x2 x3 x4 x5 ). +Tactic Notation "iRevert" "(" ident(x1) ident(x2) ident(x3) ident(x4) + ident(x5) ident(x6) ident(x7) ")" := + iForallRevert x7; iRevert ( x1 x2 x3 x4 x5 x6 ). +Tactic Notation "iRevert" "(" ident(x1) ident(x2) ident(x3) ident(x4) + ident(x5) ident(x6) ident(x7) ident(x8) ")" := + iForallRevert x8; iRevert ( x1 x2 x3 x4 x5 x6 x7 ). + +Tactic Notation "iRevert" "(" ident(x1) ")" constr(Hs) := + iRevert Hs; iRevert ( x1 ). +Tactic Notation "iRevert" "(" ident(x1) ident(x2) ")" constr(Hs) := + iRevert Hs; iRevert ( x1 x2 ). +Tactic Notation "iRevert" "(" ident(x1) ident(x2) ident(x3) ")" constr(Hs) := + iRevert Hs; iRevert ( x1 x2 x3 ). +Tactic Notation "iRevert" "(" ident(x1) ident(x2) ident(x3) ident(x4) ")" + constr(Hs) := + iRevert Hs; iRevert ( x1 x2 x3 x4 ). +Tactic Notation "iRevert" "(" ident(x1) ident(x2) ident(x3) ident(x4) + ident(x5) ")" constr(Hs) := + iRevert Hs; iRevert ( x1 x2 x3 x4 x5 ). +Tactic Notation "iRevert" "(" ident(x1) ident(x2) ident(x3) ident(x4) + ident(x5) ident(x6) ")" constr(Hs) := + iRevert Hs; iRevert ( x1 x2 x3 x4 x5 x6 ). +Tactic Notation "iRevert" "(" ident(x1) ident(x2) ident(x3) ident(x4) + ident(x5) ident(x6) ident(x7) ")" constr(Hs) := + iRevert Hs; iRevert ( x1 x2 x3 x4 x5 x6 x7 ). +Tactic Notation "iRevert" "(" ident(x1) ident(x2) ident(x3) ident(x4) + ident(x5) ident(x6) ident(x7) ident(x8) ")" constr(Hs) := + iRevert Hs; iRevert ( x1 x2 x3 x4 x5 x6 x7 x8 ). + +(** * Disjunction *) +Tactic Notation "iLeft" := + iStartProof; + eapply tac_or_l; + [iSolveTC || + let P := match goal with |- FromOr ?P _ _ => P end in + fail "iLeft:" P "not a disjunction" + |]. +Tactic Notation "iRight" := + iStartProof; + eapply tac_or_r; + [iSolveTC || + let P := match goal with |- FromOr ?P _ _ => P end in + fail "iRight:" P "not a disjunction" + |]. + +Local Tactic Notation "iOrDestruct" constr(H) "as" constr(H1) constr(H2) := + eapply tac_or_destruct with _ _ H _ H1 H2 _ _ _; (* (i:=H) (j1:=H1) (j2:=H2) *) + [env_reflexivity || fail "iOrDestruct:" H "not found" + |iSolveTC || + let P := match goal with |- IntoOr ?P _ _ => P end in + fail "iOrDestruct: cannot destruct" P + |env_reflexivity || fail "iOrDestruct:" H1 "not fresh" + |env_reflexivity || fail "iOrDestruct:" H2 "not fresh" + | |]. + +(** * Conjunction and separating conjunction *) +Tactic Notation "iSplit" := + iStartProof; + eapply tac_and_split; + [iSolveTC || + let P := match goal with |- FromAnd ?P _ _ => P end in + fail "iSplit:" P "not a conjunction"| |]. + +Tactic Notation "iSplitL" constr(Hs) := + iStartProof; + let Hs := words Hs in + let Hs := eval vm_compute in (INamed <$> Hs) in + eapply tac_sep_split with _ _ Left Hs _ _; (* (js:=Hs) *) + [iSolveTC || + let P := match goal with |- FromSep _ ?P _ _ => P end in + fail "iSplitL:" P "not a separating conjunction" + |env_reflexivity || + let Hs := iMissingHyps Hs in + fail "iSplitL: hypotheses" Hs "not found" + | |]. + +Tactic Notation "iSplitR" constr(Hs) := + iStartProof; + let Hs := words Hs in + let Hs := eval vm_compute in (INamed <$> Hs) in + eapply tac_sep_split with _ _ Right Hs _ _; (* (js:=Hs) *) + [iSolveTC || + let P := match goal with |- FromSep _ ?P _ _ => P end in + fail "iSplitR:" P "not a separating conjunction" + |env_reflexivity || + let Hs := iMissingHyps Hs in + fail "iSplitR: hypotheses" Hs "not found" + | |]. + +Tactic Notation "iSplitL" := iSplitR "". +Tactic Notation "iSplitR" := iSplitL "". + +Local Tactic Notation "iAndDestruct" constr(H) "as" constr(H1) constr(H2) := + eapply tac_and_destruct with _ H _ H1 H2 _ _ _; (* (i:=H) (j1:=H1) (j2:=H2) *) + [env_reflexivity || fail "iAndDestruct:" H "not found" + |env_cbv; iSolveTC || + let P := + lazymatch goal with + | |- IntoSep ?P _ _ => P + | |- IntoAnd _ ?P _ _ => P + end in + fail "iAndDestruct: cannot destruct" P + |env_reflexivity || fail "iAndDestruct:" H1 "or" H2 " not fresh"|]. + +Local Tactic Notation "iAndDestructChoice" constr(H) "as" constr(d) constr(H') := + eapply tac_and_destruct_choice with _ H _ d H' _ _ _; + [env_reflexivity || fail "iAndDestructChoice:" H "not found" + |env_cbv; iSolveTC || + let P := match goal with |- TCOr (IntoAnd _ ?P _ _) _ => P end in + fail "iAndDestructChoice: cannot destruct" P + |env_reflexivity || fail "iAndDestructChoice:" H' " not fresh"|]. + +(** * Combinining hypotheses *) +Tactic Notation "iCombine" constr(Hs) "as" constr(H) := + let Hs := words Hs in + let Hs := eval vm_compute in (INamed <$> Hs) in + eapply tac_combine with _ _ Hs _ _ H _; + [env_reflexivity || + let Hs := iMissingHyps Hs in + fail "iCombine: hypotheses" Hs "not found" + |iSolveTC + |env_reflexivity || fail "iCombine:" H "not fresh"|]. + +Tactic Notation "iCombine" constr(H1) constr(H2) "as" constr(H) := + iCombine [H1;H2] as H. + +(** * Existential *) +Tactic Notation "iExists" uconstr(x1) := + iStartProof; + eapply tac_exist; + [iSolveTC || + let P := match goal with |- FromExist ?P _ => P end in + fail "iExists:" P "not an existential" + |cbv beta; eexists x1]. + +Tactic Notation "iExists" uconstr(x1) "," uconstr(x2) := + iExists x1; iExists x2. +Tactic Notation "iExists" uconstr(x1) "," uconstr(x2) "," uconstr(x3) := + iExists x1; iExists x2, x3. +Tactic Notation "iExists" uconstr(x1) "," uconstr(x2) "," uconstr(x3) "," + uconstr(x4) := + iExists x1; iExists x2, x3, x4. +Tactic Notation "iExists" uconstr(x1) "," uconstr(x2) "," uconstr(x3) "," + uconstr(x4) "," uconstr(x5) := + iExists x1; iExists x2, x3, x4, x5. +Tactic Notation "iExists" uconstr(x1) "," uconstr(x2) "," uconstr(x3) "," + uconstr(x4) "," uconstr(x5) "," uconstr(x6) := + iExists x1; iExists x2, x3, x4, x5, x6. +Tactic Notation "iExists" uconstr(x1) "," uconstr(x2) "," uconstr(x3) "," + uconstr(x4) "," uconstr(x5) "," uconstr(x6) "," uconstr(x7) := + iExists x1; iExists x2, x3, x4, x5, x6, x7. +Tactic Notation "iExists" uconstr(x1) "," uconstr(x2) "," uconstr(x3) "," + uconstr(x4) "," uconstr(x5) "," uconstr(x6) "," uconstr(x7) "," + uconstr(x8) := + iExists x1; iExists x2, x3, x4, x5, x6, x7, x8. + +Local Tactic Notation "iExistDestruct" constr(H) + "as" simple_intropattern(x) constr(Hx) := + eapply tac_exist_destruct with H _ Hx _ _; (* (i:=H) (j:=Hx) *) + [env_reflexivity || fail "iExistDestruct:" H "not found" + |iSolveTC || + let P := match goal with |- IntoExist ?P _ => P end in + fail "iExistDestruct: cannot destruct" P|]; + let y := fresh in + intros y; eexists; split; + [env_reflexivity || fail "iExistDestruct:" Hx "not fresh" + |revert y; intros x]. + +(** * Modality introduction *) +Tactic Notation "iModIntro" uconstr(sel) := + iStartProof; + notypeclasses refine (tac_modal_intro _ sel _ _ _ _ _ _ _ _ _ _ _ _ _); + [iSolveTC || + fail "iModIntro: the goal is not a modality" + |iSolveTC || + let s := lazymatch goal with |- IntoModalPersistentEnv _ _ _ ?s => s end in + lazymatch eval hnf in s with + | MIEnvForall ?C => fail "iModIntro: persistent context does not satisfy" C + | MIEnvIsEmpty => fail "iModIntro: persistent context is non-empty" + end + |iSolveTC || + let s := lazymatch goal with |- IntoModalPersistentEnv _ _ _ ?s => s end in + lazymatch eval hnf in s with + | MIEnvForall ?C => fail "iModIntro: spatial context does not satisfy" C + | MIEnvIsEmpty => fail "iModIntro: spatial context is non-empty" + end + |env_cbv; iSolveTC || + fail "iModIntro: cannot filter spatial context when goal is not absorbing" + |]. +Tactic Notation "iModIntro" := iModIntro _. +Tactic Notation "iAlways" := iModIntro. + +(** * Later *) +Tactic Notation "iNext" open_constr(n) := iModIntro (â–·^n _)%I. +Tactic Notation "iNext" := iModIntro (â–·^_ _)%I. + +(** * Update modality *) +Tactic Notation "iModCore" constr(H) := + eapply tac_modal_elim with _ H _ _ _ _ _ _; + [env_reflexivity || fail "iMod:" H "not found" + |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 + |try fast_done (* optional side-condition *) + |env_reflexivity|]. + +(** * Basic destruct tactic *) +Tactic Notation "iDestructHyp" constr(H) "as" constr(pat) := + let rec go Hz pat := + lazymatch pat with + | IAnom => + lazymatch Hz with + | IAnon _ => idtac + | INamed ?Hz => let Hz' := iFresh in iRename Hz into Hz' + end + | IDrop => iClearHyp Hz + | IFrame => iFrameHyp Hz + | IIdent ?y => iRename Hz into y + | IList [[]] => iExFalso; iExact Hz + | IList [[?pat1; IDrop]] => iAndDestructChoice Hz as Left Hz; go Hz pat1 + | IList [[IDrop; ?pat2]] => iAndDestructChoice Hz as Right Hz; go Hz pat2 + | IList [[?pat1; ?pat2]] => + let Hy := iFresh in iAndDestruct Hz as Hz Hy; go Hz pat1; go Hy pat2 + | IList [[?pat1];[?pat2]] => iOrDestruct Hz as Hz Hz; [go Hz pat1|go Hz pat2] + | IPureElim => iPure Hz as ? + | IRewrite Right => iPure Hz as -> + | IRewrite Left => iPure Hz as <- + | IAlwaysElim ?pat => iPersistent Hz; go Hz pat + | IModalElim ?pat => iModCore Hz; go Hz pat + | _ => fail "iDestruct:" pat "invalid" + end in + let rec find_pat found pats := + lazymatch pats with + | [] => + lazymatch found with + | true => idtac + | false => fail "iDestruct:" pat "should contain exactly one proper introduction pattern" + end + | ISimpl :: ?pats => simpl; find_pat found pats + | IClear ?H :: ?pats => iClear H; find_pat found pats + | IClearFrame ?H :: ?pats => iFrame H; find_pat found pats + | ?pat :: ?pats => + lazymatch found with + | false => go H pat; find_pat true pats + | true => fail "iDestruct:" pat "should contain exactly one proper introduction pattern" + end + end in + let pats := intro_pat.parse pat in + find_pat false pats. + +Tactic Notation "iDestructHyp" constr(H) "as" "(" simple_intropattern(x1) ")" + constr(pat) := + iExistDestruct H as x1 H; iDestructHyp H as @ pat. +Tactic Notation "iDestructHyp" constr(H) "as" "(" simple_intropattern(x1) + simple_intropattern(x2) ")" constr(pat) := + iExistDestruct H as x1 H; iDestructHyp H as ( x2 ) pat. +Tactic Notation "iDestructHyp" constr(H) "as" "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) ")" constr(pat) := + iExistDestruct H as x1 H; iDestructHyp H as ( x2 x3 ) pat. +Tactic Notation "iDestructHyp" constr(H) "as" "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) ")" + constr(pat) := + iExistDestruct H as x1 H; iDestructHyp H as ( x2 x3 x4 ) pat. +Tactic Notation "iDestructHyp" constr(H) "as" "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) + simple_intropattern(x5) ")" constr(pat) := + iExistDestruct H as x1 H; iDestructHyp H as ( x2 x3 x4 x5 ) pat. +Tactic Notation "iDestructHyp" constr(H) "as" "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) + simple_intropattern(x5) simple_intropattern(x6) ")" constr(pat) := + iExistDestruct H as x1 H; iDestructHyp H as ( x2 x3 x4 x5 x6 ) pat. +Tactic Notation "iDestructHyp" constr(H) "as" "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) + simple_intropattern(x5) simple_intropattern(x6) simple_intropattern(x7) ")" + constr(pat) := + iExistDestruct H as x1 H; iDestructHyp H as ( x2 x3 x4 x5 x6 x7 ) pat. +Tactic Notation "iDestructHyp" constr(H) "as" "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) + simple_intropattern(x5) simple_intropattern(x6) simple_intropattern(x7) + simple_intropattern(x8) ")" constr(pat) := + iExistDestruct H as x1 H; iDestructHyp H as ( x2 x3 x4 x5 x6 x7 x8 ) pat. + +(** * Introduction tactic *) +Tactic Notation "iIntros" constr(pat) := + let rec go pats startproof := + lazymatch pats with + | [] => + lazymatch startproof with + | true => iStartProof + | false => idtac + end + (* Optimizations to avoid generating fresh names *) + | IPureElim :: ?pats => iIntro (?); go pats startproof + | IAlwaysElim (IIdent ?H) :: ?pats => iIntro #H; go pats false + | IDrop :: ?pats => iIntro _; go pats startproof + | IIdent ?H :: ?pats => iIntro H; go pats startproof + (* Introduction patterns that can only occur at the top-level *) + | IPureIntro :: ?pats => iPureIntro; go pats false + | IAlwaysIntro :: ?pats => iAlways; go pats false + | IModalIntro :: ?pats => iModIntro; go pats false + | IForall :: ?pats => repeat iIntroForall; go pats startproof + | IAll :: ?pats => repeat (iIntroForall || iIntro); go pats startproof + (* Clearing and simplifying introduction patterns *) + | ISimpl :: ?pats => simpl; go pats startproof + | IClear ?H :: ?pats => iClear H; go pats false + | IClearFrame ?H :: ?pats => iFrame H; go pats false + | IDone :: ?pats => try done; go pats startproof + (* Introduction + destruct *) + | IAlwaysElim ?pat :: ?pats => + let H := iFresh in iIntro #H; iDestructHyp H as pat; go pats false + | ?pat :: ?pats => + let H := iFresh in iIntro H; iDestructHyp H as pat; go pats false + end + in let pats := intro_pat.parse pat in go pats true. +Tactic Notation "iIntros" := iIntros [IAll]. + +Tactic Notation "iIntros" "(" simple_intropattern(x1) ")" := + iIntro ( x1 ). +Tactic Notation "iIntros" "(" simple_intropattern(x1) + simple_intropattern(x2) ")" := + iIntros ( x1 ); iIntro ( x2 ). +Tactic Notation "iIntros" "(" simple_intropattern(x1) simple_intropattern(x2) + simple_intropattern(x3) ")" := + iIntros ( x1 x2 ); iIntro ( x3 ). +Tactic Notation "iIntros" "(" simple_intropattern(x1) simple_intropattern(x2) + simple_intropattern(x3) simple_intropattern(x4) ")" := + iIntros ( x1 x2 x3 ); iIntro ( x4 ). +Tactic Notation "iIntros" "(" simple_intropattern(x1) simple_intropattern(x2) + simple_intropattern(x3) simple_intropattern(x4) simple_intropattern(x5) ")" := + iIntros ( x1 x2 x3 x4 ); iIntro ( x5 ). +Tactic Notation "iIntros" "(" simple_intropattern(x1) simple_intropattern(x2) + simple_intropattern(x3) simple_intropattern(x4) simple_intropattern(x5) + simple_intropattern(x6) ")" := + iIntros ( x1 x2 x3 x4 x5 ); iIntro ( x6 ). +Tactic Notation "iIntros" "(" simple_intropattern(x1) simple_intropattern(x2) + simple_intropattern(x3) simple_intropattern(x4) simple_intropattern(x5) + simple_intropattern(x6) simple_intropattern(x7) ")" := + iIntros ( x1 x2 x3 x4 x5 x6 ); iIntro ( x7 ). +Tactic Notation "iIntros" "(" simple_intropattern(x1) simple_intropattern(x2) + simple_intropattern(x3) simple_intropattern(x4) simple_intropattern(x5) + simple_intropattern(x6) simple_intropattern(x7) simple_intropattern(x8) ")" := + iIntros ( x1 x2 x3 x4 x5 x6 x7 ); iIntro ( x8 ). +Tactic Notation "iIntros" "(" simple_intropattern(x1) simple_intropattern(x2) + simple_intropattern(x3) simple_intropattern(x4) simple_intropattern(x5) + simple_intropattern(x6) simple_intropattern(x7) simple_intropattern(x8) + simple_intropattern(x9) ")" := + iIntros ( x1 x2 x3 x4 x5 x6 x7 x8 ); iIntro ( x9 ). +Tactic Notation "iIntros" "(" simple_intropattern(x1) simple_intropattern(x2) + simple_intropattern(x3) simple_intropattern(x4) simple_intropattern(x5) + simple_intropattern(x6) simple_intropattern(x7) simple_intropattern(x8) + simple_intropattern(x9) simple_intropattern(x10) ")" := + iIntros ( x1 x2 x3 x4 x5 x6 x7 x8 x9 ); iIntro ( x10 ). +Tactic Notation "iIntros" "(" simple_intropattern(x1) simple_intropattern(x2) + simple_intropattern(x3) simple_intropattern(x4) simple_intropattern(x5) + simple_intropattern(x6) simple_intropattern(x7) simple_intropattern(x8) + simple_intropattern(x9) simple_intropattern(x10) simple_intropattern(x11) ")" := + iIntros ( x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 ); iIntro ( x11 ). +Tactic Notation "iIntros" "(" simple_intropattern(x1) simple_intropattern(x2) + simple_intropattern(x3) simple_intropattern(x4) simple_intropattern(x5) + simple_intropattern(x6) simple_intropattern(x7) simple_intropattern(x8) + simple_intropattern(x9) simple_intropattern(x10) simple_intropattern(x11) + simple_intropattern(x12) ")" := + iIntros ( x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 x11 ); iIntro ( x12 ). + +Tactic Notation "iIntros" "(" simple_intropattern(x1) ")" constr(p) := + iIntros ( x1 ); iIntros p. +Tactic Notation "iIntros" "(" simple_intropattern(x1) simple_intropattern(x2) + ")" constr(p) := + iIntros ( x1 x2 ); iIntros p. +Tactic Notation "iIntros" "(" simple_intropattern(x1) simple_intropattern(x2) + simple_intropattern(x3) ")" constr(p) := + iIntros ( x1 x2 x3 ); iIntros p. +Tactic Notation "iIntros" "(" simple_intropattern(x1) simple_intropattern(x2) + simple_intropattern(x3) simple_intropattern(x4) ")" constr(p) := + iIntros ( x1 x2 x3 x4 ); iIntros p. +Tactic Notation "iIntros" "(" simple_intropattern(x1) simple_intropattern(x2) + simple_intropattern(x3) simple_intropattern(x4) simple_intropattern(x5) + ")" constr(p) := + iIntros ( x1 x2 x3 x4 x5 ); iIntros p. +Tactic Notation "iIntros" "(" simple_intropattern(x1) simple_intropattern(x2) + simple_intropattern(x3) simple_intropattern(x4) simple_intropattern(x5) + simple_intropattern(x6) ")" constr(p) := + iIntros ( x1 x2 x3 x4 x5 x6 ); iIntros p. +Tactic Notation "iIntros" "(" simple_intropattern(x1) simple_intropattern(x2) + simple_intropattern(x3) simple_intropattern(x4) simple_intropattern(x5) + simple_intropattern(x6) simple_intropattern(x7) ")" constr(p) := + iIntros ( x1 x2 x3 x4 x5 x6 x7 ); iIntros p. +Tactic Notation "iIntros" "(" simple_intropattern(x1) simple_intropattern(x2) + simple_intropattern(x3) simple_intropattern(x4) simple_intropattern(x5) + simple_intropattern(x6) simple_intropattern(x7) simple_intropattern(x8) + ")" constr(p) := + iIntros ( x1 x2 x3 x4 x5 x6 x7 x8 ); iIntros p. +Tactic Notation "iIntros" "(" simple_intropattern(x1) simple_intropattern(x2) + simple_intropattern(x3) simple_intropattern(x4) simple_intropattern(x5) + simple_intropattern(x6) simple_intropattern(x7) simple_intropattern(x8) + simple_intropattern(x9) ")" constr(p) := + iIntros ( x1 x2 x3 x4 x5 x6 x7 x8 x9 ); iIntros p. +Tactic Notation "iIntros" "(" simple_intropattern(x1) simple_intropattern(x2) + simple_intropattern(x3) simple_intropattern(x4) simple_intropattern(x5) + simple_intropattern(x6) simple_intropattern(x7) simple_intropattern(x8) + simple_intropattern(x9) simple_intropattern(x10) ")" constr(p) := + iIntros ( x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 ); iIntros p. +Tactic Notation "iIntros" "(" simple_intropattern(x1) simple_intropattern(x2) + simple_intropattern(x3) simple_intropattern(x4) simple_intropattern(x5) + simple_intropattern(x6) simple_intropattern(x7) simple_intropattern(x8) + simple_intropattern(x9) simple_intropattern(x10) simple_intropattern(x11) + ")" constr(p) := + iIntros ( x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 x11 ); iIntros p. +Tactic Notation "iIntros" "(" simple_intropattern(x1) simple_intropattern(x2) + simple_intropattern(x3) simple_intropattern(x4) simple_intropattern(x5) + simple_intropattern(x6) simple_intropattern(x7) simple_intropattern(x8) + simple_intropattern(x9) simple_intropattern(x10) simple_intropattern(x11) + simple_intropattern(x12) ")" constr(p) := + iIntros ( x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 x11 x12 ); iIntros p. + +Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) ")" := + iIntros p; iIntros ( x1 ). +Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) + simple_intropattern(x2) ")" := + iIntros p; iIntros ( x1 x2 ). +Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) ")" := + iIntros p; iIntros ( x1 x2 x3 ). +Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) ")" := + iIntros p; iIntros ( x1 x2 x3 x4 ). +Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) + simple_intropattern(x5) ")" := + iIntros p; iIntros ( x1 x2 x3 x4 x5 ). +Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) + simple_intropattern(x5) simple_intropattern(x6) ")" := + iIntros p; iIntros ( x1 x2 x3 x4 x5 x6 ). +Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) + simple_intropattern(x5) simple_intropattern(x6) simple_intropattern(x7) ")" := + iIntros p; iIntros ( x1 x2 x3 x4 x5 x6 x7 ). +Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) + simple_intropattern(x5) simple_intropattern(x6) simple_intropattern(x7) + simple_intropattern(x8) ")" := + iIntros p; iIntros ( x1 x2 x3 x4 x5 x6 x7 x8 ). +Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) + simple_intropattern(x5) simple_intropattern(x6) simple_intropattern(x7) + simple_intropattern(x8) simple_intropattern(x9) ")" := + iIntros p; iIntros ( x1 x2 x3 x4 x5 x6 x7 x8 x9 ). +Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) + simple_intropattern(x5) simple_intropattern(x6) simple_intropattern(x7) + simple_intropattern(x8) simple_intropattern(x9) simple_intropattern(x10) ")" := + iIntros p; iIntros ( x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 ). +Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) + simple_intropattern(x5) simple_intropattern(x6) simple_intropattern(x7) + simple_intropattern(x8) simple_intropattern(x9) simple_intropattern(x10) + simple_intropattern(x11) ")" := + iIntros p; iIntros ( x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 x11 ). +Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) + simple_intropattern(x5) simple_intropattern(x6) simple_intropattern(x7) + simple_intropattern(x8) simple_intropattern(x9) simple_intropattern(x10) + simple_intropattern(x11) simple_intropattern(x12) ")" := + iIntros p; iIntros ( x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 x11 x12 ). + +Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) ")" constr(p2) := + iIntros p; iIntros ( x1 ); iIntros p2. +Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) + simple_intropattern(x2) ")" constr(p2) := + iIntros p; iIntros ( x1 x2 ); iIntros p2. +Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) ")" constr(p2) := + iIntros p; iIntros ( x1 x2 x3 ); iIntros p2. +Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) + ")" constr(p2) := + iIntros p; iIntros ( x1 x2 x3 x4 ); iIntros p2. +Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) + simple_intropattern(x5) ")" constr(p2) := + iIntros p; iIntros ( x1 x2 x3 x4 x5 ); iIntros p2. +Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) + simple_intropattern(x5) simple_intropattern(x6) ")" constr(p2) := + iIntros p; iIntros ( x1 x2 x3 x4 x5 x6 ); iIntros p2. +Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) + simple_intropattern(x5) simple_intropattern(x6) simple_intropattern(x7) + ")" constr(p2) := + iIntros p; iIntros ( x1 x2 x3 x4 x5 x6 x7 ); iIntros p2. +Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) + simple_intropattern(x5) simple_intropattern(x6) simple_intropattern(x7) + simple_intropattern(x8) ")" constr(p2) := + iIntros p; iIntros ( x1 x2 x3 x4 x5 x6 x7 x8 ); iIntros p2. +Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) + simple_intropattern(x5) simple_intropattern(x6) simple_intropattern(x7) + simple_intropattern(x8) simple_intropattern(x9) ")" constr(p2) := + iIntros p; iIntros ( x1 x2 x3 x4 x5 x6 x7 x8 x9 ); iIntros p2. +Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) + simple_intropattern(x5) simple_intropattern(x6) simple_intropattern(x7) + simple_intropattern(x8) simple_intropattern(x9) simple_intropattern(x10) + ")" constr(p2) := + iIntros p; iIntros ( x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 ); iIntros p2. +Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) + simple_intropattern(x5) simple_intropattern(x6) simple_intropattern(x7) + simple_intropattern(x8) simple_intropattern(x9) simple_intropattern(x10) + simple_intropattern(x11) ")" constr(p2) := + iIntros p; iIntros ( x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 x11 ); iIntros p2. +Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) + simple_intropattern(x5) simple_intropattern(x6) simple_intropattern(x7) + simple_intropattern(x8) simple_intropattern(x9) simple_intropattern(x10) + simple_intropattern(x11) simple_intropattern(x12) ")" constr(p2) := + iIntros p; iIntros ( x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 x11 x12 ); iIntros p2. + + +(* Used for generalization in iInduction and iLöb *) +Tactic Notation "iRevertIntros" constr(Hs) "with" tactic(tac) := + let rec go Hs := + lazymatch Hs with + | [] => tac + | ESelPure :: ?Hs => fail "iRevertIntros: % not supported" + | ESelIdent ?p ?H :: ?Hs => + iRevert H; go Hs; + let H' := + match p with true => constr:([IAlwaysElim (IIdent H)]) | false => H end in + iIntros H' + end in + try iStartProof; let Hs := iElaborateSelPat Hs in go Hs. + +Tactic Notation "iRevertIntros" "(" ident(x1) ")" constr(Hs) "with" tactic(tac):= + iRevertIntros Hs with (iRevert (x1); tac; iIntros (x1)). +Tactic Notation "iRevertIntros" "(" ident(x1) ident(x2) ")" constr(Hs) + "with" tactic(tac):= + iRevertIntros Hs with (iRevert (x1 x2); tac; iIntros (x1 x2)). +Tactic Notation "iRevertIntros" "(" ident(x1) ident(x2) ident(x3) ")" constr(Hs) + "with" tactic(tac):= + iRevertIntros Hs with (iRevert (x1 x2 x3); tac; iIntros (x1 x2 x3)). +Tactic Notation "iRevertIntros" "(" ident(x1) ident(x2) ident(x3) ident(x4) ")" + constr(Hs) "with" tactic(tac):= + iRevertIntros Hs with (iRevert (x1 x2 x3 x4); tac; iIntros (x1 x2 x3 x4)). +Tactic Notation "iRevertIntros" "(" ident(x1) ident(x2) ident(x3) ident(x4) + ident(x5) ")" constr(Hs) "with" tactic(tac):= + iRevertIntros Hs with (iRevert (x1 x2 x3 x4 x5); tac; iIntros (x1 x2 x3 x4 x5)). +Tactic Notation "iRevertIntros" "(" ident(x1) ident(x2) ident(x3) ident(x4) + ident(x5) ident(x6) ")" constr(Hs) "with" tactic(tac):= + iRevertIntros Hs with (iRevert (x1 x2 x3 x4 x5 x6); + tac; iIntros (x1 x2 x3 x4 x5 x6)). +Tactic Notation "iRevertIntros" "(" ident(x1) ident(x2) ident(x3) ident(x4) + ident(x5) ident(x6) ident(x7) ")" constr(Hs) "with" tactic(tac):= + iRevertIntros Hs with (iRevert (x1 x2 x3 x4 x5 x6 x7); + tac; iIntros (x1 x2 x3 x4 x5 x6 x7)). +Tactic Notation "iRevertIntros" "(" ident(x1) ident(x2) ident(x3) ident(x4) + ident(x5) ident(x6) ident(x7) ident(x8) ")" constr(Hs) "with" tactic(tac):= + iRevertIntros Hs with (iRevert (x1 x2 x3 x4 x5 x6 x7 x8); + tac; iIntros (x1 x2 x3 x4 x5 x6 x7 x8)). + +Tactic Notation "iRevertIntros" "with" tactic(tac) := + iRevertIntros "" with tac. +Tactic Notation "iRevertIntros" "(" ident(x1) ")" "with" tactic(tac):= + iRevertIntros (x1) "" with tac. +Tactic Notation "iRevertIntros" "(" ident(x1) ident(x2) ")" "with" tactic(tac):= + iRevertIntros (x1 x2) "" with tac. +Tactic Notation "iRevertIntros" "(" ident(x1) ident(x2) ident(x3) ")" + "with" tactic(tac):= + iRevertIntros (x1 x2 x3) "" with tac. +Tactic Notation "iRevertIntros" "(" ident(x1) ident(x2) ident(x3) ident(x4) ")" + "with" tactic(tac):= + iRevertIntros (x1 x2 x3 x4) "" with tac. +Tactic Notation "iRevertIntros" "(" ident(x1) ident(x2) ident(x3) ident(x4) + ident(x5) ")" "with" tactic(tac):= + iRevertIntros (x1 x2 x3 x4 x5) "" with tac. +Tactic Notation "iRevertIntros" "(" ident(x1) ident(x2) ident(x3) ident(x4) + ident(x5) ident(x6) ")" "with" tactic(tac):= + iRevertIntros (x1 x2 x3 x4 x5 x6) "" with tac. +Tactic Notation "iRevertIntros" "(" ident(x1) ident(x2) ident(x3) ident(x4) + ident(x5) ident(x6) ident(x7) ")" "with" tactic(tac):= + iRevertIntros (x1 x2 x3 x4 x5 x6 x7) "" with tac. +Tactic Notation "iRevertIntros" "(" ident(x1) ident(x2) ident(x3) ident(x4) + ident(x5) ident(x6) ident(x7) ident(x8) ")" "with" tactic(tac):= + iRevertIntros (x1 x2 x3 x4 x5 x6 x7 x8) "" with tac. + +(** * Destruct tactic *) +Class CopyDestruct {PROP : bi} (P : PROP). +Arguments CopyDestruct {_} _%I. +Hint Mode CopyDestruct + ! : typeclass_instances. + +Instance copy_destruct_forall {PROP : bi} {A} (Φ : A → PROP) : CopyDestruct (∀ x, Φ x). +Instance copy_destruct_impl {PROP : bi} (P Q : PROP) : + CopyDestruct Q → CopyDestruct (P → Q). +Instance copy_destruct_wand {PROP : bi} (P Q : PROP) : + CopyDestruct Q → CopyDestruct (P -∗ Q). +Instance copy_destruct_affinely {PROP : bi} (P : PROP) : + CopyDestruct P → CopyDestruct (<affine> P). +Instance copy_destruct_persistently {PROP : bi} (P : PROP) : + CopyDestruct P → CopyDestruct (<pers> P). + +Tactic Notation "iDestructCore" open_constr(lem) "as" constr(p) tactic(tac) := + let ident := + lazymatch type of lem with + | ident => constr:(Some lem) + | string => constr:(Some (INamed lem)) + | iTrm => + lazymatch lem with + | @iTrm ident ?H _ _ => constr:(Some H) + | @iTrm string ?H _ _ => constr:(Some (INamed H)) + | _ => constr:(@None ident) + end + | _ => constr:(@None ident) + end in + let intro_destruct n := + let rec go n' := + lazymatch n' with + | 0 => fail "iDestruct: cannot introduce" n "hypotheses" + | 1 => repeat iIntroForall; let H := iFresh in iIntro H; tac H + | S ?n' => repeat iIntroForall; let H := iFresh in iIntro H; go n' + end in + intros; go n in + lazymatch type of lem with + | nat => intro_destruct lem + | Z => (* to make it work in Z_scope. We should just be able to bind + tactic notation arguments to notation scopes. *) + let n := eval compute in (Z.to_nat lem) in intro_destruct n + | _ => + (* Only copy the hypothesis in case there is a [CopyDestruct] instance. + Also, rule out cases in which it does not make sense to copy, namely when + destructing a lemma (instead of a hypothesis) or a spatial hyopthesis + (which cannot be kept). *) + iStartProof; + lazymatch ident with + | None => iPoseProofCore lem as p false tac + | Some ?H => + lazymatch iTypeOf H with + | None => fail "iDestruct:" H "not found" + | Some (true, ?P) => + (* persistent hypothesis, check for a CopyDestruct instance *) + tryif (let dummy := constr:(_ : CopyDestruct P) in idtac) + then (iPoseProofCore lem as p false tac) + else (iSpecializeCore lem as p; last (tac H)) + | Some (false, ?P) => + (* spatial hypothesis, cannot copy *) + iSpecializeCore lem as p; last (tac H) + end + end + end. + +Tactic Notation "iDestruct" open_constr(lem) "as" constr(pat) := + iDestructCore lem as pat (fun H => iDestructHyp H as pat). +Tactic Notation "iDestruct" open_constr(lem) "as" "(" simple_intropattern(x1) ")" + constr(pat) := + iDestructCore lem as pat (fun H => iDestructHyp H as ( x1 ) pat). +Tactic Notation "iDestruct" open_constr(lem) "as" "(" simple_intropattern(x1) + simple_intropattern(x2) ")" constr(pat) := + iDestructCore lem as pat (fun H => iDestructHyp H as ( x1 x2 ) pat). +Tactic Notation "iDestruct" open_constr(lem) "as" "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) ")" constr(pat) := + iDestructCore lem as pat (fun H => iDestructHyp H as ( x1 x2 x3 ) pat). +Tactic Notation "iDestruct" open_constr(lem) "as" "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) ")" + constr(pat) := + iDestructCore lem as pat (fun H => iDestructHyp H as ( x1 x2 x3 x4 ) pat). +Tactic Notation "iDestruct" open_constr(lem) "as" "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) + simple_intropattern(x5) ")" constr(pat) := + iDestructCore lem as pat (fun H => iDestructHyp H as ( x1 x2 x3 x4 x5 ) pat). +Tactic Notation "iDestruct" open_constr(lem) "as" "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) + simple_intropattern(x5) simple_intropattern(x6) ")" constr(pat) := + iDestructCore lem as pat (fun H => iDestructHyp H as ( x1 x2 x3 x4 x5 x6 ) pat). +Tactic Notation "iDestruct" open_constr(lem) "as" "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) + simple_intropattern(x5) simple_intropattern(x6) simple_intropattern(x7) ")" + constr(pat) := + iDestructCore lem as pat (fun H => iDestructHyp H as ( x1 x2 x3 x4 x5 x6 x7 ) pat). +Tactic Notation "iDestruct" open_constr(lem) "as" "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) + simple_intropattern(x5) simple_intropattern(x6) simple_intropattern(x7) + simple_intropattern(x8) ")" constr(pat) := + iDestructCore lem as pat (fun H => iDestructHyp H as ( x1 x2 x3 x4 x5 x6 x7 x8 ) pat). + +Tactic Notation "iDestruct" open_constr(lem) "as" "%" simple_intropattern(pat) := + iDestructCore lem as true (fun H => iPure H as pat). + +Tactic Notation "iPoseProof" open_constr(lem) "as" constr(pat) := + iPoseProofCore lem as pat false (fun H => iDestructHyp H as pat). +Tactic Notation "iPoseProof" open_constr(lem) "as" "(" simple_intropattern(x1) ")" + constr(pat) := + iPoseProofCore lem as pat false (fun H => iDestructHyp H as ( x1 ) pat). +Tactic Notation "iPoseProof" open_constr(lem) "as" "(" simple_intropattern(x1) + simple_intropattern(x2) ")" constr(pat) := + iPoseProofCore lem as pat false (fun H => iDestructHyp H as ( x1 x2 ) pat). +Tactic Notation "iPoseProof" open_constr(lem) "as" "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) ")" constr(pat) := + iPoseProofCore lem as pat false (fun H => iDestructHyp H as ( x1 x2 x3 ) pat). +Tactic Notation "iPoseProof" open_constr(lem) "as" "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) ")" + constr(pat) := + iPoseProofCore lem as pat false (fun H => iDestructHyp H as ( x1 x2 x3 x4 ) pat). +Tactic Notation "iPoseProof" open_constr(lem) "as" "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) + simple_intropattern(x5) ")" constr(pat) := + iPoseProofCore lem as pat false (fun H => iDestructHyp H as ( x1 x2 x3 x4 x5 ) pat). +Tactic Notation "iPoseProof" open_constr(lem) "as" "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) + simple_intropattern(x5) simple_intropattern(x6) ")" constr(pat) := + iPoseProofCore lem as pat false (fun H => iDestructHyp H as ( x1 x2 x3 x4 x5 x6 ) pat). +Tactic Notation "iPoseProof" open_constr(lem) "as" "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) + simple_intropattern(x5) simple_intropattern(x6) simple_intropattern(x7) ")" + constr(pat) := + iPoseProofCore lem as pat false (fun H => iDestructHyp H as ( x1 x2 x3 x4 x5 x6 x7 ) pat). +Tactic Notation "iPoseProof" open_constr(lem) "as" "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) + simple_intropattern(x5) simple_intropattern(x6) simple_intropattern(x7) + simple_intropattern(x8) ")" constr(pat) := + iPoseProofCore lem as pat false (fun H => iDestructHyp H as ( x1 x2 x3 x4 x5 x6 x7 x8 ) pat). + +(** * Induction *) +(* An invocation of [iInduction (x) as pat IH forall (x1...xn) Hs] will +result in the following actions: + +- Revert the proofmode hypotheses [Hs] +- Revert all remaining spatial hypotheses and the remaining persistent + hypotheses containing the induction variable [x] +- Revert the pure hypotheses [x1..xn] + +- Actuall perform induction + +- Introduce thee pure hypotheses [x1..xn] +- Introduce the spatial hypotheses and persistent hypotheses involving [x] +- Introduce the proofmode hypotheses [Hs] +*) +Tactic Notation "iInductionCore" constr(x) "as" simple_intropattern(pat) constr(IH) := + let rec fix_ihs rev_tac := + lazymatch goal with + | H : context [envs_entails _ _] |- _ => + eapply (tac_revert_ih _ _ _ H _); + [env_reflexivity + || fail "iInduction: spatial context not empty, this should not happen" + |clear H]; + fix_ihs ltac:(fun j => + let IH' := eval vm_compute in + match j with 0%N => IH | _ => IH +:+ pretty j end in + iIntros [IAlwaysElim (IIdent IH')]; + let j := eval vm_compute in (1 + j)%N in + rev_tac j) + | _ => rev_tac 0%N + end in + induction x as pat; fix_ihs ltac:(fun _ => idtac). + +Ltac iHypsContaining x := + let rec go Γ x Hs := + lazymatch Γ with + | Enil => constr:(Hs) + | Esnoc ?Γ ?H ?Q => + match Q with + | context [x] => go Γ x (H :: Hs) + | _ => go Γ x Hs + end + end in + let Γp := lazymatch goal with |- envs_entails (Envs ?Γp _ _) _ => Γp end in + let Γs := lazymatch goal with |- envs_entails (Envs _ ?Γs _) _ => Γs end in + let Hs := go Γp x (@nil ident) in go Γs x Hs. + +Tactic Notation "iInductionRevert" constr(x) constr(Hs) "with" tactic(tac) := + iRevertIntros Hs with ( + iRevertIntros "∗" with ( + idtac; + let Hsx := iHypsContaining x in + iRevertIntros Hsx with tac + ) + ). + +Tactic Notation "iInduction" constr(x) "as" simple_intropattern(pat) constr(IH) := + iInductionRevert x "" with (iInductionCore x as pat IH). +Tactic Notation "iInduction" constr(x) "as" simple_intropattern(pat) constr(IH) + "forall" "(" ident(x1) ")" := + iInductionRevert x "" with (iRevertIntros(x1) "" with (iInductionCore x as pat IH)). +Tactic Notation "iInduction" constr(x) "as" simple_intropattern(pat) constr(IH) + "forall" "(" ident(x1) ident(x2) ")" := + iInductionRevert x "" with (iRevertIntros(x1 x2) "" with (iInductionCore x as pat IH)). +Tactic Notation "iInduction" constr(x) "as" simple_intropattern(pat) constr(IH) + "forall" "(" ident(x1) ident(x2) ident(x3) ")" := + iInductionRevert x "" with (iRevertIntros(x1 x2 x3) "" with (iInductionCore x as pat IH)). +Tactic Notation "iInduction" constr(x) "as" simple_intropattern(pat) constr(IH) + "forall" "(" ident(x1) ident(x2) ident(x3) ident(x4) ")" := + iInductionRevert x "" with (iRevertIntros(x1 x2 x3 x4) "" with (iInductionCore x as pat IH)). +Tactic Notation "iInduction" constr(x) "as" simple_intropattern(pat) constr(IH) + "forall" "(" ident(x1) ident(x2) ident(x3) ident(x4) ident(x5) ")" := + iInductionRevert x "" with (iRevertIntros(x1 x2 x3 x4 x5) "" with (iInductionCore x as pat IH)). +Tactic Notation "iInduction" constr(x) "as" simple_intropattern(pat) constr(IH) + "forall" "(" ident(x1) ident(x2) ident(x3) ident(x4) ident(x5) ident(x6) ")" := + iInductionRevert x "" with (iRevertIntros(x1 x2 x3 x4 x5 x6) "" with (iInductionCore x as pat IH)). +Tactic Notation "iInduction" constr(x) "as" simple_intropattern(pat) constr(IH) + "forall" "(" ident(x1) ident(x2) ident(x3) ident(x4) ident(x5) ident(x6) + ident(x7) ")" := + iInductionRevert x "" with (iRevertIntros(x1 x2 x3 x4 x5 x6 x7) "" with (iInductionCore x as pat IH)). +Tactic Notation "iInduction" constr(x) "as" simple_intropattern(pat) constr(IH) + "forall" "(" ident(x1) ident(x2) ident(x3) ident(x4) ident(x5) ident(x6) + ident(x7) ident(x8) ")" := + iInductionRevert x "" with (iRevertIntros(x1 x2 x3 x4 x5 x6 x7 x8) "" with (iInductionCore x as pat IH)). + +Tactic Notation "iInduction" constr(x) "as" simple_intropattern(pat) constr(IH) + "forall" constr(Hs) := + iInductionRevert x Hs with (iInductionCore x as pat IH). +Tactic Notation "iInduction" constr(x) "as" simple_intropattern(pat) constr(IH) + "forall" "(" ident(x1) ")" constr(Hs) := + iInductionRevert x Hs with (iRevertIntros(x1) "" with (iInductionCore x as pat IH)). +Tactic Notation "iInduction" constr(x) "as" simple_intropattern(pat) constr(IH) + "forall" "(" ident(x1) ident(x2) ")" constr(Hs) := + iInductionRevert x Hs with (iRevertIntros(x1 x2) "" with (iInductionCore x as pat IH)). +Tactic Notation "iInduction" constr(x) "as" simple_intropattern(pat) constr(IH) + "forall" "(" ident(x1) ident(x2) ident(x3) ")" constr(Hs) := + iInductionRevert x Hs with (iRevertIntros(x1 x2 x3) "" with (iInductionCore x as pat IH)). +Tactic Notation "iInduction" constr(x) "as" simple_intropattern(pat) constr(IH) + "forall" "(" ident(x1) ident(x2) ident(x3) ident(x4) ")" constr(Hs) := + iInductionRevert x Hs with (iRevertIntros(x1 x2 x3 x4) "" with (iInductionCore x as pat IH)). +Tactic Notation "iInduction" constr(x) "as" simple_intropattern(pat) constr(IH) + "forall" "(" ident(x1) ident(x2) ident(x3) ident(x4) ident(x5) ")" + constr(Hs) := + iInductionRevert x Hs with (iRevertIntros(x1 x2 x3 x4 x5) "" with (iInductionCore x as pat IH)). +Tactic Notation "iInduction" constr(x) "as" simple_intropattern(pat) constr(IH) + "forall" "(" ident(x1) ident(x2) ident(x3) ident(x4) ident(x5) ident(x6) ")" + constr(Hs) := + iInductionRevert x Hs with (iRevertIntros(x1 x2 x3 x4 x5 x6) "" with (iInductionCore x as pat IH)). +Tactic Notation "iInduction" constr(x) "as" simple_intropattern(pat) constr(IH) + "forall" "(" ident(x1) ident(x2) ident(x3) ident(x4) ident(x5) ident(x6) + ident(x7) ")" constr(Hs) := + iInductionRevert x Hs with (iRevertIntros(x1 x2 x3 x4 x5 x6 x7) "" with (iInductionCore x as pat IH)). +Tactic Notation "iInduction" constr(x) "as" simple_intropattern(pat) constr(IH) + "forall" "(" ident(x1) ident(x2) ident(x3) ident(x4) ident(x5) ident(x6) + ident(x7) ident(x8) ")" constr(Hs) := + iInductionRevert x Hs with (iRevertIntros(x1 x2 x3 x4 x5 x6 x7 x8) "" with (iInductionCore x as pat IH)). + +(** * Löb Induction *) +Tactic Notation "iLöbCore" "as" constr (IH) := + iStartProof; + (* apply is sometimes confused wrt. canonical structures search. + refine should use the other unification algorithm, which should + not have this issue. *) + notypeclasses refine (tac_löb _ _ IH _ _ _ _); + [reflexivity || fail "iLöb: spatial context not empty, this should not happen" + |env_reflexivity || fail "iLöb:" IH "not fresh"|]. + +Tactic Notation "iLöbRevert" constr(Hs) "with" tactic(tac) := + iRevertIntros Hs with ( + iRevertIntros "∗" with tac + ). + +Tactic Notation "iLöb" "as" constr (IH) := + iLöbRevert "" with (iLöbCore as IH). +Tactic Notation "iLöb" "as" constr (IH) "forall" "(" ident(x1) ")" := + iLöbRevert "" with (iRevertIntros(x1) "" with (iLöbCore as IH)). +Tactic Notation "iLöb" "as" constr (IH) "forall" "(" ident(x1) ident(x2) ")" := + iLöbRevert "" with (iRevertIntros(x1 x2) "" with (iLöbCore as IH)). +Tactic Notation "iLöb" "as" constr (IH) "forall" "(" ident(x1) ident(x2) + ident(x3) ")" := + iLöbRevert "" with (iRevertIntros(x1 x2 x3) "" with (iLöbCore as IH)). +Tactic Notation "iLöb" "as" constr (IH) "forall" "(" ident(x1) ident(x2) + ident(x3) ident(x4) ")" := + iLöbRevert "" with (iRevertIntros(x1 x2 x3 x4) "" with (iLöbCore as IH)). +Tactic Notation "iLöb" "as" constr (IH) "forall" "(" ident(x1) ident(x2) + ident(x3) ident(x4) ident(x5) ")" := + iLöbRevert "" with (iRevertIntros(x1 x2 x3 x4 x5) "" with (iLöbCore as IH)). +Tactic Notation "iLöb" "as" constr (IH) "forall" "(" ident(x1) ident(x2) + ident(x3) ident(x4) ident(x5) ident(x6) ")" := + iLöbRevert "" with (iRevertIntros(x1 x2 x3 x4 x5 x6) "" with (iLöbCore as IH)). +Tactic Notation "iLöb" "as" constr (IH) "forall" "(" ident(x1) ident(x2) + ident(x3) ident(x4) ident(x5) ident(x6) ident(x7) ")" := + iLöbRevert "" with (iRevertIntros(x1 x2 x3 x4 x5 x6 x7) "" with (iLöbCore as IH)). +Tactic Notation "iLöb" "as" constr (IH) "forall" "(" ident(x1) ident(x2) + ident(x3) ident(x4) ident(x5) ident(x6) ident(x7) ident(x8) ")" := + iLöbRevert "" with (iRevertIntros(x1 x2 x3 x4 x5 x6 x7 x8) "" with (iLöbCore as IH)). + +Tactic Notation "iLöb" "as" constr (IH) "forall" constr(Hs) := + iLöbRevert Hs with (iLöbCore as IH). +Tactic Notation "iLöb" "as" constr (IH) "forall" "(" ident(x1) ")" constr(Hs) := + iLöbRevert Hs with (iRevertIntros(x1) "" with (iLöbCore as IH)). +Tactic Notation "iLöb" "as" constr (IH) "forall" "(" ident(x1) ident(x2) ")" + constr(Hs) := + iLöbRevert Hs with (iRevertIntros(x1 x2) "" with (iLöbCore as IH)). +Tactic Notation "iLöb" "as" constr (IH) "forall" "(" ident(x1) ident(x2) + ident(x3) ")" constr(Hs) := + iLöbRevert Hs with (iRevertIntros(x1 x2 x3) "" with (iLöbCore as IH)). +Tactic Notation "iLöb" "as" constr (IH) "forall" "(" ident(x1) ident(x2) + ident(x3) ident(x4) ")" constr(Hs) := + iLöbRevert Hs with (iRevertIntros(x1 x2 x3 x4) "" with (iLöbCore as IH)). +Tactic Notation "iLöb" "as" constr (IH) "forall" "(" ident(x1) ident(x2) + ident(x3) ident(x4) ident(x5) ")" constr(Hs) := + iLöbRevert Hs with (iRevertIntros(x1 x2 x3 x4 x5) "" with (iLöbCore as IH)). +Tactic Notation "iLöb" "as" constr (IH) "forall" "(" ident(x1) ident(x2) + ident(x3) ident(x4) ident(x5) ident(x6) ")" constr(Hs) := + iLöbRevert Hs with (iRevertIntros(x1 x2 x3 x4 x5 x6) "" with (iLöbCore as IH)). +Tactic Notation "iLöb" "as" constr (IH) "forall" "(" ident(x1) ident(x2) + ident(x3) ident(x4) ident(x5) ident(x6) ident(x7) ")" constr(Hs) := + iLöbRevert Hs with (iRevertIntros(x1 x2 x3 x4 x5 x6 x7) "" with (iLöbCore as IH)). +Tactic Notation "iLöb" "as" constr (IH) "forall" "(" ident(x1) ident(x2) + ident(x3) ident(x4) ident(x5) ident(x6) ident(x7) ident(x8) ")" constr(Hs) := + iLöbRevert Hs with (iRevertIntros(x1 x2 x3 x4 x5 x6 x7 x8) "" with (iLöbCore as IH)). + +(** * Assert *) +(* The argument [p] denotes whether [Q] is persistent. It can either be a +Boolean or an introduction pattern, which will be coerced into [true] if it +only contains `#` or `%` patterns at the top-level, and [false] otherwise. *) +Tactic Notation "iAssertCore" open_constr(Q) + "with" constr(Hs) "as" constr(p) tactic(tac) := + iStartProof; + let pats := spec_pat.parse Hs in + lazymatch pats with + | [_] => idtac + | _ => fail "iAssert: exactly one specialization pattern should be given" + end; + let H := iFresh in + eapply tac_assert with _ H Q; + [env_reflexivity + |iSpecializeCore H with hnil pats as p; [..|tac H]]. + +Tactic Notation "iAssertCore" open_constr(Q) "as" constr(p) tactic(tac) := + let p := intro_pat_persistent p in + lazymatch p with + | true => iAssertCore Q with "[#]" as p tac + | false => iAssertCore Q with "[]" as p tac + end. + +Tactic Notation "iAssert" open_constr(Q) "with" constr(Hs) "as" constr(pat) := + iAssertCore Q with Hs as pat (fun H => iDestructHyp H as pat). +Tactic Notation "iAssert" open_constr(Q) "with" constr(Hs) "as" + "(" simple_intropattern(x1) ")" constr(pat) := + iAssertCore Q with Hs as pat (fun H => iDestructHyp H as (x1) pat). +Tactic Notation "iAssert" open_constr(Q) "with" constr(Hs) "as" + "(" simple_intropattern(x1) simple_intropattern(x2) ")" constr(pat) := + iAssertCore Q with Hs as pat (fun H => iDestructHyp H as (x1 x2) pat). +Tactic Notation "iAssert" open_constr(Q) "with" constr(Hs) "as" + "(" simple_intropattern(x1) simple_intropattern(x2) simple_intropattern(x3) + ")" constr(pat) := + iAssertCore Q with Hs as pat (fun H => iDestructHyp H as (x1 x2 x3) pat). +Tactic Notation "iAssert" open_constr(Q) "with" constr(Hs) "as" + "(" simple_intropattern(x1) simple_intropattern(x2) simple_intropattern(x3) + simple_intropattern(x4) ")" constr(pat) := + iAssertCore Q with Hs as pat (fun H => iDestructHyp H as (x1 x2 x3 x4) pat). + +Tactic Notation "iAssert" open_constr(Q) "as" constr(pat) := + iAssertCore Q as pat (fun H => iDestructHyp H as pat). +Tactic Notation "iAssert" open_constr(Q) "as" + "(" simple_intropattern(x1) ")" constr(pat) := + iAssertCore Q as pat (fun H => iDestructHyp H as (x1) pat). +Tactic Notation "iAssert" open_constr(Q) "as" + "(" simple_intropattern(x1) simple_intropattern(x2) ")" constr(pat) := + iAssertCore Q as pat (fun H => iDestructHyp H as (x1 x2) pat). +Tactic Notation "iAssert" open_constr(Q) "as" + "(" simple_intropattern(x1) simple_intropattern(x2) simple_intropattern(x3) + ")" constr(pat) := + iAssertCore Q as pat (fun H => iDestructHyp H as (x1 x2 x3) pat). +Tactic Notation "iAssert" open_constr(Q) "as" + "(" simple_intropattern(x1) simple_intropattern(x2) simple_intropattern(x3) + simple_intropattern(x4) ")" constr(pat) := + iAssertCore Q as pat (fun H => iDestructHyp H as (x1 x2 x3 x4) pat). + +Tactic Notation "iAssert" open_constr(Q) "with" constr(Hs) + "as" "%" simple_intropattern(pat) := + iAssertCore Q with Hs as true (fun H => iPure H as pat). +Tactic Notation "iAssert" open_constr(Q) "as" "%" simple_intropattern(pat) := + iAssert Q with "[-]" as %pat. + +(** * Rewrite *) +Local Ltac iRewriteFindPred := + match goal with + | |- _ ⊣⊢ ?Φ ?x => + generalize x; + match goal with |- (∀ y, @?Ψ y ⊣⊢ _) => unify Φ Ψ; reflexivity end + end. + +Local Tactic Notation "iRewriteCore" constr(lr) open_constr(lem) := + iPoseProofCore lem as true true (fun Heq => + eapply (tac_rewrite _ Heq _ _ lr); + [env_reflexivity || fail "iRewrite:" Heq "not found" + |iSolveTC || + let P := match goal with |- IntoInternalEq ?P _ _ ⊢ _ => P end in + fail "iRewrite:" P "not an equality" + |iRewriteFindPred + |intros ??? ->; reflexivity|lazy beta; iClearHyp Heq]). + +Tactic Notation "iRewrite" open_constr(lem) := iRewriteCore Right lem. +Tactic Notation "iRewrite" "-" open_constr(lem) := iRewriteCore Left lem. + +Local Tactic Notation "iRewriteCore" constr(lr) open_constr(lem) "in" constr(H) := + iPoseProofCore lem as true true (fun Heq => + eapply (tac_rewrite_in _ Heq _ _ H _ _ lr); + [env_reflexivity || fail "iRewrite:" Heq "not found" + |env_reflexivity || fail "iRewrite:" H "not found" + |iSolveTC || + let P := match goal with |- IntoInternalEq ?P _ _ ⊢ _ => P end in + fail "iRewrite:" P "not an equality" + |iRewriteFindPred + |intros ??? ->; reflexivity + |env_reflexivity|lazy beta; iClearHyp Heq]). + +Tactic Notation "iRewrite" open_constr(lem) "in" constr(H) := + iRewriteCore Right lem in H. +Tactic Notation "iRewrite" "-" open_constr(lem) "in" constr(H) := + iRewriteCore Left lem in H. + +Ltac iSimplifyEq := repeat ( + iMatchHyp (fun H P => match P with ⌜_ = _âŒ%I => iDestruct H as %? end) + || simplify_eq/=). + +(** * Update modality *) +Tactic Notation "iMod" open_constr(lem) := + iDestructCore lem as false (fun H => iModCore H). +Tactic Notation "iMod" open_constr(lem) "as" constr(pat) := + iDestructCore lem as false (fun H => iModCore H; last iDestructHyp H as pat). +Tactic Notation "iMod" open_constr(lem) "as" "(" simple_intropattern(x1) ")" + constr(pat) := + iDestructCore lem as false (fun H => iModCore H; last iDestructHyp H as ( x1 ) pat). +Tactic Notation "iMod" open_constr(lem) "as" "(" simple_intropattern(x1) + simple_intropattern(x2) ")" constr(pat) := + iDestructCore lem as false (fun H => iModCore H; last iDestructHyp H as ( x1 x2 ) pat). +Tactic Notation "iMod" open_constr(lem) "as" "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) ")" constr(pat) := + iDestructCore lem as false (fun H => iModCore H; last iDestructHyp H as ( x1 x2 x3 ) pat). +Tactic Notation "iMod" open_constr(lem) "as" "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) ")" + constr(pat) := + iDestructCore lem as false (fun H => + iModCore H; last iDestructHyp H as ( x1 x2 x3 x4 ) pat). +Tactic Notation "iMod" open_constr(lem) "as" "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) + simple_intropattern(x5) ")" constr(pat) := + iDestructCore lem as false (fun H => + iModCore H; last iDestructHyp H as ( x1 x2 x3 x4 x5 ) pat). +Tactic Notation "iMod" open_constr(lem) "as" "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) + simple_intropattern(x5) simple_intropattern(x6) ")" constr(pat) := + iDestructCore lem as false (fun H => + iModCore H; last iDestructHyp H as ( x1 x2 x3 x4 x5 x6 ) pat). +Tactic Notation "iMod" open_constr(lem) "as" "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) + simple_intropattern(x5) simple_intropattern(x6) simple_intropattern(x7) ")" + constr(pat) := + iDestructCore lem as false (fun H => + iModCore H; last iDestructHyp H as ( x1 x2 x3 x4 x5 x6 x7 ) pat). +Tactic Notation "iMod" open_constr(lem) "as" "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) + simple_intropattern(x5) simple_intropattern(x6) simple_intropattern(x7) + simple_intropattern(x8) ")" constr(pat) := + iDestructCore lem as false (fun H => + iModCore H; last iDestructHyp H as ( x1 x2 x3 x4 x5 x6 x7 x8 ) pat). + +Tactic Notation "iMod" open_constr(lem) "as" "%" simple_intropattern(pat) := + iDestructCore lem as false (fun H => iModCore H; iPure H as pat). + +(** * Invariant *) + +(* Finds a hypothesis in the context that is an invariant with + namespace [N]. To do so, we check whether for each hypothesis + ["H":P] we can find an instance of [IntoInv P N] *) +Tactic Notation "iAssumptionInv" constr(N) := + let rec find Γ i := + lazymatch Γ with + | Esnoc ?Γ ?j ?P' => + first [let H := constr:(_: IntoInv P' N) in unify i j|find Γ i] + end in + lazymatch goal with + | |- envs_lookup_delete _ ?i (Envs ?Γp ?Γs _) = Some _ => + first [find Γp i|find Γs i]; env_reflexivity + end. + +(* The argument [select] is the namespace [N] or hypothesis name ["H"] of the +invariant. *) +Tactic Notation "iInvCore" constr(select) "with" constr(pats) "as" tactic(tac) constr(Hclose) := + iStartProof; + let pats := spec_pat.parse pats in + lazymatch pats with + | [_] => idtac + | _ => fail "iInv: exactly one specialization pattern should be given" + end; + let H := iFresh in + lazymatch type of select with + | string => + eapply tac_inv_elim with _ select H _ _ _ _ _ _ _; + first by (iAssumptionCore || fail "iInv: invariant" select "not found") + | ident => + eapply tac_inv_elim with _ select H _ _ _ _ _ _ _; + first by (iAssumptionCore || fail "iInv: invariant" select "not found") + | namespace => + eapply tac_inv_elim with _ _ H _ _ _ _ _ _ _; + first by (iAssumptionInv select || fail "iInv: invariant" select "not found") + | _ => fail "iInv: selector" select "is not of the right type " + end; + [iSolveTC || + let I := match goal with |- ElimInv _ ?I _ _ _ _ _ => I end in + fail "iInv: cannot eliminate invariant " I + |try (split_and?; solve [ fast_done | solve_ndisj ]) + |let R := fresh in intros R; eexists; split; [env_reflexivity|]; + iSpecializePat H pats; last ( + iApplyHyp H; clear R; + iIntros H; (* H was spatial, so it's gone due to the apply and we can reuse the name *) + iIntros Hclose; + tac H + )]. + +Tactic Notation "iInvCore" constr(N) "as" tactic(tac) constr(Hclose) := + iInvCore N with "[$]" as ltac:(tac) Hclose. + +Tactic Notation "iInv" constr(N) "as" constr(pat) constr(Hclose) := + iInvCore N as (fun H => iDestructHyp H as pat) Hclose. +Tactic Notation "iInv" constr(N) "as" "(" simple_intropattern(x1) ")" + constr(pat) constr(Hclose) := + iInvCore N as (fun H => iDestructHyp H as (x1) pat) Hclose. +Tactic Notation "iInv" constr(N) "as" "(" simple_intropattern(x1) + simple_intropattern(x2) ")" constr(pat) constr(Hclose) := + iInvCore N as (fun H => iDestructHyp H as (x1 x2) pat) Hclose. +Tactic Notation "iInv" constr(N) "as" "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) ")" + constr(pat) constr(Hclose) := + iInvCore N as (fun H => iDestructHyp H as (x1 x2 x3) pat) Hclose. +Tactic Notation "iInv" constr(N) "as" "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) ")" + constr(pat) constr(Hclose) := + iInvCore N as (fun H => iDestructHyp H as (x1 x2 x3 x4) pat) Hclose. +Tactic Notation "iInv" constr(N) "with" constr(Hs) "as" constr(pat) constr(Hclose) := + iInvCore N with Hs as (fun H => iDestructHyp H as pat) Hclose. +Tactic Notation "iInv" constr(N) "with" constr(Hs) "as" "(" simple_intropattern(x1) ")" + constr(pat) constr(Hclose) := + iInvCore N with Hs as (fun H => iDestructHyp H as (x1) pat) Hclose. +Tactic Notation "iInv" constr(N) "with" constr(Hs) "as" "(" simple_intropattern(x1) + simple_intropattern(x2) ")" constr(pat) constr(Hclose) := + iInvCore N with Hs as (fun H => iDestructHyp H as (x1 x2) pat) Hclose. +Tactic Notation "iInv" constr(N) "with" constr(Hs) "as" "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) ")" + constr(pat) constr(Hclose) := + iInvCore N with Hs as (fun H => iDestructHyp H as (x1 x2 x3) pat) Hclose. +Tactic Notation "iInv" constr(N) "with" constr(Hs) "as" "(" simple_intropattern(x1) + simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) ")" + constr(pat) constr(Hclose) := + iInvCore N with Hs as (fun H => iDestructHyp H as (x1 x2 x3 x4) pat) Hclose. + +Tactic Notation "iAccu" := + iStartProof; eapply tac_accu; [env_reflexivity || fail "iAccu: not an evar"]. + +Hint Extern 0 (_ ⊢ _) => iStartProof. + +(* Make sure that by and done solve trivial things in proof mode *) +Hint Extern 0 (envs_entails _ _) => iPureIntro; try done. +Hint Extern 0 (envs_entails _ ?Q) => + first [is_evar Q; fail 1|iAssumption]. +Hint Extern 0 (envs_entails _ emp) => iEmpIntro. + +(* TODO: look for a more principled way of adding trivial hints like those +below; see the discussion in !75 for further details. *) +Hint Extern 0 (envs_entails _ (_ ≡ _)) => + rewrite envs_entails_eq; apply bi.internal_eq_refl. +Hint Extern 0 (envs_entails _ (big_opL _ _ _)) => + rewrite envs_entails_eq; apply bi.big_sepL_nil'. +Hint Extern 0 (envs_entails _ (big_opM _ _ _)) => + rewrite envs_entails_eq; apply bi.big_sepM_empty'. +Hint Extern 0 (envs_entails _ (big_opS _ _ _)) => + rewrite envs_entails_eq; apply bi.big_sepS_empty'. +Hint Extern 0 (envs_entails _ (big_opMS _ _ _)) => + rewrite envs_entails_eq; apply bi.big_sepMS_empty'. + +Hint Extern 0 (envs_entails _ (∀ _, _)) => iIntros. +Hint Extern 0 (envs_entails _ (_ → _)) => iIntros. +Hint Extern 0 (envs_entails _ (_ -∗ _)) => iIntros. + +Hint Extern 1 (envs_entails _ (_ ∧ _)) => iSplit. +Hint Extern 1 (envs_entails _ (_ ∗ _)) => iSplit. +Hint Extern 1 (envs_entails _ (â–· _)) => iNext. +Hint Extern 1 (envs_entails _ (â– _)) => iAlways. +Hint Extern 1 (envs_entails _ (<pers> _)) => iAlways. +Hint Extern 1 (envs_entails _ (<affine> _)) => iAlways. +Hint Extern 1 (envs_entails _ (â–¡ _)) => iAlways. +Hint Extern 1 (envs_entails _ (∃ _, _)) => iExists _. +Hint Extern 1 (envs_entails _ (â—‡ _)) => iModIntro. +Hint Extern 1 (envs_entails _ (_ ∨ _)) => iLeft. +Hint Extern 1 (envs_entails _ (_ ∨ _)) => iRight. +Hint Extern 1 (envs_entails _ (|==> _)) => iModIntro. +Hint Extern 1 (envs_entails _ (<absorb> _)) => iModIntro. +Hint Extern 2 (envs_entails _ (|={_}=> _)) => iModIntro. + +Hint Extern 2 (envs_entails _ (_ ∗ _)) => progress iFrame : iFrame. diff --git a/theories/proofmode/tactics.v b/theories/proofmode/tactics.v index b9592351dbe077d9eb7ab2887ad1cde6e56ce92b..dad2afd87f00f1b0bfdf3d80894886fd76e93676 100644 --- a/theories/proofmode/tactics.v +++ b/theories/proofmode/tactics.v @@ -1,2003 +1,2 @@ -From iris.proofmode Require Import coq_tactics. -From iris.proofmode Require Import base intro_patterns spec_patterns sel_patterns. -From iris.bi Require Export bi. -From stdpp Require Import namespaces. -From iris.proofmode Require Export classes notation. -From iris.proofmode Require Import modality_instances frame_instances - class_instances_bi class_instances_sbi. -From stdpp Require Import hlist pretty. -Set Default Proof Using "Type". -Export ident. - -Declare Reduction env_cbv := cbv [ - option_bind - beq ascii_beq string_beq positive_beq Pos.succ ident_beq - env_lookup env_lookup_delete env_delete env_app env_replace env_dom - env_intuitionistic env_spatial env_counter env_spatial_is_nil envs_dom - envs_lookup envs_lookup_delete envs_delete envs_snoc envs_app - envs_simple_replace envs_replace envs_split - envs_clear_spatial envs_clear_persistent envs_incr_counter - envs_split_go envs_split prop_of_env]. -Ltac env_cbv := - match goal with |- ?u => let v := eval env_cbv in u in change v end. -Ltac env_reflexivity := env_cbv; exact eq_refl. - -(** For most of the tactics, we want to have tight control over the order and -way in which type class inference is performed. To that end, many tactics make -use of [notypeclasses refine] and the [iSolveTC] tactic to manually invoke type -class inference. - -The tactic [iSolveTC] does not use [apply _], as that often leads to issues -because it will try to solve all evars whose type is a typeclass, in -dependency order (according to Matthieu). If one fails, it aborts. However, we -generally rely on progress on the main goal to be solved to make progress -elsewhere. With [typeclasses eauto], that seems to work better. - -A drawback of [typeclasses eauto] is that it is multi-success, i.e. whenever -subsequent tactics fail, it will backtrack to [typeclasses eauto] to try the -next type class instance. This is almost always undesired and leads to poor -performance and horrible error messages, so we wrap it in a [once]. *) -Ltac iSolveTC := - solve [once (typeclasses eauto)]. - -(** * Misc *) - -Ltac iMissingHyps Hs := - let Δ := - lazymatch goal with - | |- envs_entails ?Δ _ => Δ - | |- context[ envs_split _ _ ?Δ ] => Δ - end in - let Hhyps := eval env_cbv in (envs_dom Δ) in - eval vm_compute in (list_difference Hs Hhyps). - -Ltac iTypeOf H := - let Δ := match goal with |- envs_entails ?Δ _ => Δ end in - eval env_cbv in (envs_lookup H Δ). - -Tactic Notation "iMatchHyp" tactic1(tac) := - match goal with - | |- context[ environments.Esnoc _ ?x ?P ] => tac x P - end. - -(** * Start a proof *) -Tactic Notation "iStartProof" := - lazymatch goal with - | |- envs_entails _ _ => idtac - | |- ?φ => notypeclasses refine (as_emp_valid_2 φ _ _); - [apply _ || fail "iStartProof: not a Bi entailment" - |apply tac_adequate] - end. - -(* Same as above, with 2 differences : - - We can specify a BI in which we want the proof to be done - - If the goal starts with a let or a ∀, they are automatically - introduced. *) -Tactic Notation "iStartProof" uconstr(PROP) := - lazymatch goal with - | |- @envs_entails ?PROP' _ _ => - (* This cannot be shared with the other [iStartProof], because - type_term has a non-negligeable performance impact. *) - let x := type_term (eq_refl : @eq Type PROP PROP') in idtac - - (* We eta-expand [as_emp_valid_2], in order to make sure that - [iStartProof PROP] works even if [PROP] is the carrier type. In - this case, typing this expression will end up unifying PROP with - [bi_car _], and hence trigger the canonical structures mechanism - to find the corresponding bi. *) - | |- ?φ => notypeclasses refine ((λ P : PROP, @as_emp_valid_2 φ _ P) _ _ _); - [apply _ || fail "iStartProof: not a Bi entailment" - |apply tac_adequate] - end. - -(** * Generate a fresh identifier *) -(* Tactic Notation tactics cannot return terms *) -Ltac iFresh := - (* We need to increment the environment counter using [tac_fresh]. - But because [iFresh] returns a value, we have to let bind - [tac_fresh] wrapped under a match to force evaluation of this - side-effect. See https://stackoverflow.com/a/46178884 *) - let do_incr := - lazymatch goal with - | _ => iStartProof; eapply tac_fresh; first by (env_reflexivity) - end in - lazymatch goal with - |- envs_entails ?Δ _ => - let n := eval env_cbv in (env_counter Δ) in - constr:(IAnon n) - end. - -(** * Simplification *) -Tactic Notation "iEval" tactic(t) := - iStartProof; - eapply tac_eval; - [let x := fresh in intros x; t; unfold x; reflexivity - |]. - -Tactic Notation "iEval" tactic(t) "in" constr(H) := - iStartProof; - eapply tac_eval_in with _ H _ _ _; - [env_reflexivity || fail "iEval:" H "not found" - |let x := fresh in intros x; t; unfold x; reflexivity - |env_reflexivity - |]. - -Tactic Notation "iSimpl" := iEval simpl. -Tactic Notation "iSimpl" "in" constr(H) := iEval simpl in H. - -(* It would be nice to also have an `iSsrRewrite`, however, for this we need to -pass arguments to Ssreflect's `rewrite` like `/= foo /bar` in Ltac, see: - - https://sympa.inria.fr/sympa/arc/coq-club/2018-01/msg00000.html - -PMP told me (= Robbert) in person that this is not possible today, but may be -possible in Ltac2. *) - -(** * Context manipulation *) -Tactic Notation "iRename" constr(H1) "into" constr(H2) := - eapply tac_rename with _ H1 H2 _ _; (* (i:=H1) (j:=H2) *) - [env_reflexivity || fail "iRename:" H1 "not found" - |env_reflexivity || fail "iRename:" H2 "not fresh"|]. - -Local Inductive esel_pat := - | ESelPure - | ESelIdent : bool → ident → esel_pat. - -Ltac iElaborateSelPat pat := - let rec go pat Δ Hs := - lazymatch pat with - | [] => eval cbv in Hs - | SelPure :: ?pat => go pat Δ (ESelPure :: Hs) - | SelPersistent :: ?pat => - let Hs' := eval env_cbv in (env_dom (env_intuitionistic Δ)) in - let Δ' := eval env_cbv in (envs_clear_persistent Δ) in - go pat Δ' ((ESelIdent true <$> Hs') ++ Hs) - | SelSpatial :: ?pat => - let Hs' := eval env_cbv in (env_dom (env_spatial Δ)) in - let Δ' := eval env_cbv in (envs_clear_spatial Δ) in - go pat Δ' ((ESelIdent false <$> Hs') ++ Hs) - | SelIdent ?H :: ?pat => - lazymatch eval env_cbv in (envs_lookup_delete false H Δ) with - | Some (?p,_,?Δ') => go pat Δ' (ESelIdent p H :: Hs) - | None => fail "iElaborateSelPat:" H "not found" - end - end in - lazymatch goal with - | |- envs_entails ?Δ _ => - let pat := sel_pat.parse pat in go pat Δ (@nil esel_pat) - end. - -Local Ltac iClearHyp H := - eapply tac_clear with _ H _ _; (* (i:=H) *) - [env_reflexivity || fail "iClear:" H "not found" - |env_cbv; apply _ || - let P := match goal with |- TCOr (Affine ?P) _ => P end in - fail "iClear:" H ":" P "not affine and the goal not absorbing" - |]. - -Tactic Notation "iClear" constr(Hs) := - let rec go Hs := - lazymatch Hs with - | [] => idtac - | ESelPure :: ?Hs => clear; go Hs - | ESelIdent _ ?H :: ?Hs => iClearHyp H; go Hs - end in - let Hs := iElaborateSelPat Hs in iStartProof; go Hs. - -Tactic Notation "iClear" "(" ident_list(xs) ")" constr(Hs) := - iClear Hs; clear xs. - -(** * Assumptions *) -Tactic Notation "iExact" constr(H) := - eapply tac_assumption with _ H _ _; (* (i:=H) *) - [env_reflexivity || fail "iExact:" H "not found" - |apply _ || - let P := match goal with |- FromAssumption _ ?P _ => P end in - fail "iExact:" H ":" P "does not match goal" - |env_cbv; apply _ || - fail "iExact:" H "not absorbing and the remaining hypotheses not affine"]. - -Tactic Notation "iAssumptionCore" := - let rec find Γ i P := - lazymatch Γ with - | Esnoc ?Γ ?j ?Q => first [unify P Q; unify i j|find Γ i P] - end in - match goal with - | |- envs_lookup ?i (Envs ?Γp ?Γs _) = Some (_, ?P) => - first [is_evar i; fail 1 | env_reflexivity] - | |- envs_lookup ?i (Envs ?Γp ?Γs _) = Some (_, ?P) => - is_evar i; first [find Γp i P | find Γs i P]; env_reflexivity - | |- envs_lookup_delete _ ?i (Envs ?Γp ?Γs _) = Some (_, ?P, _) => - first [is_evar i; fail 1 | env_reflexivity] - | |- envs_lookup_delete _ ?i (Envs ?Γp ?Γs _) = Some (_, ?P, _) => - is_evar i; first [find Γp i P | find Γs i P]; env_reflexivity - end. - -Tactic Notation "iAssumption" := - let Hass := fresh in - let rec find p Γ Q := - lazymatch Γ with - | Esnoc ?Γ ?j ?P => first - [pose proof (_ : FromAssumption p P Q) as Hass; - eapply (tac_assumption _ _ j p P); - [env_reflexivity - |apply Hass - |env_cbv; apply _ || - fail 1 "iAssumption:" j "not absorbing and the remaining hypotheses not affine"] - |assert (P = False%I) as Hass by reflexivity; - apply (tac_false_destruct _ j p P); - [env_reflexivity - |exact Hass] - |find p Γ Q] - end in - lazymatch goal with - | |- envs_entails (Envs ?Γp ?Γs _) ?Q => - first [find true Γp Q | find false Γs Q - |fail "iAssumption:" Q "not found"] - end. - -(** * False *) -Tactic Notation "iExFalso" := apply tac_ex_falso. - -(** * Making hypotheses persistent or pure *) -Local Tactic Notation "iPersistent" constr(H) := - eapply tac_persistent with _ H _ _ _; (* (i:=H) *) - [env_reflexivity || fail "iPersistent:" H "not found" - |apply _ || - let P := match goal with |- IntoPersistent _ ?P _ => P end in - fail "iPersistent:" P "not persistent" - |env_cbv; apply _ || - let P := match goal with |- TCOr (Affine ?P) _ => P end in - fail "iPersistent:" P "not affine and the goal not absorbing" - |env_reflexivity|]. - -Local Tactic Notation "iPure" constr(H) "as" simple_intropattern(pat) := - eapply tac_pure with _ H _ _ _; (* (i:=H1) *) - [env_reflexivity || fail "iPure:" H "not found" - |apply _ || - let P := match goal with |- IntoPure ?P _ => P end in - fail "iPure:" P "not pure" - |env_cbv; apply _ || - let P := match goal with |- TCOr (Affine ?P) _ => P end in - fail "iPure:" P "not affine and the goal not absorbing" - |intros pat]. - -Tactic Notation "iEmpIntro" := - iStartProof; - eapply tac_emp_intro; - [env_cbv; apply _ || - fail "iEmpIntro: spatial context contains non-affine hypotheses"]. - -Tactic Notation "iPureIntro" := - iStartProof; - eapply tac_pure_intro; - [env_reflexivity - |apply _ || - let P := match goal with |- FromPure _ ?P _ => P end in - fail "iPureIntro:" P "not pure" - |]. - -(** Framing *) -Local Ltac iFrameFinish := - lazy iota beta; - try match goal with - | |- envs_entails _ True => by iPureIntro - | |- envs_entails _ emp => iEmpIntro - end. - -Local Ltac iFramePure t := - iStartProof; - let φ := type of t in - eapply (tac_frame_pure _ _ _ _ t); - [apply _ || fail "iFrame: cannot frame" φ - |iFrameFinish]. - -Local Ltac iFrameHyp H := - iStartProof; - eapply tac_frame with _ H _ _ _; - [env_reflexivity || fail "iFrame:" H "not found" - |apply _ || - let R := match goal with |- Frame _ ?R _ _ => R end in - fail "iFrame: cannot frame" R - |iFrameFinish]. - -Local Ltac iFrameAnyPure := - repeat match goal with H : _ |- _ => iFramePure H end. - -Local Ltac iFrameAnyPersistent := - iStartProof; - let rec go Hs := - match Hs with [] => idtac | ?H :: ?Hs => repeat iFrameHyp H; go Hs end in - match goal with - | |- envs_entails ?Δ _ => - let Hs := eval cbv in (env_dom (env_intuitionistic Δ)) in go Hs - end. - -Local Ltac iFrameAnySpatial := - iStartProof; - let rec go Hs := - match Hs with [] => idtac | ?H :: ?Hs => try iFrameHyp H; go Hs end in - match goal with - | |- envs_entails ?Δ _ => - let Hs := eval cbv in (env_dom (env_spatial Δ)) in go Hs - end. - -Tactic Notation "iFrame" := iFrameAnySpatial. - -Tactic Notation "iFrame" "(" constr(t1) ")" := - iFramePure t1. -Tactic Notation "iFrame" "(" constr(t1) constr(t2) ")" := - iFramePure t1; iFrame ( t2 ). -Tactic Notation "iFrame" "(" constr(t1) constr(t2) constr(t3) ")" := - iFramePure t1; iFrame ( t2 t3 ). -Tactic Notation "iFrame" "(" constr(t1) constr(t2) constr(t3) constr(t4) ")" := - iFramePure t1; iFrame ( t2 t3 t4 ). -Tactic Notation "iFrame" "(" constr(t1) constr(t2) constr(t3) constr(t4) - constr(t5) ")" := - iFramePure t1; iFrame ( t2 t3 t4 t5 ). -Tactic Notation "iFrame" "(" constr(t1) constr(t2) constr(t3) constr(t4) - constr(t5) constr(t6) ")" := - iFramePure t1; iFrame ( t2 t3 t4 t5 t6 ). -Tactic Notation "iFrame" "(" constr(t1) constr(t2) constr(t3) constr(t4) - constr(t5) constr(t6) constr(t7) ")" := - iFramePure t1; iFrame ( t2 t3 t4 t5 t6 t7 ). -Tactic Notation "iFrame" "(" constr(t1) constr(t2) constr(t3) constr(t4) - constr(t5) constr(t6) constr(t7) constr(t8)")" := - iFramePure t1; iFrame ( t2 t3 t4 t5 t6 t7 t8 ). - -Tactic Notation "iFrame" constr(Hs) := - let rec go Hs := - lazymatch Hs with - | [] => idtac - | SelPure :: ?Hs => iFrameAnyPure; go Hs - | SelPersistent :: ?Hs => iFrameAnyPersistent; go Hs - | SelSpatial :: ?Hs => iFrameAnySpatial; go Hs - | SelIdent ?H :: ?Hs => iFrameHyp H; go Hs - end - in let Hs := sel_pat.parse Hs in go Hs. -Tactic Notation "iFrame" "(" constr(t1) ")" constr(Hs) := - iFramePure t1; iFrame Hs. -Tactic Notation "iFrame" "(" constr(t1) constr(t2) ")" constr(Hs) := - iFramePure t1; iFrame ( t2 ) Hs. -Tactic Notation "iFrame" "(" constr(t1) constr(t2) constr(t3) ")" constr(Hs) := - iFramePure t1; iFrame ( t2 t3 ) Hs. -Tactic Notation "iFrame" "(" constr(t1) constr(t2) constr(t3) constr(t4) ")" - constr(Hs) := - iFramePure t1; iFrame ( t2 t3 t4 ) Hs. -Tactic Notation "iFrame" "(" constr(t1) constr(t2) constr(t3) constr(t4) - constr(t5) ")" constr(Hs) := - iFramePure t1; iFrame ( t2 t3 t4 t5 ) Hs. -Tactic Notation "iFrame" "(" constr(t1) constr(t2) constr(t3) constr(t4) - constr(t5) constr(t6) ")" constr(Hs) := - iFramePure t1; iFrame ( t2 t3 t4 t5 t6 ) Hs. -Tactic Notation "iFrame" "(" constr(t1) constr(t2) constr(t3) constr(t4) - constr(t5) constr(t6) constr(t7) ")" constr(Hs) := - iFramePure t1; iFrame ( t2 t3 t4 t5 t6 t7 ) Hs. -Tactic Notation "iFrame" "(" constr(t1) constr(t2) constr(t3) constr(t4) - constr(t5) constr(t6) constr(t7) constr(t8)")" constr(Hs) := - iFramePure t1; iFrame ( t2 t3 t4 t5 t6 t7 t8 ) Hs. - -(** * Basic introduction tactics *) -Local Tactic Notation "iIntro" "(" simple_intropattern(x) ")" := - (* In the case the goal starts with an [let x := _ in _], we do not - want to unfold x and start the proof mode. Instead, we want to - use intros. So [iStartProof] has to be called only if [intros] - fails *) - intros x || - (iStartProof; - lazymatch goal with - | |- envs_entails _ _ => - eapply tac_forall_intro; - [apply _ || - let P := match goal with |- FromForall ?P _ => P end in - fail "iIntro: cannot turn" P "into a universal quantifier" - |lazy beta; intros x] - end). - -Local Tactic Notation "iIntro" constr(H) := - iStartProof; - first - [ (* (?Q → _) *) - eapply tac_impl_intro with _ H _ _ _; (* (i:=H) *) - [apply _ - |env_cbv; apply _ || - let P := lazymatch goal with |- Persistent ?P => P end in - fail 1 "iIntro: introducing non-persistent" H ":" P - "into non-empty spatial context" - |env_reflexivity || fail 1 "iIntro:" H "not fresh" - |apply _ - |] - | (* (_ -∗ _) *) - eapply tac_wand_intro with _ H _ _; (* (i:=H) *) - [apply _ - | env_reflexivity || fail 1 "iIntro:" H "not fresh" - |] - | fail "iIntro: nothing to introduce" ]. - -Local Tactic Notation "iIntro" "#" constr(H) := - iStartProof; - first - [ (* (?P → _) *) - eapply tac_impl_intro_persistent with _ H _ _ _; (* (i:=H) *) - [apply _ - |apply _ || - let P := match goal with |- IntoPersistent _ ?P _ => P end in - fail 1 "iIntro:" P "not persistent" - |env_reflexivity || fail 1 "iIntro:" H "not fresh" - |] - | (* (?P -∗ _) *) - eapply tac_wand_intro_persistent with _ H _ _ _; (* (i:=H) *) - [ apply _ - | apply _ || - let P := match goal with |- IntoPersistent _ ?P _ => P end in - fail 1 "iIntro:" P "not persistent" - |apply _ || - let P := match goal with |- TCOr (Affine ?P) _ => P end in - fail 1 "iIntro:" P "not affine and the goal not absorbing" - |env_reflexivity || fail 1 "iIntro:" H "not fresh" - |] - | fail "iIntro: nothing to introduce" ]. - -Local Tactic Notation "iIntro" "_" := - first - [ (* (?Q → _) *) - iStartProof; eapply tac_impl_intro_drop; - [ apply _ | ] - | (* (_ -∗ _) *) - iStartProof; eapply tac_wand_intro_drop; - [ apply _ - | apply _ || - let P := match goal with |- TCOr (Affine ?P) _ => P end in - fail 1 "iIntro:" P "not affine and the goal not absorbing" - |] - | (* (∀ _, _) *) iIntro (_) - | fail 1 "iIntro: nothing to introduce" ]. - -Local Tactic Notation "iIntroForall" := - lazymatch goal with - | |- ∀ _, ?P => fail (* actually an →, this is handled by iIntro below *) - | |- ∀ _, _ => intro - | |- let _ := _ in _ => intro - | |- _ => - iStartProof; - lazymatch goal with - | |- envs_entails _ (∀ x : _, _) => let x' := fresh x in iIntro (x') - end - end. -Local Tactic Notation "iIntro" := - lazymatch goal with - | |- _ → ?P => intro - | |- _ => - iStartProof; - lazymatch goal with - | |- envs_entails _ (_ -∗ _) => iIntro (?) || let H := iFresh in iIntro #H || iIntro H - | |- envs_entails _ (_ → _) => iIntro (?) || let H := iFresh in iIntro #H || iIntro H - end - end. - -(** * Specialize *) -Record iTrm {X As S} := - ITrm { itrm : X ; itrm_vars : hlist As ; itrm_hyps : S }. -Arguments ITrm {_ _ _} _ _ _. - -Notation "( H $! x1 .. xn )" := - (ITrm H (hcons x1 .. (hcons xn hnil) ..) "") (at level 0, x1, xn at level 9). -Notation "( H $! x1 .. xn 'with' pat )" := - (ITrm H (hcons x1 .. (hcons xn hnil) ..) pat) (at level 0, x1, xn at level 9). -Notation "( H 'with' pat )" := (ITrm H hnil pat) (at level 0). - -(** There is some hacky stuff going on here: because of Coq bug #6583, unresolved -type classes in the arguments `xs` are resolved at arbitrary moments. Tactics -like `apply`, `split` and `eexists` wrongly trigger type class search to resolve -these holes. To avoid TC being triggered too eagerly, this tactic uses `refine` -at most places instead of `apply`. *) -Local Tactic Notation "iSpecializeArgs" constr(H) open_constr(xs) := - let rec go xs := - lazymatch xs with - | hnil => idtac - | hcons ?x ?xs => - notypeclasses refine (tac_forall_specialize _ _ H _ _ _ _ _ _ _); - [env_reflexivity || fail "iSpecialize:" H "not found" - |iSolveTC || - let P := match goal with |- IntoForall ?P _ => P end in - fail "iSpecialize: cannot instantiate" P "with" x - |lazymatch goal with (* Force [A] in [ex_intro] to deal with coercions. *) - | |- ∃ _ : ?A, _ => - notypeclasses refine (@ex_intro A _ x (conj _ _)) - end; [shelve..|env_reflexivity|go xs]] - end in - go xs. - -Local Tactic Notation "iSpecializePat" open_constr(H) constr(pat) := - let solve_to_wand H1 := - iSolveTC || - let P := match goal with |- IntoWand _ _ ?P _ _ => P end in - fail "iSpecialize:" P "not an implication/wand" in - let solve_done d := - lazymatch d with - | true => - done || - let Q := match goal with |- envs_entails _ ?Q => Q end in - fail "iSpecialize: cannot solve" Q "using done" - | false => idtac - end in - let rec go H1 pats := - lazymatch pats with - | [] => idtac - | SForall :: ?pats => - idtac "[IPM] The * specialization pattern is deprecated because it is applied implicitly."; - go H1 pats - | SIdent ?H2 :: ?pats => - notypeclasses refine (tac_specialize _ _ _ H2 _ H1 _ _ _ _ _ _ _ _ _ _); - [env_reflexivity || fail "iSpecialize:" H2 "not found" - |env_reflexivity || fail "iSpecialize:" H1 "not found" - |iSolveTC || - let P := match goal with |- IntoWand _ _ ?P ?Q _ => P end in - let Q := match goal with |- IntoWand _ _ ?P ?Q _ => Q end in - fail "iSpecialize: cannot instantiate" P "with" Q - |env_reflexivity|go H1 pats] - | SPureGoal ?d :: ?pats => - notypeclasses refine (tac_specialize_assert_pure _ _ H1 _ _ _ _ _ _ _ _ _ _ _ _); - [env_reflexivity || fail "iSpecialize:" H1 "not found" - |solve_to_wand H1 - |iSolveTC || - let Q := match goal with |- FromPure _ ?Q _ => Q end in - fail "iSpecialize:" Q "not pure" - |env_reflexivity - |solve_done d (*goal*) - |go H1 pats] - | SGoal (SpecGoal GPersistent false ?Hs_frame [] ?d) :: ?pats => - notypeclasses refine (tac_specialize_assert_persistent _ _ _ H1 _ _ _ _ _ _ _ _ _ _ _ _ _); - [env_reflexivity || fail "iSpecialize:" H1 "not found" - |solve_to_wand H1 - |iSolveTC || - let Q := match goal with |- Persistent ?Q => Q end in - fail "iSpecialize:" Q "not persistent" - |iSolveTC - |env_reflexivity - |iFrame Hs_frame; solve_done d (*goal*) - |go H1 pats] - | SGoal (SpecGoal GPersistent _ _ _ _) :: ?pats => - fail "iSpecialize: cannot select hypotheses for persistent premise" - | SGoal (SpecGoal ?m ?lr ?Hs_frame ?Hs ?d) :: ?pats => - let Hs' := eval cbv in (if lr then Hs else Hs_frame ++ Hs) in - notypeclasses refine (tac_specialize_assert _ _ _ _ H1 _ lr Hs' _ _ _ _ _ _ _ _ _ _ _); - [env_reflexivity || fail "iSpecialize:" H1 "not found" - |solve_to_wand H1 - |lazymatch m with - | GSpatial => notypeclasses refine (add_modal_id _ _) - | GModal => iSolveTC || fail "iSpecialize: goal not a modality" - end - |env_reflexivity || - let Hs' := iMissingHyps Hs' in - fail "iSpecialize: hypotheses" Hs' "not found" - |iFrame Hs_frame; solve_done d (*goal*) - |go H1 pats] - | SAutoFrame GPersistent :: ?pats => - notypeclasses refine (tac_specialize_assert_persistent _ _ _ H1 _ _ _ _ _ _ _ _ _ _ _ _ _); - [env_reflexivity || fail "iSpecialize:" H1 "not found" - |solve_to_wand H1 - |iSolveTC || - let Q := match goal with |- Persistent ?Q => Q end in - fail "iSpecialize:" Q "not persistent" - |env_reflexivity - |solve [iFrame "∗ #"] - |go H1 pats] - | SAutoFrame ?m :: ?pats => - notypeclasses refine (tac_specialize_frame _ _ H1 _ _ _ _ _ _ _ _ _ _ _ _); - [env_reflexivity || fail "iSpecialize:" H1 "not found" - |solve_to_wand H1 - |lazymatch m with - | GSpatial => notypeclasses refine (add_modal_id _ _) - | GModal => iSolveTC || fail "iSpecialize: goal not a modality" - end - |first - [notypeclasses refine (tac_unlock_emp _ _ _) - |notypeclasses refine (tac_unlock_True _ _ _) - |iFrame "∗ #"; notypeclasses refine (tac_unlock _ _ _) - |fail "iSpecialize: premise cannot be solved by framing"] - |exact eq_refl]; iIntro H1; go H1 pats - end in let pats := spec_pat.parse pat in go H pats. - -(* The argument [p] denotes whether the conclusion of the specialized term is -persistent. If so, one can use all spatial hypotheses for both proving the -premises and the remaning goal. The argument [p] can either be a Boolean or an -introduction pattern, which will be coerced into [true] when it solely contains -`#` or `%` patterns at the top-level. - -In case the specialization pattern in [t] states that the modality of the goal -should be kept for one of the premises (i.e. [>[H1 .. Hn]] is used) then [p] -defaults to [false] (i.e. spatial hypotheses are not preserved). *) -Tactic Notation "iSpecializeCore" open_constr(H) - "with" open_constr(xs) open_constr(pat) "as" constr(p) := - let p := intro_pat_persistent p in - let pat := spec_pat.parse pat in - let H := - lazymatch type of H with - | string => constr:(INamed H) - | _ => H - end in - iSpecializeArgs H xs; [..| - lazymatch type of H with - | ident => - (* The lemma [tac_specialize_persistent_helper] allows one to use all - spatial hypotheses for both proving the premises of the lemma we - specialize as well as those of the remaining goal. We can only use it when - the result of the specialization is persistent, and no modality is - eliminated. As an optimization, we do not use this when only universal - quantifiers are instantiated. *) - let pat := spec_pat.parse pat in - lazymatch eval compute in - (p && bool_decide (pat ≠[]) && negb (existsb spec_pat_modal pat)) with - | true => - (* FIXME: do something reasonable when the BI is not affine *) - notypeclasses refine (tac_specialize_persistent_helper _ _ H _ _ _ _ _ _ _ _ _ _ _); - [env_reflexivity || fail "iSpecialize:" H "not found" - |iSpecializePat H pat; - [.. - |refine (tac_specialize_persistent_helper_done _ H _ _ _); - env_reflexivity] - |iSolveTC || - let Q := match goal with |- IntoPersistent _ ?Q _ => Q end in - fail "iSpecialize:" Q "not persistent" - |env_cbv; iSolveTC || - let Q := match goal with |- TCAnd _ (Affine ?Q) => Q end in - fail "iSpecialize:" Q "not affine" - |env_reflexivity - |(* goal *)] - | false => iSpecializePat H pat - end - | _ => fail "iSpecialize:" H "should be a hypothesis, use iPoseProof instead" - end]. - -Tactic Notation "iSpecializeCore" open_constr(t) "as" constr(p) := - lazymatch type of t with - | string => iSpecializeCore t with hnil "" as p - | ident => iSpecializeCore t with hnil "" as p - | _ => - lazymatch t with - | ITrm ?H ?xs ?pat => iSpecializeCore H with xs pat as p - | _ => fail "iSpecialize:" t "should be a proof mode term" - end - end. - -Tactic Notation "iSpecialize" open_constr(t) := - iSpecializeCore t as false. -Tactic Notation "iSpecialize" open_constr(t) "as" "#" := - iSpecializeCore t as true. - -(** * Pose proof *) -(* The tactic [iIntoEmpValid] tactic solves a goal [bi_emp_valid Q]. The -argument [t] must be a Coq term whose type is of the following shape: - -[∀ (x_1 : A_1) .. (x_n : A_n), φ] - -and so that we have an instance `AsValid φ Q`. - -Examples of such [φ]s are - -- [bi_emp_valid P], in which case [Q] should be [P] -- [P1 ⊢ P2], in which case [Q] should be [P1 -∗ P2] -- [P1 ⊣⊢ P2], in which case [Q] should be [P1 ↔ P2] - -The tactic instantiates each dependent argument [x_i] with an evar and generates -a goal [R] for each non-dependent argument [x_i : R]. For example, if the -original goal was [Q] and [t] has type [∀ x, P x → Q], then it generates an evar -[?x] for [x] and a subgoal [P ?x]. *) -Tactic Notation "iIntoEmpValid" open_constr(t) := - let rec go t := - (* We try two reduction tactics for the type of t before trying to - specialize it. We first try the head normal form in order to - unfold all the definition that could hide an entailment. Then, - we try the much weaker [eval cbv zeta], because entailment is - not necessarilly opaque, and could be unfolded by [hnf]. - - However, for calling type class search, we only use [cbv zeta] - in order to make sure we do not unfold [bi_emp_valid]. *) - let tT := type of t in - first - [ let tT' := eval hnf in tT in go_specialize t tT' - | let tT' := eval cbv zeta in tT in go_specialize t tT' - | let tT' := eval cbv zeta in tT in - notypeclasses refine (as_emp_valid_1 tT _ _); - [iSolveTC || fail "iPoseProof: not a BI assertion" - |exact t]] - with go_specialize t tT := - lazymatch tT with (* We do not use hnf of tT, because, if - entailment is not opaque, then it would - unfold it. *) - | ?P → ?Q => let H := fresh in assert P as H; [|go uconstr:(t H); clear H] - | ∀ _ : ?T, _ => - (* Put [T] inside an [id] to avoid TC inference from being invoked. *) - (* This is a workarround for Coq bug #6583. *) - let e := fresh in evar (e:id T); - let e' := eval unfold e in e in clear e; go (t e') - end - in - go t. - -(* The tactic [tac] is called with a temporary fresh name [H]. The argument -[lazy_tc] denotes whether type class inference on the premises of [lem] should -be performed before (if false) or after (if true) [tac H] is called. - -The tactic [iApply] uses laxy type class inference, so that evars can first be -instantiated by matching with the goal, whereas [iDestruct] does not, because -eliminations may not be performed when type classes have not been resolved. -*) -Tactic Notation "iPoseProofCore" open_constr(lem) - "as" constr(p) constr(lazy_tc) tactic(tac) := - iStartProof; - let Htmp := iFresh in - let t := lazymatch lem with ITrm ?t ?xs ?pat => t | _ => lem end in - let t := lazymatch type of t with string => constr:(INamed t) | _ => t end in - let spec_tac _ := - lazymatch lem with - | ITrm ?t ?xs ?pat => iSpecializeCore (ITrm Htmp xs pat) as p - | _ => idtac - end in - let go goal_tac := - lazymatch type of t with - | ident => - eapply tac_pose_proof_hyp with _ _ t _ Htmp _; - [env_reflexivity || fail "iPoseProof:" t "not found" - |env_reflexivity || fail "iPoseProof:" Htmp "not fresh" - |goal_tac ()] - | _ => - eapply tac_pose_proof with _ Htmp _; (* (j:=H) *) - [iIntoEmpValid t - |env_reflexivity || fail "iPoseProof:" Htmp "not fresh" - |goal_tac ()] - end; - try iSolveTC in - lazymatch eval compute in lazy_tc with - | true => go ltac:(fun _ => spec_tac (); last (tac Htmp)) - | false => go spec_tac; last (tac Htmp) - end. - -(** * Apply *) -Tactic Notation "iApplyHyp" constr(H) := - let rec go H := first - [eapply tac_apply with _ H _ _ _; - [env_reflexivity - |iSolveTC - |lazy beta (* reduce betas created by instantiation *)] - |iSpecializePat H "[]"; last go H] in - iExact H || - go H || - lazymatch iTypeOf H with - | Some (_,?Q) => fail "iApply: cannot apply" Q - end. - -Tactic Notation "iApply" open_constr(lem) := - iPoseProofCore lem as false true (fun H => iApplyHyp H). - -(** * Revert *) -Local Tactic Notation "iForallRevert" ident(x) := - let err x := - intros x; - iMatchHyp (fun H P => - lazymatch P with - | context [x] => fail 2 "iRevert:" x "is used in hypothesis" H - end) in - iStartProof; - let A := type of x in - lazymatch type of A with - | Prop => revert x; first [apply tac_pure_revert|err x] - | _ => revert x; first [apply tac_forall_revert|err x] - end. - -Tactic Notation "iRevert" constr(Hs) := - let rec go Hs := - lazymatch Hs with - | [] => idtac - | ESelPure :: ?Hs => - repeat match goal with x : _ |- _ => revert x end; - go Hs - | ESelIdent _ ?H :: ?Hs => - eapply tac_revert with _ H _ _; (* (i:=H2) *) - [env_reflexivity || fail "iRevert:" H "not found" - |env_cbv; go Hs] - end in - let Hs := iElaborateSelPat Hs in iStartProof; go Hs. - -Tactic Notation "iRevert" "(" ident(x1) ")" := - iForallRevert x1. -Tactic Notation "iRevert" "(" ident(x1) ident(x2) ")" := - iForallRevert x2; iRevert ( x1 ). -Tactic Notation "iRevert" "(" ident(x1) ident(x2) ident(x3) ")" := - iForallRevert x3; iRevert ( x1 x2 ). -Tactic Notation "iRevert" "(" ident(x1) ident(x2) ident(x3) ident(x4) ")" := - iForallRevert x4; iRevert ( x1 x2 x3 ). -Tactic Notation "iRevert" "(" ident(x1) ident(x2) ident(x3) ident(x4) - ident(x5) ")" := - iForallRevert x5; iRevert ( x1 x2 x3 x4 ). -Tactic Notation "iRevert" "(" ident(x1) ident(x2) ident(x3) ident(x4) - ident(x5) ident(x6) ")" := - iForallRevert x6; iRevert ( x1 x2 x3 x4 x5 ). -Tactic Notation "iRevert" "(" ident(x1) ident(x2) ident(x3) ident(x4) - ident(x5) ident(x6) ident(x7) ")" := - iForallRevert x7; iRevert ( x1 x2 x3 x4 x5 x6 ). -Tactic Notation "iRevert" "(" ident(x1) ident(x2) ident(x3) ident(x4) - ident(x5) ident(x6) ident(x7) ident(x8) ")" := - iForallRevert x8; iRevert ( x1 x2 x3 x4 x5 x6 x7 ). - -Tactic Notation "iRevert" "(" ident(x1) ")" constr(Hs) := - iRevert Hs; iRevert ( x1 ). -Tactic Notation "iRevert" "(" ident(x1) ident(x2) ")" constr(Hs) := - iRevert Hs; iRevert ( x1 x2 ). -Tactic Notation "iRevert" "(" ident(x1) ident(x2) ident(x3) ")" constr(Hs) := - iRevert Hs; iRevert ( x1 x2 x3 ). -Tactic Notation "iRevert" "(" ident(x1) ident(x2) ident(x3) ident(x4) ")" - constr(Hs) := - iRevert Hs; iRevert ( x1 x2 x3 x4 ). -Tactic Notation "iRevert" "(" ident(x1) ident(x2) ident(x3) ident(x4) - ident(x5) ")" constr(Hs) := - iRevert Hs; iRevert ( x1 x2 x3 x4 x5 ). -Tactic Notation "iRevert" "(" ident(x1) ident(x2) ident(x3) ident(x4) - ident(x5) ident(x6) ")" constr(Hs) := - iRevert Hs; iRevert ( x1 x2 x3 x4 x5 x6 ). -Tactic Notation "iRevert" "(" ident(x1) ident(x2) ident(x3) ident(x4) - ident(x5) ident(x6) ident(x7) ")" constr(Hs) := - iRevert Hs; iRevert ( x1 x2 x3 x4 x5 x6 x7 ). -Tactic Notation "iRevert" "(" ident(x1) ident(x2) ident(x3) ident(x4) - ident(x5) ident(x6) ident(x7) ident(x8) ")" constr(Hs) := - iRevert Hs; iRevert ( x1 x2 x3 x4 x5 x6 x7 x8 ). - -(** * Disjunction *) -Tactic Notation "iLeft" := - iStartProof; - eapply tac_or_l; - [iSolveTC || - let P := match goal with |- FromOr ?P _ _ => P end in - fail "iLeft:" P "not a disjunction" - |]. -Tactic Notation "iRight" := - iStartProof; - eapply tac_or_r; - [iSolveTC || - let P := match goal with |- FromOr ?P _ _ => P end in - fail "iRight:" P "not a disjunction" - |]. - -Local Tactic Notation "iOrDestruct" constr(H) "as" constr(H1) constr(H2) := - eapply tac_or_destruct with _ _ H _ H1 H2 _ _ _; (* (i:=H) (j1:=H1) (j2:=H2) *) - [env_reflexivity || fail "iOrDestruct:" H "not found" - |iSolveTC || - let P := match goal with |- IntoOr ?P _ _ => P end in - fail "iOrDestruct: cannot destruct" P - |env_reflexivity || fail "iOrDestruct:" H1 "not fresh" - |env_reflexivity || fail "iOrDestruct:" H2 "not fresh" - | |]. - -(** * Conjunction and separating conjunction *) -Tactic Notation "iSplit" := - iStartProof; - eapply tac_and_split; - [iSolveTC || - let P := match goal with |- FromAnd ?P _ _ => P end in - fail "iSplit:" P "not a conjunction"| |]. - -Tactic Notation "iSplitL" constr(Hs) := - iStartProof; - let Hs := words Hs in - let Hs := eval vm_compute in (INamed <$> Hs) in - eapply tac_sep_split with _ _ Left Hs _ _; (* (js:=Hs) *) - [iSolveTC || - let P := match goal with |- FromSep _ ?P _ _ => P end in - fail "iSplitL:" P "not a separating conjunction" - |env_reflexivity || - let Hs := iMissingHyps Hs in - fail "iSplitL: hypotheses" Hs "not found" - | |]. - -Tactic Notation "iSplitR" constr(Hs) := - iStartProof; - let Hs := words Hs in - let Hs := eval vm_compute in (INamed <$> Hs) in - eapply tac_sep_split with _ _ Right Hs _ _; (* (js:=Hs) *) - [iSolveTC || - let P := match goal with |- FromSep _ ?P _ _ => P end in - fail "iSplitR:" P "not a separating conjunction" - |env_reflexivity || - let Hs := iMissingHyps Hs in - fail "iSplitR: hypotheses" Hs "not found" - | |]. - -Tactic Notation "iSplitL" := iSplitR "". -Tactic Notation "iSplitR" := iSplitL "". - -Local Tactic Notation "iAndDestruct" constr(H) "as" constr(H1) constr(H2) := - eapply tac_and_destruct with _ H _ H1 H2 _ _ _; (* (i:=H) (j1:=H1) (j2:=H2) *) - [env_reflexivity || fail "iAndDestruct:" H "not found" - |env_cbv; iSolveTC || - let P := - lazymatch goal with - | |- IntoSep ?P _ _ => P - | |- IntoAnd _ ?P _ _ => P - end in - fail "iAndDestruct: cannot destruct" P - |env_reflexivity || fail "iAndDestruct:" H1 "or" H2 " not fresh"|]. - -Local Tactic Notation "iAndDestructChoice" constr(H) "as" constr(d) constr(H') := - eapply tac_and_destruct_choice with _ H _ d H' _ _ _; - [env_reflexivity || fail "iAndDestructChoice:" H "not found" - |env_cbv; iSolveTC || - let P := match goal with |- TCOr (IntoAnd _ ?P _ _) _ => P end in - fail "iAndDestructChoice: cannot destruct" P - |env_reflexivity || fail "iAndDestructChoice:" H' " not fresh"|]. - -(** * Combinining hypotheses *) -Tactic Notation "iCombine" constr(Hs) "as" constr(H) := - let Hs := words Hs in - let Hs := eval vm_compute in (INamed <$> Hs) in - eapply tac_combine with _ _ Hs _ _ H _; - [env_reflexivity || - let Hs := iMissingHyps Hs in - fail "iCombine: hypotheses" Hs "not found" - |iSolveTC - |env_reflexivity || fail "iCombine:" H "not fresh"|]. - -Tactic Notation "iCombine" constr(H1) constr(H2) "as" constr(H) := - iCombine [H1;H2] as H. - -(** * Existential *) -Tactic Notation "iExists" uconstr(x1) := - iStartProof; - eapply tac_exist; - [iSolveTC || - let P := match goal with |- FromExist ?P _ => P end in - fail "iExists:" P "not an existential" - |cbv beta; eexists x1]. - -Tactic Notation "iExists" uconstr(x1) "," uconstr(x2) := - iExists x1; iExists x2. -Tactic Notation "iExists" uconstr(x1) "," uconstr(x2) "," uconstr(x3) := - iExists x1; iExists x2, x3. -Tactic Notation "iExists" uconstr(x1) "," uconstr(x2) "," uconstr(x3) "," - uconstr(x4) := - iExists x1; iExists x2, x3, x4. -Tactic Notation "iExists" uconstr(x1) "," uconstr(x2) "," uconstr(x3) "," - uconstr(x4) "," uconstr(x5) := - iExists x1; iExists x2, x3, x4, x5. -Tactic Notation "iExists" uconstr(x1) "," uconstr(x2) "," uconstr(x3) "," - uconstr(x4) "," uconstr(x5) "," uconstr(x6) := - iExists x1; iExists x2, x3, x4, x5, x6. -Tactic Notation "iExists" uconstr(x1) "," uconstr(x2) "," uconstr(x3) "," - uconstr(x4) "," uconstr(x5) "," uconstr(x6) "," uconstr(x7) := - iExists x1; iExists x2, x3, x4, x5, x6, x7. -Tactic Notation "iExists" uconstr(x1) "," uconstr(x2) "," uconstr(x3) "," - uconstr(x4) "," uconstr(x5) "," uconstr(x6) "," uconstr(x7) "," - uconstr(x8) := - iExists x1; iExists x2, x3, x4, x5, x6, x7, x8. - -Local Tactic Notation "iExistDestruct" constr(H) - "as" simple_intropattern(x) constr(Hx) := - eapply tac_exist_destruct with H _ Hx _ _; (* (i:=H) (j:=Hx) *) - [env_reflexivity || fail "iExistDestruct:" H "not found" - |iSolveTC || - let P := match goal with |- IntoExist ?P _ => P end in - fail "iExistDestruct: cannot destruct" P|]; - let y := fresh in - intros y; eexists; split; - [env_reflexivity || fail "iExistDestruct:" Hx "not fresh" - |revert y; intros x]. - -(** * Modality introduction *) -Tactic Notation "iModIntro" uconstr(sel) := - iStartProof; - notypeclasses refine (tac_modal_intro _ sel _ _ _ _ _ _ _ _ _ _ _ _ _); - [iSolveTC || - fail "iModIntro: the goal is not a modality" - |iSolveTC || - let s := lazymatch goal with |- IntoModalPersistentEnv _ _ _ ?s => s end in - lazymatch eval hnf in s with - | MIEnvForall ?C => fail "iModIntro: persistent context does not satisfy" C - | MIEnvIsEmpty => fail "iModIntro: persistent context is non-empty" - end - |iSolveTC || - let s := lazymatch goal with |- IntoModalPersistentEnv _ _ _ ?s => s end in - lazymatch eval hnf in s with - | MIEnvForall ?C => fail "iModIntro: spatial context does not satisfy" C - | MIEnvIsEmpty => fail "iModIntro: spatial context is non-empty" - end - |env_cbv; iSolveTC || - fail "iModIntro: cannot filter spatial context when goal is not absorbing" - |]. -Tactic Notation "iModIntro" := iModIntro _. -Tactic Notation "iAlways" := iModIntro. - -(** * Later *) -Tactic Notation "iNext" open_constr(n) := iModIntro (â–·^n _)%I. -Tactic Notation "iNext" := iModIntro (â–·^_ _)%I. - -(** * Update modality *) -Tactic Notation "iModCore" constr(H) := - eapply tac_modal_elim with _ H _ _ _ _ _ _; - [env_reflexivity || fail "iMod:" H "not found" - |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 - |try fast_done (* optional side-condition *) - |env_reflexivity|]. - -(** * Basic destruct tactic *) -Tactic Notation "iDestructHyp" constr(H) "as" constr(pat) := - let rec go Hz pat := - lazymatch pat with - | IAnom => - lazymatch Hz with - | IAnon _ => idtac - | INamed ?Hz => let Hz' := iFresh in iRename Hz into Hz' - end - | IDrop => iClearHyp Hz - | IFrame => iFrameHyp Hz - | IIdent ?y => iRename Hz into y - | IList [[]] => iExFalso; iExact Hz - | IList [[?pat1; IDrop]] => iAndDestructChoice Hz as Left Hz; go Hz pat1 - | IList [[IDrop; ?pat2]] => iAndDestructChoice Hz as Right Hz; go Hz pat2 - | IList [[?pat1; ?pat2]] => - let Hy := iFresh in iAndDestruct Hz as Hz Hy; go Hz pat1; go Hy pat2 - | IList [[?pat1];[?pat2]] => iOrDestruct Hz as Hz Hz; [go Hz pat1|go Hz pat2] - | IPureElim => iPure Hz as ? - | IRewrite Right => iPure Hz as -> - | IRewrite Left => iPure Hz as <- - | IAlwaysElim ?pat => iPersistent Hz; go Hz pat - | IModalElim ?pat => iModCore Hz; go Hz pat - | _ => fail "iDestruct:" pat "invalid" - end in - let rec find_pat found pats := - lazymatch pats with - | [] => - lazymatch found with - | true => idtac - | false => fail "iDestruct:" pat "should contain exactly one proper introduction pattern" - end - | ISimpl :: ?pats => simpl; find_pat found pats - | IClear ?H :: ?pats => iClear H; find_pat found pats - | IClearFrame ?H :: ?pats => iFrame H; find_pat found pats - | ?pat :: ?pats => - lazymatch found with - | false => go H pat; find_pat true pats - | true => fail "iDestruct:" pat "should contain exactly one proper introduction pattern" - end - end in - let pats := intro_pat.parse pat in - find_pat false pats. - -Tactic Notation "iDestructHyp" constr(H) "as" "(" simple_intropattern(x1) ")" - constr(pat) := - iExistDestruct H as x1 H; iDestructHyp H as @ pat. -Tactic Notation "iDestructHyp" constr(H) "as" "(" simple_intropattern(x1) - simple_intropattern(x2) ")" constr(pat) := - iExistDestruct H as x1 H; iDestructHyp H as ( x2 ) pat. -Tactic Notation "iDestructHyp" constr(H) "as" "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) ")" constr(pat) := - iExistDestruct H as x1 H; iDestructHyp H as ( x2 x3 ) pat. -Tactic Notation "iDestructHyp" constr(H) "as" "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) ")" - constr(pat) := - iExistDestruct H as x1 H; iDestructHyp H as ( x2 x3 x4 ) pat. -Tactic Notation "iDestructHyp" constr(H) "as" "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) - simple_intropattern(x5) ")" constr(pat) := - iExistDestruct H as x1 H; iDestructHyp H as ( x2 x3 x4 x5 ) pat. -Tactic Notation "iDestructHyp" constr(H) "as" "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) - simple_intropattern(x5) simple_intropattern(x6) ")" constr(pat) := - iExistDestruct H as x1 H; iDestructHyp H as ( x2 x3 x4 x5 x6 ) pat. -Tactic Notation "iDestructHyp" constr(H) "as" "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) - simple_intropattern(x5) simple_intropattern(x6) simple_intropattern(x7) ")" - constr(pat) := - iExistDestruct H as x1 H; iDestructHyp H as ( x2 x3 x4 x5 x6 x7 ) pat. -Tactic Notation "iDestructHyp" constr(H) "as" "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) - simple_intropattern(x5) simple_intropattern(x6) simple_intropattern(x7) - simple_intropattern(x8) ")" constr(pat) := - iExistDestruct H as x1 H; iDestructHyp H as ( x2 x3 x4 x5 x6 x7 x8 ) pat. - -(** * Introduction tactic *) -Tactic Notation "iIntros" constr(pat) := - let rec go pats startproof := - lazymatch pats with - | [] => - lazymatch startproof with - | true => iStartProof - | false => idtac - end - (* Optimizations to avoid generating fresh names *) - | IPureElim :: ?pats => iIntro (?); go pats startproof - | IAlwaysElim (IIdent ?H) :: ?pats => iIntro #H; go pats false - | IDrop :: ?pats => iIntro _; go pats startproof - | IIdent ?H :: ?pats => iIntro H; go pats startproof - (* Introduction patterns that can only occur at the top-level *) - | IPureIntro :: ?pats => iPureIntro; go pats false - | IAlwaysIntro :: ?pats => iAlways; go pats false - | IModalIntro :: ?pats => iModIntro; go pats false - | IForall :: ?pats => repeat iIntroForall; go pats startproof - | IAll :: ?pats => repeat (iIntroForall || iIntro); go pats startproof - (* Clearing and simplifying introduction patterns *) - | ISimpl :: ?pats => simpl; go pats startproof - | IClear ?H :: ?pats => iClear H; go pats false - | IClearFrame ?H :: ?pats => iFrame H; go pats false - | IDone :: ?pats => try done; go pats startproof - (* Introduction + destruct *) - | IAlwaysElim ?pat :: ?pats => - let H := iFresh in iIntro #H; iDestructHyp H as pat; go pats false - | ?pat :: ?pats => - let H := iFresh in iIntro H; iDestructHyp H as pat; go pats false - end - in let pats := intro_pat.parse pat in go pats true. -Tactic Notation "iIntros" := iIntros [IAll]. - -Tactic Notation "iIntros" "(" simple_intropattern(x1) ")" := - iIntro ( x1 ). -Tactic Notation "iIntros" "(" simple_intropattern(x1) - simple_intropattern(x2) ")" := - iIntros ( x1 ); iIntro ( x2 ). -Tactic Notation "iIntros" "(" simple_intropattern(x1) simple_intropattern(x2) - simple_intropattern(x3) ")" := - iIntros ( x1 x2 ); iIntro ( x3 ). -Tactic Notation "iIntros" "(" simple_intropattern(x1) simple_intropattern(x2) - simple_intropattern(x3) simple_intropattern(x4) ")" := - iIntros ( x1 x2 x3 ); iIntro ( x4 ). -Tactic Notation "iIntros" "(" simple_intropattern(x1) simple_intropattern(x2) - simple_intropattern(x3) simple_intropattern(x4) simple_intropattern(x5) ")" := - iIntros ( x1 x2 x3 x4 ); iIntro ( x5 ). -Tactic Notation "iIntros" "(" simple_intropattern(x1) simple_intropattern(x2) - simple_intropattern(x3) simple_intropattern(x4) simple_intropattern(x5) - simple_intropattern(x6) ")" := - iIntros ( x1 x2 x3 x4 x5 ); iIntro ( x6 ). -Tactic Notation "iIntros" "(" simple_intropattern(x1) simple_intropattern(x2) - simple_intropattern(x3) simple_intropattern(x4) simple_intropattern(x5) - simple_intropattern(x6) simple_intropattern(x7) ")" := - iIntros ( x1 x2 x3 x4 x5 x6 ); iIntro ( x7 ). -Tactic Notation "iIntros" "(" simple_intropattern(x1) simple_intropattern(x2) - simple_intropattern(x3) simple_intropattern(x4) simple_intropattern(x5) - simple_intropattern(x6) simple_intropattern(x7) simple_intropattern(x8) ")" := - iIntros ( x1 x2 x3 x4 x5 x6 x7 ); iIntro ( x8 ). -Tactic Notation "iIntros" "(" simple_intropattern(x1) simple_intropattern(x2) - simple_intropattern(x3) simple_intropattern(x4) simple_intropattern(x5) - simple_intropattern(x6) simple_intropattern(x7) simple_intropattern(x8) - simple_intropattern(x9) ")" := - iIntros ( x1 x2 x3 x4 x5 x6 x7 x8 ); iIntro ( x9 ). -Tactic Notation "iIntros" "(" simple_intropattern(x1) simple_intropattern(x2) - simple_intropattern(x3) simple_intropattern(x4) simple_intropattern(x5) - simple_intropattern(x6) simple_intropattern(x7) simple_intropattern(x8) - simple_intropattern(x9) simple_intropattern(x10) ")" := - iIntros ( x1 x2 x3 x4 x5 x6 x7 x8 x9 ); iIntro ( x10 ). -Tactic Notation "iIntros" "(" simple_intropattern(x1) simple_intropattern(x2) - simple_intropattern(x3) simple_intropattern(x4) simple_intropattern(x5) - simple_intropattern(x6) simple_intropattern(x7) simple_intropattern(x8) - simple_intropattern(x9) simple_intropattern(x10) simple_intropattern(x11) ")" := - iIntros ( x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 ); iIntro ( x11 ). -Tactic Notation "iIntros" "(" simple_intropattern(x1) simple_intropattern(x2) - simple_intropattern(x3) simple_intropattern(x4) simple_intropattern(x5) - simple_intropattern(x6) simple_intropattern(x7) simple_intropattern(x8) - simple_intropattern(x9) simple_intropattern(x10) simple_intropattern(x11) - simple_intropattern(x12) ")" := - iIntros ( x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 x11 ); iIntro ( x12 ). - -Tactic Notation "iIntros" "(" simple_intropattern(x1) ")" constr(p) := - iIntros ( x1 ); iIntros p. -Tactic Notation "iIntros" "(" simple_intropattern(x1) simple_intropattern(x2) - ")" constr(p) := - iIntros ( x1 x2 ); iIntros p. -Tactic Notation "iIntros" "(" simple_intropattern(x1) simple_intropattern(x2) - simple_intropattern(x3) ")" constr(p) := - iIntros ( x1 x2 x3 ); iIntros p. -Tactic Notation "iIntros" "(" simple_intropattern(x1) simple_intropattern(x2) - simple_intropattern(x3) simple_intropattern(x4) ")" constr(p) := - iIntros ( x1 x2 x3 x4 ); iIntros p. -Tactic Notation "iIntros" "(" simple_intropattern(x1) simple_intropattern(x2) - simple_intropattern(x3) simple_intropattern(x4) simple_intropattern(x5) - ")" constr(p) := - iIntros ( x1 x2 x3 x4 x5 ); iIntros p. -Tactic Notation "iIntros" "(" simple_intropattern(x1) simple_intropattern(x2) - simple_intropattern(x3) simple_intropattern(x4) simple_intropattern(x5) - simple_intropattern(x6) ")" constr(p) := - iIntros ( x1 x2 x3 x4 x5 x6 ); iIntros p. -Tactic Notation "iIntros" "(" simple_intropattern(x1) simple_intropattern(x2) - simple_intropattern(x3) simple_intropattern(x4) simple_intropattern(x5) - simple_intropattern(x6) simple_intropattern(x7) ")" constr(p) := - iIntros ( x1 x2 x3 x4 x5 x6 x7 ); iIntros p. -Tactic Notation "iIntros" "(" simple_intropattern(x1) simple_intropattern(x2) - simple_intropattern(x3) simple_intropattern(x4) simple_intropattern(x5) - simple_intropattern(x6) simple_intropattern(x7) simple_intropattern(x8) - ")" constr(p) := - iIntros ( x1 x2 x3 x4 x5 x6 x7 x8 ); iIntros p. -Tactic Notation "iIntros" "(" simple_intropattern(x1) simple_intropattern(x2) - simple_intropattern(x3) simple_intropattern(x4) simple_intropattern(x5) - simple_intropattern(x6) simple_intropattern(x7) simple_intropattern(x8) - simple_intropattern(x9) ")" constr(p) := - iIntros ( x1 x2 x3 x4 x5 x6 x7 x8 x9 ); iIntros p. -Tactic Notation "iIntros" "(" simple_intropattern(x1) simple_intropattern(x2) - simple_intropattern(x3) simple_intropattern(x4) simple_intropattern(x5) - simple_intropattern(x6) simple_intropattern(x7) simple_intropattern(x8) - simple_intropattern(x9) simple_intropattern(x10) ")" constr(p) := - iIntros ( x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 ); iIntros p. -Tactic Notation "iIntros" "(" simple_intropattern(x1) simple_intropattern(x2) - simple_intropattern(x3) simple_intropattern(x4) simple_intropattern(x5) - simple_intropattern(x6) simple_intropattern(x7) simple_intropattern(x8) - simple_intropattern(x9) simple_intropattern(x10) simple_intropattern(x11) - ")" constr(p) := - iIntros ( x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 x11 ); iIntros p. -Tactic Notation "iIntros" "(" simple_intropattern(x1) simple_intropattern(x2) - simple_intropattern(x3) simple_intropattern(x4) simple_intropattern(x5) - simple_intropattern(x6) simple_intropattern(x7) simple_intropattern(x8) - simple_intropattern(x9) simple_intropattern(x10) simple_intropattern(x11) - simple_intropattern(x12) ")" constr(p) := - iIntros ( x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 x11 x12 ); iIntros p. - -Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) ")" := - iIntros p; iIntros ( x1 ). -Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) - simple_intropattern(x2) ")" := - iIntros p; iIntros ( x1 x2 ). -Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) ")" := - iIntros p; iIntros ( x1 x2 x3 ). -Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) ")" := - iIntros p; iIntros ( x1 x2 x3 x4 ). -Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) - simple_intropattern(x5) ")" := - iIntros p; iIntros ( x1 x2 x3 x4 x5 ). -Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) - simple_intropattern(x5) simple_intropattern(x6) ")" := - iIntros p; iIntros ( x1 x2 x3 x4 x5 x6 ). -Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) - simple_intropattern(x5) simple_intropattern(x6) simple_intropattern(x7) ")" := - iIntros p; iIntros ( x1 x2 x3 x4 x5 x6 x7 ). -Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) - simple_intropattern(x5) simple_intropattern(x6) simple_intropattern(x7) - simple_intropattern(x8) ")" := - iIntros p; iIntros ( x1 x2 x3 x4 x5 x6 x7 x8 ). -Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) - simple_intropattern(x5) simple_intropattern(x6) simple_intropattern(x7) - simple_intropattern(x8) simple_intropattern(x9) ")" := - iIntros p; iIntros ( x1 x2 x3 x4 x5 x6 x7 x8 x9 ). -Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) - simple_intropattern(x5) simple_intropattern(x6) simple_intropattern(x7) - simple_intropattern(x8) simple_intropattern(x9) simple_intropattern(x10) ")" := - iIntros p; iIntros ( x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 ). -Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) - simple_intropattern(x5) simple_intropattern(x6) simple_intropattern(x7) - simple_intropattern(x8) simple_intropattern(x9) simple_intropattern(x10) - simple_intropattern(x11) ")" := - iIntros p; iIntros ( x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 x11 ). -Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) - simple_intropattern(x5) simple_intropattern(x6) simple_intropattern(x7) - simple_intropattern(x8) simple_intropattern(x9) simple_intropattern(x10) - simple_intropattern(x11) simple_intropattern(x12) ")" := - iIntros p; iIntros ( x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 x11 x12 ). - -Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) ")" constr(p2) := - iIntros p; iIntros ( x1 ); iIntros p2. -Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) - simple_intropattern(x2) ")" constr(p2) := - iIntros p; iIntros ( x1 x2 ); iIntros p2. -Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) ")" constr(p2) := - iIntros p; iIntros ( x1 x2 x3 ); iIntros p2. -Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) - ")" constr(p2) := - iIntros p; iIntros ( x1 x2 x3 x4 ); iIntros p2. -Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) - simple_intropattern(x5) ")" constr(p2) := - iIntros p; iIntros ( x1 x2 x3 x4 x5 ); iIntros p2. -Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) - simple_intropattern(x5) simple_intropattern(x6) ")" constr(p2) := - iIntros p; iIntros ( x1 x2 x3 x4 x5 x6 ); iIntros p2. -Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) - simple_intropattern(x5) simple_intropattern(x6) simple_intropattern(x7) - ")" constr(p2) := - iIntros p; iIntros ( x1 x2 x3 x4 x5 x6 x7 ); iIntros p2. -Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) - simple_intropattern(x5) simple_intropattern(x6) simple_intropattern(x7) - simple_intropattern(x8) ")" constr(p2) := - iIntros p; iIntros ( x1 x2 x3 x4 x5 x6 x7 x8 ); iIntros p2. -Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) - simple_intropattern(x5) simple_intropattern(x6) simple_intropattern(x7) - simple_intropattern(x8) simple_intropattern(x9) ")" constr(p2) := - iIntros p; iIntros ( x1 x2 x3 x4 x5 x6 x7 x8 x9 ); iIntros p2. -Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) - simple_intropattern(x5) simple_intropattern(x6) simple_intropattern(x7) - simple_intropattern(x8) simple_intropattern(x9) simple_intropattern(x10) - ")" constr(p2) := - iIntros p; iIntros ( x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 ); iIntros p2. -Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) - simple_intropattern(x5) simple_intropattern(x6) simple_intropattern(x7) - simple_intropattern(x8) simple_intropattern(x9) simple_intropattern(x10) - simple_intropattern(x11) ")" constr(p2) := - iIntros p; iIntros ( x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 x11 ); iIntros p2. -Tactic Notation "iIntros" constr(p) "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) - simple_intropattern(x5) simple_intropattern(x6) simple_intropattern(x7) - simple_intropattern(x8) simple_intropattern(x9) simple_intropattern(x10) - simple_intropattern(x11) simple_intropattern(x12) ")" constr(p2) := - iIntros p; iIntros ( x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 x11 x12 ); iIntros p2. - - -(* Used for generalization in iInduction and iLöb *) -Tactic Notation "iRevertIntros" constr(Hs) "with" tactic(tac) := - let rec go Hs := - lazymatch Hs with - | [] => tac - | ESelPure :: ?Hs => fail "iRevertIntros: % not supported" - | ESelIdent ?p ?H :: ?Hs => - iRevert H; go Hs; - let H' := - match p with true => constr:([IAlwaysElim (IIdent H)]) | false => H end in - iIntros H' - end in - try iStartProof; let Hs := iElaborateSelPat Hs in go Hs. - -Tactic Notation "iRevertIntros" "(" ident(x1) ")" constr(Hs) "with" tactic(tac):= - iRevertIntros Hs with (iRevert (x1); tac; iIntros (x1)). -Tactic Notation "iRevertIntros" "(" ident(x1) ident(x2) ")" constr(Hs) - "with" tactic(tac):= - iRevertIntros Hs with (iRevert (x1 x2); tac; iIntros (x1 x2)). -Tactic Notation "iRevertIntros" "(" ident(x1) ident(x2) ident(x3) ")" constr(Hs) - "with" tactic(tac):= - iRevertIntros Hs with (iRevert (x1 x2 x3); tac; iIntros (x1 x2 x3)). -Tactic Notation "iRevertIntros" "(" ident(x1) ident(x2) ident(x3) ident(x4) ")" - constr(Hs) "with" tactic(tac):= - iRevertIntros Hs with (iRevert (x1 x2 x3 x4); tac; iIntros (x1 x2 x3 x4)). -Tactic Notation "iRevertIntros" "(" ident(x1) ident(x2) ident(x3) ident(x4) - ident(x5) ")" constr(Hs) "with" tactic(tac):= - iRevertIntros Hs with (iRevert (x1 x2 x3 x4 x5); tac; iIntros (x1 x2 x3 x4 x5)). -Tactic Notation "iRevertIntros" "(" ident(x1) ident(x2) ident(x3) ident(x4) - ident(x5) ident(x6) ")" constr(Hs) "with" tactic(tac):= - iRevertIntros Hs with (iRevert (x1 x2 x3 x4 x5 x6); - tac; iIntros (x1 x2 x3 x4 x5 x6)). -Tactic Notation "iRevertIntros" "(" ident(x1) ident(x2) ident(x3) ident(x4) - ident(x5) ident(x6) ident(x7) ")" constr(Hs) "with" tactic(tac):= - iRevertIntros Hs with (iRevert (x1 x2 x3 x4 x5 x6 x7); - tac; iIntros (x1 x2 x3 x4 x5 x6 x7)). -Tactic Notation "iRevertIntros" "(" ident(x1) ident(x2) ident(x3) ident(x4) - ident(x5) ident(x6) ident(x7) ident(x8) ")" constr(Hs) "with" tactic(tac):= - iRevertIntros Hs with (iRevert (x1 x2 x3 x4 x5 x6 x7 x8); - tac; iIntros (x1 x2 x3 x4 x5 x6 x7 x8)). - -Tactic Notation "iRevertIntros" "with" tactic(tac) := - iRevertIntros "" with tac. -Tactic Notation "iRevertIntros" "(" ident(x1) ")" "with" tactic(tac):= - iRevertIntros (x1) "" with tac. -Tactic Notation "iRevertIntros" "(" ident(x1) ident(x2) ")" "with" tactic(tac):= - iRevertIntros (x1 x2) "" with tac. -Tactic Notation "iRevertIntros" "(" ident(x1) ident(x2) ident(x3) ")" - "with" tactic(tac):= - iRevertIntros (x1 x2 x3) "" with tac. -Tactic Notation "iRevertIntros" "(" ident(x1) ident(x2) ident(x3) ident(x4) ")" - "with" tactic(tac):= - iRevertIntros (x1 x2 x3 x4) "" with tac. -Tactic Notation "iRevertIntros" "(" ident(x1) ident(x2) ident(x3) ident(x4) - ident(x5) ")" "with" tactic(tac):= - iRevertIntros (x1 x2 x3 x4 x5) "" with tac. -Tactic Notation "iRevertIntros" "(" ident(x1) ident(x2) ident(x3) ident(x4) - ident(x5) ident(x6) ")" "with" tactic(tac):= - iRevertIntros (x1 x2 x3 x4 x5 x6) "" with tac. -Tactic Notation "iRevertIntros" "(" ident(x1) ident(x2) ident(x3) ident(x4) - ident(x5) ident(x6) ident(x7) ")" "with" tactic(tac):= - iRevertIntros (x1 x2 x3 x4 x5 x6 x7) "" with tac. -Tactic Notation "iRevertIntros" "(" ident(x1) ident(x2) ident(x3) ident(x4) - ident(x5) ident(x6) ident(x7) ident(x8) ")" "with" tactic(tac):= - iRevertIntros (x1 x2 x3 x4 x5 x6 x7 x8) "" with tac. - -(** * Destruct tactic *) -Class CopyDestruct {PROP : bi} (P : PROP). -Arguments CopyDestruct {_} _%I. -Hint Mode CopyDestruct + ! : typeclass_instances. - -Instance copy_destruct_forall {PROP : bi} {A} (Φ : A → PROP) : CopyDestruct (∀ x, Φ x). -Instance copy_destruct_impl {PROP : bi} (P Q : PROP) : - CopyDestruct Q → CopyDestruct (P → Q). -Instance copy_destruct_wand {PROP : bi} (P Q : PROP) : - CopyDestruct Q → CopyDestruct (P -∗ Q). -Instance copy_destruct_affinely {PROP : bi} (P : PROP) : - CopyDestruct P → CopyDestruct (<affine> P). -Instance copy_destruct_persistently {PROP : bi} (P : PROP) : - CopyDestruct P → CopyDestruct (<pers> P). - -Tactic Notation "iDestructCore" open_constr(lem) "as" constr(p) tactic(tac) := - let ident := - lazymatch type of lem with - | ident => constr:(Some lem) - | string => constr:(Some (INamed lem)) - | iTrm => - lazymatch lem with - | @iTrm ident ?H _ _ => constr:(Some H) - | @iTrm string ?H _ _ => constr:(Some (INamed H)) - | _ => constr:(@None ident) - end - | _ => constr:(@None ident) - end in - let intro_destruct n := - let rec go n' := - lazymatch n' with - | 0 => fail "iDestruct: cannot introduce" n "hypotheses" - | 1 => repeat iIntroForall; let H := iFresh in iIntro H; tac H - | S ?n' => repeat iIntroForall; let H := iFresh in iIntro H; go n' - end in - intros; go n in - lazymatch type of lem with - | nat => intro_destruct lem - | Z => (* to make it work in Z_scope. We should just be able to bind - tactic notation arguments to notation scopes. *) - let n := eval compute in (Z.to_nat lem) in intro_destruct n - | _ => - (* Only copy the hypothesis in case there is a [CopyDestruct] instance. - Also, rule out cases in which it does not make sense to copy, namely when - destructing a lemma (instead of a hypothesis) or a spatial hyopthesis - (which cannot be kept). *) - iStartProof; - lazymatch ident with - | None => iPoseProofCore lem as p false tac - | Some ?H => - lazymatch iTypeOf H with - | None => fail "iDestruct:" H "not found" - | Some (true, ?P) => - (* persistent hypothesis, check for a CopyDestruct instance *) - tryif (let dummy := constr:(_ : CopyDestruct P) in idtac) - then (iPoseProofCore lem as p false tac) - else (iSpecializeCore lem as p; last (tac H)) - | Some (false, ?P) => - (* spatial hypothesis, cannot copy *) - iSpecializeCore lem as p; last (tac H) - end - end - end. - -Tactic Notation "iDestruct" open_constr(lem) "as" constr(pat) := - iDestructCore lem as pat (fun H => iDestructHyp H as pat). -Tactic Notation "iDestruct" open_constr(lem) "as" "(" simple_intropattern(x1) ")" - constr(pat) := - iDestructCore lem as pat (fun H => iDestructHyp H as ( x1 ) pat). -Tactic Notation "iDestruct" open_constr(lem) "as" "(" simple_intropattern(x1) - simple_intropattern(x2) ")" constr(pat) := - iDestructCore lem as pat (fun H => iDestructHyp H as ( x1 x2 ) pat). -Tactic Notation "iDestruct" open_constr(lem) "as" "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) ")" constr(pat) := - iDestructCore lem as pat (fun H => iDestructHyp H as ( x1 x2 x3 ) pat). -Tactic Notation "iDestruct" open_constr(lem) "as" "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) ")" - constr(pat) := - iDestructCore lem as pat (fun H => iDestructHyp H as ( x1 x2 x3 x4 ) pat). -Tactic Notation "iDestruct" open_constr(lem) "as" "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) - simple_intropattern(x5) ")" constr(pat) := - iDestructCore lem as pat (fun H => iDestructHyp H as ( x1 x2 x3 x4 x5 ) pat). -Tactic Notation "iDestruct" open_constr(lem) "as" "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) - simple_intropattern(x5) simple_intropattern(x6) ")" constr(pat) := - iDestructCore lem as pat (fun H => iDestructHyp H as ( x1 x2 x3 x4 x5 x6 ) pat). -Tactic Notation "iDestruct" open_constr(lem) "as" "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) - simple_intropattern(x5) simple_intropattern(x6) simple_intropattern(x7) ")" - constr(pat) := - iDestructCore lem as pat (fun H => iDestructHyp H as ( x1 x2 x3 x4 x5 x6 x7 ) pat). -Tactic Notation "iDestruct" open_constr(lem) "as" "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) - simple_intropattern(x5) simple_intropattern(x6) simple_intropattern(x7) - simple_intropattern(x8) ")" constr(pat) := - iDestructCore lem as pat (fun H => iDestructHyp H as ( x1 x2 x3 x4 x5 x6 x7 x8 ) pat). - -Tactic Notation "iDestruct" open_constr(lem) "as" "%" simple_intropattern(pat) := - iDestructCore lem as true (fun H => iPure H as pat). - -Tactic Notation "iPoseProof" open_constr(lem) "as" constr(pat) := - iPoseProofCore lem as pat false (fun H => iDestructHyp H as pat). -Tactic Notation "iPoseProof" open_constr(lem) "as" "(" simple_intropattern(x1) ")" - constr(pat) := - iPoseProofCore lem as pat false (fun H => iDestructHyp H as ( x1 ) pat). -Tactic Notation "iPoseProof" open_constr(lem) "as" "(" simple_intropattern(x1) - simple_intropattern(x2) ")" constr(pat) := - iPoseProofCore lem as pat false (fun H => iDestructHyp H as ( x1 x2 ) pat). -Tactic Notation "iPoseProof" open_constr(lem) "as" "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) ")" constr(pat) := - iPoseProofCore lem as pat false (fun H => iDestructHyp H as ( x1 x2 x3 ) pat). -Tactic Notation "iPoseProof" open_constr(lem) "as" "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) ")" - constr(pat) := - iPoseProofCore lem as pat false (fun H => iDestructHyp H as ( x1 x2 x3 x4 ) pat). -Tactic Notation "iPoseProof" open_constr(lem) "as" "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) - simple_intropattern(x5) ")" constr(pat) := - iPoseProofCore lem as pat false (fun H => iDestructHyp H as ( x1 x2 x3 x4 x5 ) pat). -Tactic Notation "iPoseProof" open_constr(lem) "as" "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) - simple_intropattern(x5) simple_intropattern(x6) ")" constr(pat) := - iPoseProofCore lem as pat false (fun H => iDestructHyp H as ( x1 x2 x3 x4 x5 x6 ) pat). -Tactic Notation "iPoseProof" open_constr(lem) "as" "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) - simple_intropattern(x5) simple_intropattern(x6) simple_intropattern(x7) ")" - constr(pat) := - iPoseProofCore lem as pat false (fun H => iDestructHyp H as ( x1 x2 x3 x4 x5 x6 x7 ) pat). -Tactic Notation "iPoseProof" open_constr(lem) "as" "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) - simple_intropattern(x5) simple_intropattern(x6) simple_intropattern(x7) - simple_intropattern(x8) ")" constr(pat) := - iPoseProofCore lem as pat false (fun H => iDestructHyp H as ( x1 x2 x3 x4 x5 x6 x7 x8 ) pat). - -(** * Induction *) -(* An invocation of [iInduction (x) as pat IH forall (x1...xn) Hs] will -result in the following actions: - -- Revert the proofmode hypotheses [Hs] -- Revert all remaining spatial hypotheses and the remaining persistent - hypotheses containing the induction variable [x] -- Revert the pure hypotheses [x1..xn] - -- Actuall perform induction - -- Introduce thee pure hypotheses [x1..xn] -- Introduce the spatial hypotheses and persistent hypotheses involving [x] -- Introduce the proofmode hypotheses [Hs] -*) -Tactic Notation "iInductionCore" constr(x) "as" simple_intropattern(pat) constr(IH) := - let rec fix_ihs rev_tac := - lazymatch goal with - | H : context [envs_entails _ _] |- _ => - eapply (tac_revert_ih _ _ _ H _); - [env_reflexivity - || fail "iInduction: spatial context not empty, this should not happen" - |clear H]; - fix_ihs ltac:(fun j => - let IH' := eval vm_compute in - match j with 0%N => IH | _ => IH +:+ pretty j end in - iIntros [IAlwaysElim (IIdent IH')]; - let j := eval vm_compute in (1 + j)%N in - rev_tac j) - | _ => rev_tac 0%N - end in - induction x as pat; fix_ihs ltac:(fun _ => idtac). - -Ltac iHypsContaining x := - let rec go Γ x Hs := - lazymatch Γ with - | Enil => constr:(Hs) - | Esnoc ?Γ ?H ?Q => - match Q with - | context [x] => go Γ x (H :: Hs) - | _ => go Γ x Hs - end - end in - let Γp := lazymatch goal with |- envs_entails (Envs ?Γp _ _) _ => Γp end in - let Γs := lazymatch goal with |- envs_entails (Envs _ ?Γs _) _ => Γs end in - let Hs := go Γp x (@nil ident) in go Γs x Hs. - -Tactic Notation "iInductionRevert" constr(x) constr(Hs) "with" tactic(tac) := - iRevertIntros Hs with ( - iRevertIntros "∗" with ( - idtac; - let Hsx := iHypsContaining x in - iRevertIntros Hsx with tac - ) - ). - -Tactic Notation "iInduction" constr(x) "as" simple_intropattern(pat) constr(IH) := - iInductionRevert x "" with (iInductionCore x as pat IH). -Tactic Notation "iInduction" constr(x) "as" simple_intropattern(pat) constr(IH) - "forall" "(" ident(x1) ")" := - iInductionRevert x "" with (iRevertIntros(x1) "" with (iInductionCore x as pat IH)). -Tactic Notation "iInduction" constr(x) "as" simple_intropattern(pat) constr(IH) - "forall" "(" ident(x1) ident(x2) ")" := - iInductionRevert x "" with (iRevertIntros(x1 x2) "" with (iInductionCore x as pat IH)). -Tactic Notation "iInduction" constr(x) "as" simple_intropattern(pat) constr(IH) - "forall" "(" ident(x1) ident(x2) ident(x3) ")" := - iInductionRevert x "" with (iRevertIntros(x1 x2 x3) "" with (iInductionCore x as pat IH)). -Tactic Notation "iInduction" constr(x) "as" simple_intropattern(pat) constr(IH) - "forall" "(" ident(x1) ident(x2) ident(x3) ident(x4) ")" := - iInductionRevert x "" with (iRevertIntros(x1 x2 x3 x4) "" with (iInductionCore x as pat IH)). -Tactic Notation "iInduction" constr(x) "as" simple_intropattern(pat) constr(IH) - "forall" "(" ident(x1) ident(x2) ident(x3) ident(x4) ident(x5) ")" := - iInductionRevert x "" with (iRevertIntros(x1 x2 x3 x4 x5) "" with (iInductionCore x as pat IH)). -Tactic Notation "iInduction" constr(x) "as" simple_intropattern(pat) constr(IH) - "forall" "(" ident(x1) ident(x2) ident(x3) ident(x4) ident(x5) ident(x6) ")" := - iInductionRevert x "" with (iRevertIntros(x1 x2 x3 x4 x5 x6) "" with (iInductionCore x as pat IH)). -Tactic Notation "iInduction" constr(x) "as" simple_intropattern(pat) constr(IH) - "forall" "(" ident(x1) ident(x2) ident(x3) ident(x4) ident(x5) ident(x6) - ident(x7) ")" := - iInductionRevert x "" with (iRevertIntros(x1 x2 x3 x4 x5 x6 x7) "" with (iInductionCore x as pat IH)). -Tactic Notation "iInduction" constr(x) "as" simple_intropattern(pat) constr(IH) - "forall" "(" ident(x1) ident(x2) ident(x3) ident(x4) ident(x5) ident(x6) - ident(x7) ident(x8) ")" := - iInductionRevert x "" with (iRevertIntros(x1 x2 x3 x4 x5 x6 x7 x8) "" with (iInductionCore x as pat IH)). - -Tactic Notation "iInduction" constr(x) "as" simple_intropattern(pat) constr(IH) - "forall" constr(Hs) := - iInductionRevert x Hs with (iInductionCore x as pat IH). -Tactic Notation "iInduction" constr(x) "as" simple_intropattern(pat) constr(IH) - "forall" "(" ident(x1) ")" constr(Hs) := - iInductionRevert x Hs with (iRevertIntros(x1) "" with (iInductionCore x as pat IH)). -Tactic Notation "iInduction" constr(x) "as" simple_intropattern(pat) constr(IH) - "forall" "(" ident(x1) ident(x2) ")" constr(Hs) := - iInductionRevert x Hs with (iRevertIntros(x1 x2) "" with (iInductionCore x as pat IH)). -Tactic Notation "iInduction" constr(x) "as" simple_intropattern(pat) constr(IH) - "forall" "(" ident(x1) ident(x2) ident(x3) ")" constr(Hs) := - iInductionRevert x Hs with (iRevertIntros(x1 x2 x3) "" with (iInductionCore x as pat IH)). -Tactic Notation "iInduction" constr(x) "as" simple_intropattern(pat) constr(IH) - "forall" "(" ident(x1) ident(x2) ident(x3) ident(x4) ")" constr(Hs) := - iInductionRevert x Hs with (iRevertIntros(x1 x2 x3 x4) "" with (iInductionCore x as pat IH)). -Tactic Notation "iInduction" constr(x) "as" simple_intropattern(pat) constr(IH) - "forall" "(" ident(x1) ident(x2) ident(x3) ident(x4) ident(x5) ")" - constr(Hs) := - iInductionRevert x Hs with (iRevertIntros(x1 x2 x3 x4 x5) "" with (iInductionCore x as pat IH)). -Tactic Notation "iInduction" constr(x) "as" simple_intropattern(pat) constr(IH) - "forall" "(" ident(x1) ident(x2) ident(x3) ident(x4) ident(x5) ident(x6) ")" - constr(Hs) := - iInductionRevert x Hs with (iRevertIntros(x1 x2 x3 x4 x5 x6) "" with (iInductionCore x as pat IH)). -Tactic Notation "iInduction" constr(x) "as" simple_intropattern(pat) constr(IH) - "forall" "(" ident(x1) ident(x2) ident(x3) ident(x4) ident(x5) ident(x6) - ident(x7) ")" constr(Hs) := - iInductionRevert x Hs with (iRevertIntros(x1 x2 x3 x4 x5 x6 x7) "" with (iInductionCore x as pat IH)). -Tactic Notation "iInduction" constr(x) "as" simple_intropattern(pat) constr(IH) - "forall" "(" ident(x1) ident(x2) ident(x3) ident(x4) ident(x5) ident(x6) - ident(x7) ident(x8) ")" constr(Hs) := - iInductionRevert x Hs with (iRevertIntros(x1 x2 x3 x4 x5 x6 x7 x8) "" with (iInductionCore x as pat IH)). - -(** * Löb Induction *) -Tactic Notation "iLöbCore" "as" constr (IH) := - iStartProof; - (* apply is sometimes confused wrt. canonical structures search. - refine should use the other unification algorithm, which should - not have this issue. *) - notypeclasses refine (tac_löb _ _ IH _ _ _ _); - [reflexivity || fail "iLöb: spatial context not empty, this should not happen" - |env_reflexivity || fail "iLöb:" IH "not fresh"|]. - -Tactic Notation "iLöbRevert" constr(Hs) "with" tactic(tac) := - iRevertIntros Hs with ( - iRevertIntros "∗" with tac - ). - -Tactic Notation "iLöb" "as" constr (IH) := - iLöbRevert "" with (iLöbCore as IH). -Tactic Notation "iLöb" "as" constr (IH) "forall" "(" ident(x1) ")" := - iLöbRevert "" with (iRevertIntros(x1) "" with (iLöbCore as IH)). -Tactic Notation "iLöb" "as" constr (IH) "forall" "(" ident(x1) ident(x2) ")" := - iLöbRevert "" with (iRevertIntros(x1 x2) "" with (iLöbCore as IH)). -Tactic Notation "iLöb" "as" constr (IH) "forall" "(" ident(x1) ident(x2) - ident(x3) ")" := - iLöbRevert "" with (iRevertIntros(x1 x2 x3) "" with (iLöbCore as IH)). -Tactic Notation "iLöb" "as" constr (IH) "forall" "(" ident(x1) ident(x2) - ident(x3) ident(x4) ")" := - iLöbRevert "" with (iRevertIntros(x1 x2 x3 x4) "" with (iLöbCore as IH)). -Tactic Notation "iLöb" "as" constr (IH) "forall" "(" ident(x1) ident(x2) - ident(x3) ident(x4) ident(x5) ")" := - iLöbRevert "" with (iRevertIntros(x1 x2 x3 x4 x5) "" with (iLöbCore as IH)). -Tactic Notation "iLöb" "as" constr (IH) "forall" "(" ident(x1) ident(x2) - ident(x3) ident(x4) ident(x5) ident(x6) ")" := - iLöbRevert "" with (iRevertIntros(x1 x2 x3 x4 x5 x6) "" with (iLöbCore as IH)). -Tactic Notation "iLöb" "as" constr (IH) "forall" "(" ident(x1) ident(x2) - ident(x3) ident(x4) ident(x5) ident(x6) ident(x7) ")" := - iLöbRevert "" with (iRevertIntros(x1 x2 x3 x4 x5 x6 x7) "" with (iLöbCore as IH)). -Tactic Notation "iLöb" "as" constr (IH) "forall" "(" ident(x1) ident(x2) - ident(x3) ident(x4) ident(x5) ident(x6) ident(x7) ident(x8) ")" := - iLöbRevert "" with (iRevertIntros(x1 x2 x3 x4 x5 x6 x7 x8) "" with (iLöbCore as IH)). - -Tactic Notation "iLöb" "as" constr (IH) "forall" constr(Hs) := - iLöbRevert Hs with (iLöbCore as IH). -Tactic Notation "iLöb" "as" constr (IH) "forall" "(" ident(x1) ")" constr(Hs) := - iLöbRevert Hs with (iRevertIntros(x1) "" with (iLöbCore as IH)). -Tactic Notation "iLöb" "as" constr (IH) "forall" "(" ident(x1) ident(x2) ")" - constr(Hs) := - iLöbRevert Hs with (iRevertIntros(x1 x2) "" with (iLöbCore as IH)). -Tactic Notation "iLöb" "as" constr (IH) "forall" "(" ident(x1) ident(x2) - ident(x3) ")" constr(Hs) := - iLöbRevert Hs with (iRevertIntros(x1 x2 x3) "" with (iLöbCore as IH)). -Tactic Notation "iLöb" "as" constr (IH) "forall" "(" ident(x1) ident(x2) - ident(x3) ident(x4) ")" constr(Hs) := - iLöbRevert Hs with (iRevertIntros(x1 x2 x3 x4) "" with (iLöbCore as IH)). -Tactic Notation "iLöb" "as" constr (IH) "forall" "(" ident(x1) ident(x2) - ident(x3) ident(x4) ident(x5) ")" constr(Hs) := - iLöbRevert Hs with (iRevertIntros(x1 x2 x3 x4 x5) "" with (iLöbCore as IH)). -Tactic Notation "iLöb" "as" constr (IH) "forall" "(" ident(x1) ident(x2) - ident(x3) ident(x4) ident(x5) ident(x6) ")" constr(Hs) := - iLöbRevert Hs with (iRevertIntros(x1 x2 x3 x4 x5 x6) "" with (iLöbCore as IH)). -Tactic Notation "iLöb" "as" constr (IH) "forall" "(" ident(x1) ident(x2) - ident(x3) ident(x4) ident(x5) ident(x6) ident(x7) ")" constr(Hs) := - iLöbRevert Hs with (iRevertIntros(x1 x2 x3 x4 x5 x6 x7) "" with (iLöbCore as IH)). -Tactic Notation "iLöb" "as" constr (IH) "forall" "(" ident(x1) ident(x2) - ident(x3) ident(x4) ident(x5) ident(x6) ident(x7) ident(x8) ")" constr(Hs) := - iLöbRevert Hs with (iRevertIntros(x1 x2 x3 x4 x5 x6 x7 x8) "" with (iLöbCore as IH)). - -(** * Assert *) -(* The argument [p] denotes whether [Q] is persistent. It can either be a -Boolean or an introduction pattern, which will be coerced into [true] if it -only contains `#` or `%` patterns at the top-level, and [false] otherwise. *) -Tactic Notation "iAssertCore" open_constr(Q) - "with" constr(Hs) "as" constr(p) tactic(tac) := - iStartProof; - let pats := spec_pat.parse Hs in - lazymatch pats with - | [_] => idtac - | _ => fail "iAssert: exactly one specialization pattern should be given" - end; - let H := iFresh in - eapply tac_assert with _ H Q; - [env_reflexivity - |iSpecializeCore H with hnil pats as p; [..|tac H]]. - -Tactic Notation "iAssertCore" open_constr(Q) "as" constr(p) tactic(tac) := - let p := intro_pat_persistent p in - lazymatch p with - | true => iAssertCore Q with "[#]" as p tac - | false => iAssertCore Q with "[]" as p tac - end. - -Tactic Notation "iAssert" open_constr(Q) "with" constr(Hs) "as" constr(pat) := - iAssertCore Q with Hs as pat (fun H => iDestructHyp H as pat). -Tactic Notation "iAssert" open_constr(Q) "with" constr(Hs) "as" - "(" simple_intropattern(x1) ")" constr(pat) := - iAssertCore Q with Hs as pat (fun H => iDestructHyp H as (x1) pat). -Tactic Notation "iAssert" open_constr(Q) "with" constr(Hs) "as" - "(" simple_intropattern(x1) simple_intropattern(x2) ")" constr(pat) := - iAssertCore Q with Hs as pat (fun H => iDestructHyp H as (x1 x2) pat). -Tactic Notation "iAssert" open_constr(Q) "with" constr(Hs) "as" - "(" simple_intropattern(x1) simple_intropattern(x2) simple_intropattern(x3) - ")" constr(pat) := - iAssertCore Q with Hs as pat (fun H => iDestructHyp H as (x1 x2 x3) pat). -Tactic Notation "iAssert" open_constr(Q) "with" constr(Hs) "as" - "(" simple_intropattern(x1) simple_intropattern(x2) simple_intropattern(x3) - simple_intropattern(x4) ")" constr(pat) := - iAssertCore Q with Hs as pat (fun H => iDestructHyp H as (x1 x2 x3 x4) pat). - -Tactic Notation "iAssert" open_constr(Q) "as" constr(pat) := - iAssertCore Q as pat (fun H => iDestructHyp H as pat). -Tactic Notation "iAssert" open_constr(Q) "as" - "(" simple_intropattern(x1) ")" constr(pat) := - iAssertCore Q as pat (fun H => iDestructHyp H as (x1) pat). -Tactic Notation "iAssert" open_constr(Q) "as" - "(" simple_intropattern(x1) simple_intropattern(x2) ")" constr(pat) := - iAssertCore Q as pat (fun H => iDestructHyp H as (x1 x2) pat). -Tactic Notation "iAssert" open_constr(Q) "as" - "(" simple_intropattern(x1) simple_intropattern(x2) simple_intropattern(x3) - ")" constr(pat) := - iAssertCore Q as pat (fun H => iDestructHyp H as (x1 x2 x3) pat). -Tactic Notation "iAssert" open_constr(Q) "as" - "(" simple_intropattern(x1) simple_intropattern(x2) simple_intropattern(x3) - simple_intropattern(x4) ")" constr(pat) := - iAssertCore Q as pat (fun H => iDestructHyp H as (x1 x2 x3 x4) pat). - -Tactic Notation "iAssert" open_constr(Q) "with" constr(Hs) - "as" "%" simple_intropattern(pat) := - iAssertCore Q with Hs as true (fun H => iPure H as pat). -Tactic Notation "iAssert" open_constr(Q) "as" "%" simple_intropattern(pat) := - iAssert Q with "[-]" as %pat. - -(** * Rewrite *) -Local Ltac iRewriteFindPred := - match goal with - | |- _ ⊣⊢ ?Φ ?x => - generalize x; - match goal with |- (∀ y, @?Ψ y ⊣⊢ _) => unify Φ Ψ; reflexivity end - end. - -Local Tactic Notation "iRewriteCore" constr(lr) open_constr(lem) := - iPoseProofCore lem as true true (fun Heq => - eapply (tac_rewrite _ Heq _ _ lr); - [env_reflexivity || fail "iRewrite:" Heq "not found" - |iSolveTC || - let P := match goal with |- IntoInternalEq ?P _ _ ⊢ _ => P end in - fail "iRewrite:" P "not an equality" - |iRewriteFindPred - |intros ??? ->; reflexivity|lazy beta; iClearHyp Heq]). - -Tactic Notation "iRewrite" open_constr(lem) := iRewriteCore Right lem. -Tactic Notation "iRewrite" "-" open_constr(lem) := iRewriteCore Left lem. - -Local Tactic Notation "iRewriteCore" constr(lr) open_constr(lem) "in" constr(H) := - iPoseProofCore lem as true true (fun Heq => - eapply (tac_rewrite_in _ Heq _ _ H _ _ lr); - [env_reflexivity || fail "iRewrite:" Heq "not found" - |env_reflexivity || fail "iRewrite:" H "not found" - |iSolveTC || - let P := match goal with |- IntoInternalEq ?P _ _ ⊢ _ => P end in - fail "iRewrite:" P "not an equality" - |iRewriteFindPred - |intros ??? ->; reflexivity - |env_reflexivity|lazy beta; iClearHyp Heq]). - -Tactic Notation "iRewrite" open_constr(lem) "in" constr(H) := - iRewriteCore Right lem in H. -Tactic Notation "iRewrite" "-" open_constr(lem) "in" constr(H) := - iRewriteCore Left lem in H. - -Ltac iSimplifyEq := repeat ( - iMatchHyp (fun H P => match P with ⌜_ = _âŒ%I => iDestruct H as %? end) - || simplify_eq/=). - -(** * Update modality *) -Tactic Notation "iMod" open_constr(lem) := - iDestructCore lem as false (fun H => iModCore H). -Tactic Notation "iMod" open_constr(lem) "as" constr(pat) := - iDestructCore lem as false (fun H => iModCore H; last iDestructHyp H as pat). -Tactic Notation "iMod" open_constr(lem) "as" "(" simple_intropattern(x1) ")" - constr(pat) := - iDestructCore lem as false (fun H => iModCore H; last iDestructHyp H as ( x1 ) pat). -Tactic Notation "iMod" open_constr(lem) "as" "(" simple_intropattern(x1) - simple_intropattern(x2) ")" constr(pat) := - iDestructCore lem as false (fun H => iModCore H; last iDestructHyp H as ( x1 x2 ) pat). -Tactic Notation "iMod" open_constr(lem) "as" "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) ")" constr(pat) := - iDestructCore lem as false (fun H => iModCore H; last iDestructHyp H as ( x1 x2 x3 ) pat). -Tactic Notation "iMod" open_constr(lem) "as" "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) ")" - constr(pat) := - iDestructCore lem as false (fun H => - iModCore H; last iDestructHyp H as ( x1 x2 x3 x4 ) pat). -Tactic Notation "iMod" open_constr(lem) "as" "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) - simple_intropattern(x5) ")" constr(pat) := - iDestructCore lem as false (fun H => - iModCore H; last iDestructHyp H as ( x1 x2 x3 x4 x5 ) pat). -Tactic Notation "iMod" open_constr(lem) "as" "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) - simple_intropattern(x5) simple_intropattern(x6) ")" constr(pat) := - iDestructCore lem as false (fun H => - iModCore H; last iDestructHyp H as ( x1 x2 x3 x4 x5 x6 ) pat). -Tactic Notation "iMod" open_constr(lem) "as" "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) - simple_intropattern(x5) simple_intropattern(x6) simple_intropattern(x7) ")" - constr(pat) := - iDestructCore lem as false (fun H => - iModCore H; last iDestructHyp H as ( x1 x2 x3 x4 x5 x6 x7 ) pat). -Tactic Notation "iMod" open_constr(lem) "as" "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) - simple_intropattern(x5) simple_intropattern(x6) simple_intropattern(x7) - simple_intropattern(x8) ")" constr(pat) := - iDestructCore lem as false (fun H => - iModCore H; last iDestructHyp H as ( x1 x2 x3 x4 x5 x6 x7 x8 ) pat). - -Tactic Notation "iMod" open_constr(lem) "as" "%" simple_intropattern(pat) := - iDestructCore lem as false (fun H => iModCore H; iPure H as pat). - -(** * Invariant *) - -(* Finds a hypothesis in the context that is an invariant with - namespace [N]. To do so, we check whether for each hypothesis - ["H":P] we can find an instance of [IntoInv P N] *) -Tactic Notation "iAssumptionInv" constr(N) := - let rec find Γ i := - lazymatch Γ with - | Esnoc ?Γ ?j ?P' => - first [let H := constr:(_: IntoInv P' N) in unify i j|find Γ i] - end in - lazymatch goal with - | |- envs_lookup_delete _ ?i (Envs ?Γp ?Γs _) = Some _ => - first [find Γp i|find Γs i]; env_reflexivity - end. - -(* The argument [select] is the namespace [N] or hypothesis name ["H"] of the -invariant. *) -Tactic Notation "iInvCore" constr(select) "with" constr(pats) "as" tactic(tac) constr(Hclose) := - iStartProof; - let pats := spec_pat.parse pats in - lazymatch pats with - | [_] => idtac - | _ => fail "iInv: exactly one specialization pattern should be given" - end; - let H := iFresh in - lazymatch type of select with - | string => - eapply tac_inv_elim with _ select H _ _ _ _ _ _ _; - first by (iAssumptionCore || fail "iInv: invariant" select "not found") - | ident => - eapply tac_inv_elim with _ select H _ _ _ _ _ _ _; - first by (iAssumptionCore || fail "iInv: invariant" select "not found") - | namespace => - eapply tac_inv_elim with _ _ H _ _ _ _ _ _ _; - first by (iAssumptionInv select || fail "iInv: invariant" select "not found") - | _ => fail "iInv: selector" select "is not of the right type " - end; - [iSolveTC || - let I := match goal with |- ElimInv _ ?I _ _ _ _ _ => I end in - fail "iInv: cannot eliminate invariant " I - |try (split_and?; solve [ fast_done | solve_ndisj ]) - |let R := fresh in intros R; eexists; split; [env_reflexivity|]; - iSpecializePat H pats; last ( - iApplyHyp H; clear R; - iIntros H; (* H was spatial, so it's gone due to the apply and we can reuse the name *) - iIntros Hclose; - tac H - )]. - -Tactic Notation "iInvCore" constr(N) "as" tactic(tac) constr(Hclose) := - iInvCore N with "[$]" as ltac:(tac) Hclose. - -Tactic Notation "iInv" constr(N) "as" constr(pat) constr(Hclose) := - iInvCore N as (fun H => iDestructHyp H as pat) Hclose. -Tactic Notation "iInv" constr(N) "as" "(" simple_intropattern(x1) ")" - constr(pat) constr(Hclose) := - iInvCore N as (fun H => iDestructHyp H as (x1) pat) Hclose. -Tactic Notation "iInv" constr(N) "as" "(" simple_intropattern(x1) - simple_intropattern(x2) ")" constr(pat) constr(Hclose) := - iInvCore N as (fun H => iDestructHyp H as (x1 x2) pat) Hclose. -Tactic Notation "iInv" constr(N) "as" "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) ")" - constr(pat) constr(Hclose) := - iInvCore N as (fun H => iDestructHyp H as (x1 x2 x3) pat) Hclose. -Tactic Notation "iInv" constr(N) "as" "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) ")" - constr(pat) constr(Hclose) := - iInvCore N as (fun H => iDestructHyp H as (x1 x2 x3 x4) pat) Hclose. -Tactic Notation "iInv" constr(N) "with" constr(Hs) "as" constr(pat) constr(Hclose) := - iInvCore N with Hs as (fun H => iDestructHyp H as pat) Hclose. -Tactic Notation "iInv" constr(N) "with" constr(Hs) "as" "(" simple_intropattern(x1) ")" - constr(pat) constr(Hclose) := - iInvCore N with Hs as (fun H => iDestructHyp H as (x1) pat) Hclose. -Tactic Notation "iInv" constr(N) "with" constr(Hs) "as" "(" simple_intropattern(x1) - simple_intropattern(x2) ")" constr(pat) constr(Hclose) := - iInvCore N with Hs as (fun H => iDestructHyp H as (x1 x2) pat) Hclose. -Tactic Notation "iInv" constr(N) "with" constr(Hs) "as" "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) ")" - constr(pat) constr(Hclose) := - iInvCore N with Hs as (fun H => iDestructHyp H as (x1 x2 x3) pat) Hclose. -Tactic Notation "iInv" constr(N) "with" constr(Hs) "as" "(" simple_intropattern(x1) - simple_intropattern(x2) simple_intropattern(x3) simple_intropattern(x4) ")" - constr(pat) constr(Hclose) := - iInvCore N with Hs as (fun H => iDestructHyp H as (x1 x2 x3 x4) pat) Hclose. - -Tactic Notation "iAccu" := - iStartProof; eapply tac_accu; [env_reflexivity || fail "iAccu: not an evar"]. - -Hint Extern 0 (_ ⊢ _) => iStartProof. - -(* Make sure that by and done solve trivial things in proof mode *) -Hint Extern 0 (envs_entails _ _) => iPureIntro; try done. -Hint Extern 0 (envs_entails _ ?Q) => - first [is_evar Q; fail 1|iAssumption]. -Hint Extern 0 (envs_entails _ emp) => iEmpIntro. - -(* TODO: look for a more principled way of adding trivial hints like those -below; see the discussion in !75 for further details. *) -Hint Extern 0 (envs_entails _ (_ ≡ _)) => - rewrite envs_entails_eq; apply bi.internal_eq_refl. -Hint Extern 0 (envs_entails _ (big_opL _ _ _)) => - rewrite envs_entails_eq; apply bi.big_sepL_nil'. -Hint Extern 0 (envs_entails _ (big_opM _ _ _)) => - rewrite envs_entails_eq; apply bi.big_sepM_empty'. -Hint Extern 0 (envs_entails _ (big_opS _ _ _)) => - rewrite envs_entails_eq; apply bi.big_sepS_empty'. -Hint Extern 0 (envs_entails _ (big_opMS _ _ _)) => - rewrite envs_entails_eq; apply bi.big_sepMS_empty'. - -Hint Extern 0 (envs_entails _ (∀ _, _)) => iIntros. -Hint Extern 0 (envs_entails _ (_ → _)) => iIntros. -Hint Extern 0 (envs_entails _ (_ -∗ _)) => iIntros. - -Hint Extern 1 (envs_entails _ (_ ∧ _)) => iSplit. -Hint Extern 1 (envs_entails _ (_ ∗ _)) => iSplit. -Hint Extern 1 (envs_entails _ (â–· _)) => iNext. -Hint Extern 1 (envs_entails _ (â– _)) => iAlways. -Hint Extern 1 (envs_entails _ (<pers> _)) => iAlways. -Hint Extern 1 (envs_entails _ (<affine> _)) => iAlways. -Hint Extern 1 (envs_entails _ (â–¡ _)) => iAlways. -Hint Extern 1 (envs_entails _ (∃ _, _)) => iExists _. -Hint Extern 1 (envs_entails _ (â—‡ _)) => iModIntro. -Hint Extern 1 (envs_entails _ (_ ∨ _)) => iLeft. -Hint Extern 1 (envs_entails _ (_ ∨ _)) => iRight. -Hint Extern 1 (envs_entails _ (|==> _)) => iModIntro. -Hint Extern 1 (envs_entails _ (<absorb> _)) => iModIntro. -Hint Extern 2 (envs_entails _ (|={_}=> _)) => iModIntro. - -Hint Extern 2 (envs_entails _ (_ ∗ _)) => progress iFrame : iFrame. +From iris.proofmode Require Export ltac_tactics. +From iris.proofmode Require Import class_instances_bi class_instances_sbi frame_instances modality_instances.