Mercurial > cpdt > repo
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'. |