comparison src/CpdtTactics.v @ 389:76e1dfcb86eb

Comment CpdtTactics.v
author Adam Chlipala <adam@chlipala.net>
date Thu, 12 Apr 2012 18:41:32 -0400
parents d1276004eec9
children 539ed97750bb
comparison
equal deleted inserted replaced
388:057a29f9c773 389:76e1dfcb86eb
1 (* Copyright (c) 2008-2011, Adam Chlipala 1 (* Copyright (c) 2008-2012, Adam Chlipala
2 * 2 *
3 * This work is licensed under a 3 * This work is licensed under a
4 * Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 4 * Creative Commons Attribution-Noncommercial-No Derivative Works 3.0
5 * Unported License. 5 * Unported License.
6 * The license text is available at: 6 * The license text is available at:
12 Require Omega. 12 Require Omega.
13 13
14 Set Implicit Arguments. 14 Set Implicit Arguments.
15 15
16 16
17 (** A version of [injection] that does some standard simplifications afterward: clear the hypothesis in question, bring the new facts above the double line, and attempt substitution for known variables. *)
17 Ltac inject H := injection H; clear H; intros; try subst. 18 Ltac inject H := injection H; clear H; intros; try subst.
18 19
20 (** Try calling tactic function [f] on all hypotheses, keeping the first application that doesn't fail. *)
19 Ltac appHyps f := 21 Ltac appHyps f :=
20 match goal with 22 match goal with
21 | [ H : _ |- _ ] => f H 23 | [ H : _ |- _ ] => f H
22 end. 24 end.
23 25
26 (** Succeed iff [x] is in the list [ls], represented with left-associated nested tuples. *)
24 Ltac inList x ls := 27 Ltac inList x ls :=
25 match ls with 28 match ls with
26 | x => idtac 29 | x => idtac
27 | (_, x) => idtac 30 | (_, x) => idtac
28 | (?LS, _) => inList x LS 31 | (?LS, _) => inList x LS
29 end. 32 end.
30 33
34 (** Try calling tactic function [f] on every element of tupled list [ls], keeping the first call not to fail. *)
31 Ltac app f ls := 35 Ltac app f ls :=
32 match ls with 36 match ls with
33 | (?LS, ?X) => f X || app f LS || fail 1 37 | (?LS, ?X) => f X || app f LS || fail 1
34 | _ => f ls 38 | _ => f ls
35 end. 39 end.
36 40
41 (** Run [f] on every element of [ls], not just the first that doesn't fail. *)
37 Ltac all f ls := 42 Ltac all f ls :=
38 match ls with 43 match ls with
39 | (?LS, ?X) => f X; all f LS 44 | (?LS, ?X) => f X; all f LS
40 | (_, _) => fail 1 45 | (_, _) => fail 1
41 | _ => f ls 46 | _ => f ls
42 end. 47 end.
43 48
49 (** Workhorse tactic to simplify hypotheses for a variety of proofs.
50 * Argument [invOne] is a tuple-list of predicates for which we always do inversion automatically. *)
44 Ltac simplHyp invOne := 51 Ltac simplHyp invOne :=
52 (** Helper function to do inversion on certain hypotheses, where [H] is the hypothesis and [F] its head symbol *)
45 let invert H F := 53 let invert H F :=
46 inList F invOne; (inversion H; fail) 54 (** We only proceed for those predicates in [invOne]. *)
47 || (inversion H; [idtac]; clear H; try subst) in 55 inList F invOne;
56 (** This case covers an inversion that succeeds immediately, meaning no constructors of [F] applied. *)
57 (inversion H; fail)
58 (** Otherwise, we only proceed if inversion eliminates all but one constructor case. *)
59 || (inversion H; [idtac]; clear H; try subst) in
60
48 match goal with 61 match goal with
62 (** Eliminate all existential hypotheses. *)
49 | [ H : ex _ |- _ ] => destruct H 63 | [ H : ex _ |- _ ] => destruct H
50 64
65 (** Find opportunities to take advantage of injectivity of data constructors, for several different arities. *)
51 | [ H : ?F ?X = ?F ?Y |- ?G ] => 66 | [ H : ?F ?X = ?F ?Y |- ?G ] =>
67 (** This first branch of the [||] fails the whole attempt iff the arguments of the constructor applications are already easy to prove equal. *)
52 (assert (X = Y); [ assumption | fail 1 ]) 68 (assert (X = Y); [ assumption | fail 1 ])
69 (** If we pass that filter, then we use injection on [H] and do some simplification as in [inject].
70 * The odd-looking check of the goal form is to avoid cases where [injection] gives a more complex result because of dependent typing, which we aren't equipped to handle here. *)
53 || (injection H; 71 || (injection H;
54 match goal with 72 match goal with
55 | [ |- X = Y -> G ] => 73 | [ |- X = Y -> G ] =>
56 try clear H; intros; try subst 74 try clear H; intros; try subst
57 end) 75 end)
62 match goal with 80 match goal with
63 | [ |- U = V -> X = Y -> G ] => 81 | [ |- U = V -> X = Y -> G ] =>
64 try clear H; intros; try subst 82 try clear H; intros; try subst
65 end) 83 end)
66 84
85 (** Consider some different arities of a predicate [F] in a hypothesis that we might want to invert. *)
67 | [ H : ?F _ |- _ ] => invert H F 86 | [ H : ?F _ |- _ ] => invert H F
68 | [ H : ?F _ _ |- _ ] => invert H F 87 | [ H : ?F _ _ |- _ ] => invert H F
69 | [ H : ?F _ _ _ |- _ ] => invert H F 88 | [ H : ?F _ _ _ |- _ ] => invert H F
70 | [ H : ?F _ _ _ _ |- _ ] => invert H F 89 | [ H : ?F _ _ _ _ |- _ ] => invert H F
71 | [ H : ?F _ _ _ _ _ |- _ ] => invert H F 90 | [ H : ?F _ _ _ _ _ |- _ ] => invert H F
72 91
92 (** Use an (axiom-dependent!) inversion principle for dependent pairs, from the standard library. *)
73 | [ H : existT _ ?T _ = existT _ ?T _ |- _ ] => generalize (inj_pair2 _ _ _ _ _ H); clear H 93 | [ H : existT _ ?T _ = existT _ ?T _ |- _ ] => generalize (inj_pair2 _ _ _ _ _ H); clear H
94
95 (** If we're not ready to use that principle yet, try the standard inversion, which often enables the previous rule. *)
74 | [ H : existT _ _ _ = existT _ _ _ |- _ ] => inversion H; clear H 96 | [ H : existT _ _ _ = existT _ _ _ |- _ ] => inversion H; clear H
75 97
98 (** Similar logic to the cases for constructor injectivity above, but specialized to [Some], since the above cases won't deal with polymorphic constructors. *)
76 | [ H : Some _ = Some _ |- _ ] => injection H; clear H 99 | [ H : Some _ = Some _ |- _ ] => injection H; clear H
77 end. 100 end.
78 101
102 (** Find some hypothesis to rewrite with, ensuring that [auto] proves all of the extra subgoals added by [rewrite]. *)
79 Ltac rewriteHyp := 103 Ltac rewriteHyp :=
80 match goal with 104 match goal with
81 | [ H : _ |- _ ] => rewrite H; auto; [idtac] 105 | [ H : _ |- _ ] => rewrite H by solve [ auto ]
82 end. 106 end.
83 107
108 (** Combine [autorewrite] with automatic hypothesis rewrites. *)
84 Ltac rewriterP := repeat (rewriteHyp; autorewrite with core in *). 109 Ltac rewriterP := repeat (rewriteHyp; autorewrite with core in *).
85
86 Ltac rewriter := autorewrite with core in *; rewriterP. 110 Ltac rewriter := autorewrite with core in *; rewriterP.
87 111
112 (** This one is just so darned useful, let's add it as a hint here. *)
88 Hint Rewrite app_ass. 113 Hint Rewrite app_ass.
89 114
115 (** Devious marker predicate to use for encoding state within proof goals *)
90 Definition done (T : Type) (x : T) := True. 116 Definition done (T : Type) (x : T) := True.
91 117
118 (** Try a new instantiation of a universally quantified fact, proved by [e].
119 * [trace] is an accumulator recording which instantiations we choose. *)
92 Ltac inster e trace := 120 Ltac inster e trace :=
121 (** Does [e] have any quantifiers left? *)
93 match type of e with 122 match type of e with
94 | forall x : _, _ => 123 | forall x : _, _ =>
124 (** Yes, so let's pick the first context variable of the right type. *)
95 match goal with 125 match goal with
96 | [ H : _ |- _ ] => 126 | [ H : _ |- _ ] =>
97 inster (e H) (trace, H) 127 inster (e H) (trace, H)
98 | _ => fail 2 128 | _ => fail 2
99 end 129 end
100 | _ => 130 | _ =>
131 (** No more quantifiers, so now we check if the trace we computed was already used. *)
101 match trace with 132 match trace with
102 | (_, _) => 133 | (_, _) =>
134 (** We only reach this case if the trace is nonempty, ensuring that [inster] fails if no progress can be made. *)
103 match goal with 135 match goal with
104 | [ H : done (trace, _) |- _ ] => fail 1 136 | [ H : done (trace, _) |- _ ] =>
137 (** Uh oh, found a record of this trace in the context! Abort to backtrack to try another trace. *)
138 fail 1
105 | _ => 139 | _ =>
140 (** What is the type of the proof [e] now? *)
106 let T := type of e in 141 let T := type of e in
107 match type of T with 142 match type of T with
108 | Prop => 143 | Prop =>
144 (** [e] should be thought of as a proof, so let's add it to the context, and also add a new marker hypothesis recording our choice of trace. *)
109 generalize e; intro; 145 generalize e; intro;
110 assert (done (trace, tt)); [constructor | idtac] 146 assert (done (trace, tt)) by constructor
111 | _ => 147 | _ =>
148 (** [e] is something beside a proof. Better make sure no element of our current trace was generated by a previous call to [inster], or we might get stuck in an infinite loop! (We store previous [inster] terms in second positions of tuples used as arguments to [done] in hypotheses. Proofs instantiated by [inster] merely use [tt] in such positions.) *)
112 all ltac:(fun X => 149 all ltac:(fun X =>
113 match goal with 150 match goal with
114 | [ H : done (_, X) |- _ ] => fail 1 151 | [ H : done (_, X) |- _ ] => fail 1
115 | _ => idtac 152 | _ => idtac
116 end) trace; 153 end) trace;
154 (** Pick a new name for our new instantiation. *)
117 let i := fresh "i" in (pose (i := e); 155 let i := fresh "i" in (pose (i := e);
118 assert (done (trace, i)); [constructor | idtac]) 156 assert (done (trace, i)) by constructor)
119 end 157 end
120 end 158 end
121 end 159 end
122 end. 160 end.
123 161
162 (** After a round of application with the above, we will have a lot of junk [done] markers to clean up; hence this tactic. *)
124 Ltac un_done := 163 Ltac un_done :=
125 repeat match goal with 164 repeat match goal with
126 | [ H : done _ |- _ ] => clear H 165 | [ H : done _ |- _ ] => clear H
127 end. 166 end.
128 167
129 Require Import JMeq. 168 Require Import JMeq.
130 169
170 (** A more parameterized version of the famous [crush]. Extra arguments are:
171 * - A tuple-list of lemmas we try [inster]-ing
172 * - A tuple-list of predicates we try inversion for *)
131 Ltac crush' lemmas invOne := 173 Ltac crush' lemmas invOne :=
132 let sintuition := simpl in *; intuition; try subst; repeat (simplHyp invOne; intuition; try subst); try congruence in 174 (** A useful combination of standard automation *)
133 let rewriter := autorewrite with core in *; 175 let sintuition := simpl in *; intuition; try subst;
134 repeat (match goal with 176 repeat (simplHyp invOne; intuition; try subst); try congruence in
135 | [ H : ?P |- _ ] => 177
136 match P with 178 (** A fancier version of [rewriter] from above, which uses [crush'] to discharge side conditions *)
137 | context[JMeq] => fail 1 179 let rewriter := autorewrite with core in *;
138 | _ => (rewrite H; []) 180 repeat (match goal with
139 || (rewrite H; [ | solve [ crush' lemmas invOne ] ]) 181 | [ H : ?P |- _ ] =>
140 || (rewrite H; [ | solve [ crush' lemmas invOne ] | solve [ crush' lemmas invOne ] ]) 182 match P with
141 end 183 | context[JMeq] => fail 1 (** JMeq is too fancy to deal with here. *)
142 end; autorewrite with core in *) 184 | _ => rewrite H by crush' lemmas invOne
143 in (sintuition; rewriter; 185 end
144 match lemmas with 186 end; autorewrite with core in *) in
145 | false => idtac 187
146 | _ => repeat ((app ltac:(fun L => inster L L) lemmas || appHyps ltac:(fun L => inster L L)); 188 (** Now the main sequence of heuristics: *)
147 repeat (simplHyp invOne; intuition)); un_done 189 (sintuition; rewriter;
148 end; sintuition; rewriter; sintuition; try omega; try (elimtype False; omega)). 190 match lemmas with
149 191 | false => idtac (** No lemmas? Nothing to do here *)
192 | _ =>
193 (** Try a loop of instantiating lemmas... *)
194 repeat ((app ltac:(fun L => inster L L) lemmas
195 (** ...or instantiating hypotheses... *)
196 || appHyps ltac:(fun L => inster L L));
197 (** ...and then simplifying hypotheses. *)
198 repeat (simplHyp invOne; intuition)); un_done
199 end;
200 sintuition; rewriter; sintuition;
201 (** End with a last attempt to prove an arithmetic fact with [omega], or prove any sort of fact in a context that is contradictory by reasoning that [omega] can do. *)
202 try omega; try (elimtype False; omega)).
203
204 (** [crush] instantiates [crush'] with the simplest possible parameters. *)
150 Ltac crush := crush' false fail. 205 Ltac crush := crush' false fail.
151 206
207 (** * Wrap Program's [dependent destruction] in a slightly more pleasant form *)
208
152 Require Import Program.Equality. 209 Require Import Program.Equality.
153 210
211 (** Run [dependent destruction] on [E] and look for opportunities to simplify the result.
212 The weird introduction of [x] helps get around limitations of [dependent destruction], in terms of which sorts of arguments it will accept (e.g., variables bound to hypotheses within Ltac [match]es). *)
154 Ltac dep_destruct E := 213 Ltac dep_destruct E :=
155 let x := fresh "x" in 214 let x := fresh "x" in
156 remember E as x; simpl in x; dependent destruction x; 215 remember E as x; simpl in x; dependent destruction x;
157 try match goal with 216 try match goal with
158 | [ H : _ = E |- _ ] => rewrite <- H in *; clear H 217 | [ H : _ = E |- _ ] => rewrite <- H in *; clear H
159 end. 218 end.
160 219
220 (** Nuke all hypotheses that we can get away with, without invalidating the goal statement. *)
161 Ltac clear_all := 221 Ltac clear_all :=
162 repeat match goal with 222 repeat match goal with
163 | [ H : _ |- _ ] => clear H 223 | [ H : _ |- _ ] => clear H
164 end. 224 end.
165 225
226 (** Instantiate a quantifier in a hypothesis [H] with value [v], or, if [v] doesn't have the right type, with a new unification variable.
227 * Also prove the lefthand sides of any implications that this exposes, simplifying [H] to leave out those implications. *)
166 Ltac guess v H := 228 Ltac guess v H :=
167 repeat match type of H with 229 repeat match type of H with
168 | forall x : ?T, _ => 230 | forall x : ?T, _ =>
169 match type of T with 231 match type of T with
170 | Prop => 232 | Prop =>
171 (let H' := fresh "H'" in 233 (let H' := fresh "H'" in
172 assert (H' : T); [ 234 assert (H' : T); [
173 solve [ eauto 6 ] 235 solve [ eauto 6 ]
174 | generalize (H H'); clear H H'; intro H ]) 236 | specialize (H H'); clear H' ])
175 || fail 1 237 || fail 1
176 | _ => 238 | _ =>
177 (generalize (H v); clear H; intro H) 239 specialize (H v)
178 || let x := fresh "x" in 240 || let x := fresh "x" in
179 evar (x : T); 241 evar (x : T);
180 let x' := eval cbv delta [x] in x in 242 let x' := eval unfold x in x in
181 clear x; generalize (H x'); clear H; intro H 243 clear x; specialize (H x')
182 end 244 end
183 end. 245 end.
184 246
247 (** Version of [guess] that leaves the original [H] intact *)
185 Ltac guessKeep v H := 248 Ltac guessKeep v H :=
186 let H' := fresh "H'" in 249 let H' := fresh "H'" in
187 generalize H; intro H'; guess v H'. 250 generalize H; intro H'; guess v H'.