From 42e3e6def1d2a4649e459d5c4dbae5e94e6fc91c Mon Sep 17 00:00:00 2001
From: Robbert Krebbers <mail@robbertkrebbers.nl>
Date: Fri, 19 Feb 2016 11:46:27 +0100
Subject: [PATCH] Improve and document split_and tactics.

---
 theories/fin_maps.v |  2 +-
 theories/finite.v   |  6 +++---
 theories/hashset.v  |  6 +++---
 theories/pretty.v   |  2 +-
 theories/tactics.v  | 19 ++++++++++++++-----
 theories/zmap.v     |  2 +-
 6 files changed, 23 insertions(+), 14 deletions(-)

diff --git a/theories/fin_maps.v b/theories/fin_maps.v
index 7ac4cf42..a400ad49 100644
--- a/theories/fin_maps.v
+++ b/theories/fin_maps.v
@@ -476,7 +476,7 @@ Lemma insert_subset_inv {A} (m1 m2 : M A) i x :
   m1 !! i = None → <[i:=x]> m1 ⊂ m2 →
   ∃ m2', m2 = <[i:=x]>m2' ∧ m1 ⊂ m2' ∧ m2' !! i = None.
 Proof.
-  intros Hi Hm1m2. exists (delete i m2). split_ands.
+  intros Hi Hm1m2. exists (delete i m2). split_and?.
   - rewrite insert_delete. done. eapply lookup_weaken, strict_include; eauto.
     by rewrite lookup_insert.
   - eauto using insert_delete_subset.
diff --git a/theories/finite.v b/theories/finite.v
index ccb41220..c56dfcf8 100644
--- a/theories/finite.v
+++ b/theories/finite.v
@@ -220,7 +220,7 @@ Proof. done. Qed.
 Program Instance sum_finite `{Finite A, Finite B} : Finite (A + B)%type :=
   {| enum := (inl <$> enum A) ++ (inr <$> enum B) |}.
 Next Obligation.
-  intros. apply NoDup_app; split_ands.
+  intros. apply NoDup_app; split_and?.
   - apply (NoDup_fmap_2 _). by apply NoDup_enum.
   - intro. rewrite !elem_of_list_fmap. intros (?&?&?) (?&?&?); congruence.
   - apply (NoDup_fmap_2 _). by apply NoDup_enum.
@@ -237,7 +237,7 @@ Program Instance prod_finite `{Finite A, Finite B} : Finite (A * B)%type :=
 Next Obligation.
   intros ??????. induction (NoDup_enum A) as [|x xs Hx Hxs IH]; simpl.
   { constructor. }
-  apply NoDup_app; split_ands.
+  apply NoDup_app; split_and?.
   - by apply (NoDup_fmap_2 _), NoDup_enum.
   - intros [? y]. rewrite elem_of_list_fmap. intros (?&?&?); simplify_eq.
     clear IH. induction Hxs as [|x' xs ?? IH]; simpl.
@@ -271,7 +271,7 @@ Next Obligation.
   intros ????. induction n as [|n IH]; simpl; [apply NoDup_singleton |].
   revert IH. generalize (list_enum (enum A) n). intros l Hl.
   induction (NoDup_enum A) as [|x xs Hx Hxs IH]; simpl; auto; [constructor |].
-  apply NoDup_app; split_ands.
+  apply NoDup_app; split_and?.
   - by apply (NoDup_fmap_2 _).
   - intros [k1 Hk1]. clear Hxs IH. rewrite elem_of_list_fmap.
     intros ([k2 Hk2]&?&?) Hxk2; simplify_eq/=. destruct Hx. revert Hxk2.
diff --git a/theories/hashset.v b/theories/hashset.v
index 30b42fa9..c18bdffe 100644
--- a/theories/hashset.v
+++ b/theories/hashset.v
@@ -85,7 +85,7 @@ Proof.
       rewrite elem_of_list_intersection in Hx; naive_solver. }
     intros [(l&?&?) (k&?&?)]. assert (x ∈ list_intersection l k)
       by (by rewrite elem_of_list_intersection).
-    exists (list_intersection l k); split; [exists l, k|]; split_ands; auto.
+    exists (list_intersection l k); split; [exists l, k|]; split_and?; auto.
     by rewrite option_guard_True by eauto using elem_of_not_nil.
   - unfold elem_of, hashset_elem_of, intersection, hashset_intersection.
     intros [m1 ?] [m2 ?] x; simpl.
@@ -95,7 +95,7 @@ Proof.
     intros [(l&?&?) Hm2]; destruct (m2 !! hash x) as [k|] eqn:?; eauto.
     destruct (decide (x ∈ k)); [destruct Hm2; eauto|].
     assert (x ∈ list_difference l k) by (by rewrite elem_of_list_difference).
-    exists (list_difference l k); split; [right; exists l,k|]; split_ands; auto.
+    exists (list_difference l k); split; [right; exists l,k|]; split_and?; auto.
     by rewrite option_guard_True by eauto using elem_of_not_nil.
   - unfold elem_of at 2, hashset_elem_of, elements, hashset_elems.
     intros [m Hm] x; simpl. setoid_rewrite elem_of_list_bind. split.
@@ -107,7 +107,7 @@ Proof.
     rewrite map_Forall_to_list in Hm. generalize (NoDup_fst_map_to_list m).
     induction Hm as [|[n l] m' [??]];
       csimpl; inversion_clear 1 as [|?? Hn]; [constructor|].
-    apply NoDup_app; split_ands; eauto.
+    apply NoDup_app; split_and?; eauto.
     setoid_rewrite elem_of_list_bind; intros x ? ([n' l']&?&?); simpl in *.
     assert (hash x = n ∧ hash x = n') as [??]; subst.
     { split; [eapply (Forall_forall (λ x, hash x = n) l); eauto|].
diff --git a/theories/pretty.v b/theories/pretty.v
index e7f0b645..2ca9129e 100644
--- a/theories/pretty.v
+++ b/theories/pretty.v
@@ -57,7 +57,7 @@ Proof.
   { intros -> Hlen; edestruct help; rewrite Hlen; simpl; lia. }
   { intros <- Hlen; edestruct help; rewrite <-Hlen; simpl; lia. }
   intros Hs Hlen; apply IH in Hs; destruct Hs;
-    simplify_eq/=; split_ands'; auto using N.div_lt_upper_bound with lia.
+    simplify_eq/=; split_and?; auto using N.div_lt_upper_bound with lia.
   rewrite (N.div_mod x 10), (N.div_mod y 10) by done.
   auto using N.mod_lt with f_equal.
 Qed.
diff --git a/theories/tactics.v b/theories/tactics.v
index 939e9fab..544dc0a1 100644
--- a/theories/tactics.v
+++ b/theories/tactics.v
@@ -54,11 +54,20 @@ Ltac done :=
 Tactic Notation "by" tactic(tac) :=
   tac; done.
 
-(** Whereas the [split] tactic splits any inductive with one constructor, the
-tactic [split_and] only splits a conjunction. *)
-Ltac split_and := match goal with |- _ ∧ _ => split end.
-Ltac split_ands := repeat split_and.
-Ltac split_ands' := repeat (hnf; split_and).
+(** Tactics for splitting conjunctions:
+
+- [split_and] : split the goal if is syntactically of the shape [_ ∧ _]
+- [split_ands?] : split the goal repeatedly (perhaps zero times) while it is
+  of the shape [_ ∧ _].
+- [split_ands!] : works similarly, but at least one split should succeed. In
+  order to do so, it will head normalize the goal first to possibly expose a
+  conjunction.
+
+Note that [split_and] differs from [split] by only splitting conjunctions. The
+[split] tactic splits any inductive with one constructor. *)
+Tactic Notation "split_and" := match goal with |- _ ∧ _ => split end.
+Tactic Notation "split_and" "?" := repeat split_and.
+Tactic Notation "split_and" "!" := hnf; split_and; split_and?.
 
 (** The tactic [case_match] destructs an arbitrary match in the conclusion or
 assumptions, and generates a corresponding equality. This tactic is best used
diff --git a/theories/zmap.v b/theories/zmap.v
index 58a7059d..a0644bfa 100644
--- a/theories/zmap.v
+++ b/theories/zmap.v
@@ -64,7 +64,7 @@ Proof.
   - intros ? [o t t']; unfold map_to_list; simpl.
     assert (NoDup ((prod_map Z.pos id <$> map_to_list t) ++
       prod_map Z.neg id <$> map_to_list t')).
-    { apply NoDup_app; split_ands.
+    { apply NoDup_app; split_and?.
       - apply (NoDup_fmap_2 _), NoDup_map_to_list.
       - intro. rewrite !elem_of_list_fmap. naive_solver.
       - apply (NoDup_fmap_2 _), NoDup_map_to_list. }
-- 
GitLab