From 642028e3f2399c65cd39abd800de8cc2ad5ef8a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arma=C3=ABl=20Gu=C3=A9neau?= <armael.gueneau@ens-lyon.org> Date: Thu, 9 Sep 2021 17:29:23 +0200 Subject: [PATCH] Optimize iDestruct (splitting cases) The idea is to avoid unnecessary calls to iRename: when choosing names for sub-patterns, instead of generating fresh names or reusing an existing (unrelated) name, if the sub-pattern is an identifier, then directly chose this identifier as the new name, thus saving a rename later on. --- iris/proofmode/ltac_tactics.v | 72 ++++++++++++++++++++++++++++------- 1 file changed, 58 insertions(+), 14 deletions(-) diff --git a/iris/proofmode/ltac_tactics.v b/iris/proofmode/ltac_tactics.v index f34e41fff..3c5bf6a53 100644 --- a/iris/proofmode/ltac_tactics.v +++ b/iris/proofmode/ltac_tactics.v @@ -1454,6 +1454,24 @@ Tactic Notation "iModCore" constr(H) := (** * Basic destruct tactic *) +(* Two helper tactics to compute an identifier for the hypothesis corresponding + to the intro-pattern [pat], when trying to avoid extra renamings. These + mainly look whether the pattern is a name, and use that name in that case. + Otherwise, [ident_for_pat] generates a fresh name (a safe option), and + [ident_for_pat_default] uses the [default] name that it was provided. +*) +Local Ltac ident_for_pat pat := + lazymatch pat with + | IIdent ?x => x + | _ => let x := iFresh in x + end. + +Local Ltac ident_for_pat_default pat default := + lazymatch pat with + | IIdent ?x => x + | _ => default + end. + (** [pat0] is the unparsed pattern, and is only used in error messages *) Local Ltac iDestructHypGo Hz pat0 pat := lazymatch pat with @@ -1464,36 +1482,59 @@ Local Ltac iDestructHypGo Hz pat0 pat := end | IDrop => iClearHyp Hz | IFrame => iFrameHyp Hz + | IIdent Hz => idtac | IIdent ?y => iRename Hz into y | IList [[]] => iExFalso; iExact Hz (* conjunctive patterns like [H1 H2] *) | IList [[?pat1; IDrop]] => - iAndDestructChoice Hz as Left Hz; - iDestructHypGo Hz pat0 pat1 + let x := ident_for_pat_default pat1 Hz in + iAndDestructChoice Hz as Left x; + iDestructHypGo x pat0 pat1 | IList [[IDrop; ?pat2]] => - iAndDestructChoice Hz as Right Hz; - iDestructHypGo Hz pat0 pat2 + let x := ident_for_pat_default pat2 Hz in + iAndDestructChoice Hz as Right x; + iDestructHypGo x pat0 pat2 (* [% ...] is always interpreted as an existential; there are [IntoExist] instances in place to handle conjunctions with a pure left-hand side this way as well. *) | IList [[IPure IGallinaAnon; ?pat2]] => - iExistDestruct Hz as ? Hz; iDestructHypGo Hz pat0 pat2 + let x := ident_for_pat_default pat2 Hz in + iExistDestruct Hz as ? x; iDestructHypGo x pat0 pat2 | IList [[IPure (IGallinaNamed ?s); ?pat2]] => let x := fresh in - iExistDestruct Hz as x Hz; + let y := ident_for_pat_default pat2 Hz in + iExistDestruct Hz as x y; rename_by_string x s; - iDestructHypGo Hz pat0 pat2 + iDestructHypGo y pat0 pat2 | IList [[?pat1; ?pat2]] => - let Hy := iFresh in iAndDestruct Hz as Hz Hy; - iDestructHypGo Hz pat0 pat1; iDestructHypGo Hy pat0 pat2 + (* We have to take care of not using the same name for the two hypotheses. + Furthermore, we can only reuse the name [Hz] for the first hypothesis, + on which we recurse first. + + One tricky case is iDestruct "H" as "[? H]": here, we cannot reuse the + name "H" for the first hypothesis, as it would clash with the chosen + name for the second one. Hence, we need to compute the second name + first, and check it against [Hz] to know if it can be reused. + *) + let x2 := ident_for_pat pat2 in + let x1 := + lazymatch x2 with + | Hz => ident_for_pat pat1 + | _ => ident_for_pat_default pat1 Hz + end + in + iAndDestruct Hz as x1 x2; + iDestructHypGo x1 pat0 pat1; iDestructHypGo x2 pat0 pat2 | IList [_ :: _ :: _] => fail "iDestruct:" pat0 "has too many conjuncts" | IList [[_]] => fail "iDestruct:" pat0 "has just a single conjunct" (* disjunctive patterns like [H1|H2] *) | IList [[?pat1];[?pat2]] => - iOrDestruct Hz as Hz Hz; - [iDestructHypGo Hz pat0 pat1|iDestructHypGo Hz pat0 pat2] + let x1 := ident_for_pat_default pat1 Hz in + let x2 := ident_for_pat_default pat2 Hz in + iOrDestruct Hz as x1 x2; + [iDestructHypGo x1 pat0 pat1|iDestructHypGo x2 pat0 pat2] (* this matches a list of three or more disjunctions [H1|H2|H3] *) | IList (_ :: _ :: _ :: _) => fail "iDestruct:" pat0 "has too many disjuncts" (* the above patterns don't match [H1 H2|H3] *) @@ -1506,9 +1547,12 @@ Local Ltac iDestructHypGo Hz pat0 pat := rename_by_string x s | IRewrite Right => iPure Hz as -> | IRewrite Left => iPure Hz as <- - | IIntuitionistic ?pat => iIntuitionistic Hz; iDestructHypGo Hz pat0 pat - | ISpatial ?pat => iSpatial Hz; iDestructHypGo Hz pat0 pat - | IModalElim ?pat => iModCore Hz; iDestructHypGo Hz pat0 pat + | IIntuitionistic ?pat => + iIntuitionistic Hz; iDestructHypGo Hz pat0 pat + | ISpatial ?pat => + iSpatial Hz; iDestructHypGo Hz pat0 pat + | IModalElim ?pat => + iModCore Hz; iDestructHypGo Hz pat0 pat | _ => fail "iDestruct:" pat0 "is not supported due to" pat end. Local Ltac iDestructHypFindPat Hgo pat found pats := -- GitLab