/
tactic.lean
1899 lines (1617 loc) · 74.3 KB
/
tactic.lean
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/-
Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
prelude
import init.function init.data.option.basic init.util
import init.control.combinators init.control.monad init.control.alternative init.control.monad_fail
import init.data.nat.div init.meta.exceptional init.meta.format init.meta.environment
import init.meta.pexpr init.data.repr init.data.string.basic init.meta.interaction_monad
import init.classical
open native
meta constant tactic_state : Type
universes u v
namespace tactic_state
meta constant env : tactic_state → environment
/-- Format the given tactic state. If `target_lhs_only` is true and the target
is of the form `lhs ~ rhs`, where `~` is a simplification relation,
then only the `lhs` is displayed.
Remark: the parameter `target_lhs_only` is a temporary hack used to implement
the `conv` monad. It will be removed in the future. -/
meta constant to_format (s : tactic_state) (target_lhs_only : bool := ff) : format
/-- Format expression with respect to the main goal in the tactic state.
If the tactic state does not contain any goals, then format expression
using an empty local context. -/
meta constant format_expr : tactic_state → expr → format
meta constant get_options : tactic_state → options
meta constant set_options : tactic_state → options → tactic_state
end tactic_state
meta instance : has_to_format tactic_state :=
⟨tactic_state.to_format⟩
meta instance : has_to_string tactic_state :=
⟨λ s, (to_fmt s).to_string s.get_options⟩
/-- `tactic` is the monad for building tactics.
You use this to:
- View and modify the local goals and hypotheses in the prover's state.
- Invoke type checking and elaboration of terms.
- View and modify the environment.
- Build new tactics out of existing ones such as `simp` and `rewrite`.
-/
@[reducible] meta def tactic := interaction_monad tactic_state
@[reducible] meta def tactic_result := interaction_monad.result tactic_state
namespace tactic
export interaction_monad (hiding failed fail)
/-- Cause the tactic to fail with no error message. -/
meta def failed {α : Type} : tactic α := interaction_monad.failed
meta def fail {α : Type u} {β : Type v} [has_to_format β] (msg : β) : tactic α :=
interaction_monad.fail msg
end tactic
namespace tactic_result
export interaction_monad.result
end tactic_result
open tactic
open tactic_result
infixl ` >>=[tactic] `:2 := interaction_monad_bind
infixl ` >>[tactic] `:2 := interaction_monad_seq
meta instance : alternative tactic :=
{ failure := @interaction_monad.failed _,
orelse := @interaction_monad_orelse _,
..interaction_monad.monad }
meta def {u₁ u₂} tactic.up {α : Type u₂} (t : tactic α) : tactic (ulift.{u₁} α) :=
λ s, match t s with
| success a s' := success (ulift.up a) s'
| exception t ref s := exception t ref s
end
meta def {u₁ u₂} tactic.down {α : Type u₂} (t : tactic (ulift.{u₁} α)) : tactic α :=
λ s, match t s with
| success (ulift.up a) s' := success a s'
| exception t ref s := exception t ref s
end
namespace interactive
/-- Typeclass for custom interaction monads, which provides
the information required to convert an interactive-mode
construction to a `tactic` which can actually be executed.
Given a `[monad m]`, `execute_with` explains how to turn a `begin ... end`
block, or a `by ...` statement into a `tactic α` which can actually be
executed. The `inhabited` first argument facilitates the passing of an
optional configuration parameter `config`, using the syntax:
```
begin [custom_monad] with config,
...
end
```
-/
meta class executor (m : Type → Type u) [monad m] :=
(config_type : Type)
[inhabited : inhabited config_type]
(execute_with : config_type → m unit → tactic unit)
attribute [inline] executor.execute_with
@[inline]
meta def executor.execute_explicit (m : Type → Type u)
[monad m] [e : executor m] : m unit → tactic unit :=
executor.execute_with e.inhabited.default
@[inline]
meta def executor.execute_with_explicit (m : Type → Type u)
[monad m] [executor m] : executor.config_type m → m unit → tactic unit :=
executor.execute_with
/-- Default `executor` instance for `tactic`s themselves -/
meta instance : executor tactic :=
{ config_type := unit,
inhabited := ⟨()⟩,
execute_with := λ _, id }
end interactive
namespace tactic
open interaction_monad.result
variables {α : Type u}
/-- Does nothing. -/
meta def skip : tactic unit :=
success ()
/--
`try_core t` acts like `t`, but succeeds even if `t` fails. It returns the
result of `t` if `t` succeeded and `none` otherwise.
-/
meta def try_core (t : tactic α) : tactic (option α) := λ s,
match t s with
| (exception _ _ _) := success none s
| (success a s') := success (some a) s'
end
/--
`try t` acts like `t`, but succeeds even if `t` fails.
-/
meta def try (t : tactic α) : tactic unit := λ s,
match t s with
| (exception _ _ _) := success () s
| (success _ s') := success () s'
end
meta def try_lst : list (tactic unit) → tactic unit
| [] := failed
| (tac :: tacs) := λ s,
match tac s with
| success _ s' := try (try_lst tacs) s'
| exception e p s' :=
match try_lst tacs s' with
| exception _ _ _ := exception e p s'
| r := r
end
end
/--
`fail_if_success t` acts like `t`, but succeeds if `t` fails and fails if `t`
succeeds. Changes made by `t` to the `tactic_state` are preserved only if `t`
succeeds.
-/
meta def fail_if_success {α : Type u} (t : tactic α) : tactic unit := λ s,
match (t s) with
| (success a s) := mk_exception "fail_if_success combinator failed, given tactic succeeded" none s
| (exception _ _ _) := success () s
end
/--
`success_if_fail t` acts like `t`, but succeeds if `t` fails and fails if `t`
succeeds. Changes made by `t` to the `tactic_state` are preserved only if `t`
succeeds.
-/
meta def success_if_fail {α : Type u} (t : tactic α) : tactic unit := λ s,
match t s with
| (success a s) :=
mk_exception "success_if_fail combinator failed, given tactic succeeded" none s
| (exception _ _ _) := success () s
end
open nat
/--
`iterate_at_most n t` iterates `t` `n` times or until `t` fails, returning the
result of each successful iteration.
-/
meta def iterate_at_most : nat → tactic α → tactic (list α)
| 0 t := pure []
| (n + 1) t := do
(some a) ← try_core t | pure [],
as ← iterate_at_most n t,
pure $ a :: as
/--
`iterate_at_most' n t` repeats `t` `n` times or until `t` fails.
-/
meta def iterate_at_most' : nat → tactic unit → tactic unit
| 0 t := skip
| (succ n) t := do
(some _) ← try_core t | skip,
iterate_at_most' n t
/--
`iterate_exactly n t` iterates `t` `n` times, returning the result of
each iteration. If any iteration fails, the whole tactic fails.
-/
meta def iterate_exactly : nat → tactic α → tactic (list α)
| 0 t := pure []
| (n + 1) t := do
a ← t,
as ← iterate_exactly n t,
pure $ a ::as
/--
`iterate_exactly' n t` executes `t` `n` times. If any iteration fails, the whole
tactic fails.
-/
meta def iterate_exactly' : nat → tactic unit → tactic unit
| 0 t := skip
| (n + 1) t := t *> iterate_exactly' n t
/--
`iterate t` repeats `t` 100.000 times or until `t` fails, returning the
result of each iteration.
-/
meta def iterate : tactic α → tactic (list α) :=
iterate_at_most 100000
/--
`iterate' t` repeats `t` 100.000 times or until `t` fails.
-/
meta def iterate' : tactic unit → tactic unit :=
iterate_at_most' 100000
meta def returnopt (e : option α) : tactic α :=
λ s, match e with
| (some a) := success a s
| none := mk_exception "failed" none s
end
meta instance opt_to_tac : has_coe (option α) (tactic α) :=
⟨returnopt⟩
/-- Decorate t's exceptions with msg. -/
meta def decorate_ex (msg : format) (t : tactic α) : tactic α :=
λ s, result.cases_on (t s)
success
(λ opt_thunk,
match opt_thunk with
| some e := exception (some (λ u, msg ++ format.nest 2 (format.line ++ e u)))
| none := exception none
end)
/-- Set the tactic_state. -/
@[inline] meta def write (s' : tactic_state) : tactic unit :=
λ s, success () s'
/-- Get the tactic_state. -/
@[inline] meta def read : tactic tactic_state :=
λ s, success s s
/--
`capture t` acts like `t`, but succeeds with a result containing either the returned value
or the exception.
Changes made by `t` to the `tactic_state` are preserved in both cases.
The result can be used to inspect the error message, or passed to `unwrap` to rethrow the
failure later.
-/
meta def capture (t : tactic α) : tactic (tactic_result α) :=
λ s, match t s with
| (success r s') := success (success r s') s'
| (exception f p s') := success (exception f p s') s'
end
/--
`unwrap r` unwraps a result previously obtained using `capture`.
If the previous result was a success, this produces its wrapped value.
If the previous result was an exception, this "rethrows" the exception as if it came
from where it originated.
`do r ← capture t, unwrap r` is identical to `t`, but allows for intermediate tactics to be inserted.
-/
meta def unwrap {α : Type*} (t : tactic_result α) : tactic α :=
match t with
| (success r s') := return r
| e := λ s, e
end
/--
`resume r` continues execution from a result previously obtained using `capture`.
This is like `unwrap`, but the `tactic_state` is rolled back to point of capture even upon success.
-/
meta def resume {α : Type*} (t : tactic_result α) : tactic α :=
λ s, t
meta def get_options : tactic options :=
do s ← read, return s.get_options
meta def set_options (o : options) : tactic unit :=
do s ← read, write (s.set_options o)
meta def save_options {α : Type} (t : tactic α) : tactic α :=
do o ← get_options,
a ← t,
set_options o,
return a
meta def returnex {α : Type} (e : exceptional α) : tactic α :=
λ s, match e with
| exceptional.success a := success a s
| exceptional.exception f :=
match get_options s with
| success opt _ := exception (some (λ u, f opt)) none s
| exception _ _ _ := exception (some (λ u, f options.mk)) none s
end
end
meta instance ex_to_tac {α : Type} : has_coe (exceptional α) (tactic α) :=
⟨returnex⟩
end tactic
meta def tactic_format_expr (e : expr) : tactic format :=
do s ← tactic.read, return (tactic_state.format_expr s e)
meta class has_to_tactic_format (α : Type u) :=
(to_tactic_format : α → tactic format)
meta instance : has_to_tactic_format expr :=
⟨tactic_format_expr⟩
meta def tactic.pp {α : Type u} [has_to_tactic_format α] : α → tactic format :=
has_to_tactic_format.to_tactic_format
open tactic format
meta instance {α : Type u} [has_to_tactic_format α] : has_to_tactic_format (list α) :=
⟨λ l, to_fmt <$> l.mmap pp⟩
meta instance (α : Type u) (β : Type v) [has_to_tactic_format α] [has_to_tactic_format β] :
has_to_tactic_format (α × β) :=
⟨λ ⟨a, b⟩, to_fmt <$> (prod.mk <$> pp a <*> pp b)⟩
meta def option_to_tactic_format {α : Type u} [has_to_tactic_format α] : option α → tactic format
| (some a) := do fa ← pp a, return (to_fmt "(some " ++ fa ++ ")")
| none := return "none"
meta instance {α : Type u} [has_to_tactic_format α] : has_to_tactic_format (option α) :=
⟨option_to_tactic_format⟩
meta instance {α} (a : α) : has_to_tactic_format (reflected a) :=
⟨λ h, pp h.to_expr⟩
@[priority 10] meta instance has_to_format_to_has_to_tactic_format (α : Type) [has_to_format α] : has_to_tactic_format α :=
⟨(λ x, return x) ∘ to_fmt⟩
namespace tactic
open tactic_state
meta def get_env : tactic environment :=
do s ← read,
return $ env s
meta def get_decl (n : name) : tactic declaration :=
do s ← read,
(env s).get n
meta constant get_trace_msg_pos : tactic pos
meta def trace {α : Type u} [has_to_tactic_format α] (a : α) : tactic unit :=
do fmt ← pp a,
return $ _root_.trace_fmt fmt (λ u, ())
meta def trace_call_stack : tactic unit :=
assume state, _root_.trace_call_stack (success () state)
meta def timetac {α : Type u} (desc : string) (t : thunk (tactic α)) : tactic α :=
λ s, timeit desc (t () s)
meta def trace_state : tactic unit :=
do s ← read,
trace $ to_fmt s
/-- A parameter representing how aggressively definitions should be unfolded when trying to decide if two terms match, unify or are definitionally equal.
By default, theorem declarations are never unfolded.
- `all` will unfold everything, including macros and theorems. Except projection macros.
- `semireducible` will unfold everything except theorems and definitions tagged as irreducible.
- `instances` will unfold all class instance definitions and definitions tagged with reducible.
- `reducible` will only unfold definitions tagged with the `reducible` attribute.
- `none` will never unfold anything.
[NOTE] You are not allowed to tag a definition with more than one of `reducible`, `irreducible`, `semireducible` attributes.
[NOTE] there is a config flag `m_unfold_lemmas`that will make it unfold theorems.
-/
inductive transparency
| all | semireducible | instances | reducible | none
export transparency (reducible semireducible)
/-- (eval_expr α e) evaluates 'e' IF 'e' has type 'α'. -/
meta constant eval_expr (α : Type u) [reflected α] : expr → tactic α
/-- Return the partial term/proof constructed so far. Note that the resultant expression
may contain variables that are not declarate in the current main goal. -/
meta constant result : tactic expr
/-- Display the partial term/proof constructed so far. This tactic is *not* equivalent to
`do { r ← result, s ← read, return (format_expr s r) }` because this one will format the result with respect
to the current goal, and trace_result will do it with respect to the initial goal. -/
meta constant format_result : tactic format
/-- Return target type of the main goal. Fail if tactic_state does not have any goal left. -/
meta constant target : tactic expr
meta constant intro_core : name → tactic expr
meta constant intron : nat → tactic unit
/-- Clear the given local constant. The tactic fails if the given expression is not a local constant. -/
meta constant clear : expr → tactic unit
/-- `revert_lst : list expr → tactic nat` is the reverse of `intron`. It takes a local constant `c` and puts it back as bound by a `pi` or `elet` of the main target.
If there are other local constants that depend on `c`, these are also reverted. Because of this, the `nat` that is returned is the actual number of reverted local constants.
Example: with `x : ℕ, h : P(x) ⊢ T(x)`, `revert_lst [x]` returns `2` and produces the state ` ⊢ Π x, P(x) → T(x)`.
-/
meta constant revert_lst : list expr → tactic nat
/-- Return `e` in weak head normal form with respect to the given transparency setting.
If `unfold_ginductive` is `tt`, then nested and/or mutually recursive inductive datatype constructors
and types are unfolded. Recall that nested and mutually recursive inductive datatype declarations
are compiled into primitive datatypes accepted by the Kernel. -/
meta constant whnf (e : expr) (md := semireducible) (unfold_ginductive := tt) : tactic expr
/-- (head) eta expand the given expression. `f : α → β` head-eta-expands to `λ a, f a`. If `f` isn't a function then it just returns `f`. -/
meta constant head_eta_expand : expr → tactic expr
/-- (head) beta reduction. `(λ x, B) c` reduces to `B[x/c]`. -/
meta constant head_beta : expr → tactic expr
/-- (head) zeta reduction. Reduction of let bindings at the head of the expression. `let x : a := b in c` reduces to `c[x/b]`. -/
meta constant head_zeta : expr → tactic expr
/-- Zeta reduction. Reduction of let bindings. `let x : a := b in c` reduces to `c[x/b]`. -/
meta constant zeta : expr → tactic expr
/-- (head) eta reduction. `(λ x, f x)` reduces to `f`. -/
meta constant head_eta : expr → tactic expr
/-- Succeeds if `t` and `s` can be unified using the given transparency setting. -/
meta constant unify (t s : expr) (md := semireducible) (approx := ff) : tactic unit
/-- Similar to `unify`, but it treats metavariables as constants. -/
meta constant is_def_eq (t s : expr) (md := semireducible) (approx := ff) : tactic unit
/-- Infer the type of the given expression.
Remark: transparency does not affect type inference -/
meta constant infer_type : expr → tactic expr
/-- Get the `local_const` expr for the given `name`. -/
meta constant get_local : name → tactic expr
/-- Resolve a name using the current local context, environment, aliases, etc. -/
meta constant resolve_name : name → tactic pexpr
/-- Return the hypothesis in the main goal. Fail if tactic_state does not have any goal left. -/
meta constant local_context : tactic (list expr)
/-- Get a fresh name that is guaranteed to not be in use in the local context.
If `n` is provided and `n` is not in use, then `n` is returned.
Otherwise a number `i` is appended to give `"n_i"`.
-/
meta constant get_unused_name (n : name := `_x) (i : option nat := none) : tactic name
/-- Helper tactic for creating simple applications where some arguments are inferred using
type inference.
Example, given
```
rel.{l_1 l_2} : Pi (α : Type.{l_1}) (β : α -> Type.{l_2}), (Pi x : α, β x) -> (Pi x : α, β x) -> , Prop
nat : Type
real : Type
vec.{l} : Pi (α : Type l) (n : nat), Type.{l1}
f g : Pi (n : nat), vec real n
```
then
```
mk_app_core semireducible "rel" [f, g]
```
returns the application
```
rel.{1 2} nat (fun n : nat, vec real n) f g
```
The unification constraints due to type inference are solved using the transparency `md`.
-/
meta constant mk_app (fn : name) (args : list expr) (md := semireducible) : tactic expr
/-- Similar to `mk_app`, but allows to specify which arguments are explicit/implicit.
Example, given `(a b : nat)` then
```
mk_mapp "ite" [some (a > b), none, none, some a, some b]
```
returns the application
```
@ite.{1} nat (a > b) (nat.decidable_gt a b) a b
```
-/
meta constant mk_mapp (fn : name) (args : list (option expr)) (md := semireducible) : tactic expr
/-- (mk_congr_arg h₁ h₂) is a more efficient version of (mk_app `congr_arg [h₁, h₂]) -/
meta constant mk_congr_arg : expr → expr → tactic expr
/-- (mk_congr_fun h₁ h₂) is a more efficient version of (mk_app `congr_fun [h₁, h₂]) -/
meta constant mk_congr_fun : expr → expr → tactic expr
/-- (mk_congr h₁ h₂) is a more efficient version of (mk_app `congr [h₁, h₂]) -/
meta constant mk_congr : expr → expr → tactic expr
/-- (mk_eq_refl h) is a more efficient version of (mk_app `eq.refl [h]) -/
meta constant mk_eq_refl : expr → tactic expr
/-- (mk_eq_symm h) is a more efficient version of (mk_app `eq.symm [h]) -/
meta constant mk_eq_symm : expr → tactic expr
/-- (mk_eq_trans h₁ h₂) is a more efficient version of (mk_app `eq.trans [h₁, h₂]) -/
meta constant mk_eq_trans : expr → expr → tactic expr
/-- (mk_eq_mp h₁ h₂) is a more efficient version of (mk_app `eq.mp [h₁, h₂]) -/
meta constant mk_eq_mp : expr → expr → tactic expr
/-- (mk_eq_mpr h₁ h₂) is a more efficient version of (mk_app `eq.mpr [h₁, h₂]) -/
meta constant mk_eq_mpr : expr → expr → tactic expr
/- Given a local constant t, if t has type (lhs = rhs) apply substitution.
Otherwise, try to find a local constant that has type of the form (t = t') or (t' = t).
The tactic fails if the given expression is not a local constant. -/
meta constant subst_core : expr → tactic unit
/-- Close the current goal using `e`. Fail if the type of `e` is not definitionally equal to
the target type. -/
meta constant exact (e : expr) (md := semireducible) : tactic unit
/-- Elaborate the given quoted expression with respect to the current main goal.
Note that this means that any implicit arguments for the given `pexpr` will be applied with fresh metavariables.
If `allow_mvars` is tt, then metavariables are tolerated and become new goals if `subgoals` is tt. -/
meta constant to_expr (q : pexpr) (allow_mvars := tt) (subgoals := tt) : tactic expr
/-- Return true if the given expression is a type class. -/
meta constant is_class : expr → tactic bool
/-- Try to create an instance of the given type class. -/
meta constant mk_instance : expr → tactic expr
/-- Change the target of the main goal.
The input expression must be definitionally equal to the current target.
If `check` is `ff`, then the tactic does not check whether `e`
is definitionally equal to the current target. If it is not,
then the error will only be detected by the kernel type checker. -/
meta constant change (e : expr) (check : bool := tt): tactic unit
/-- `assert_core H T`, adds a new goal for T, and change target to `T -> target`. -/
meta constant assert_core : name → expr → tactic unit
/-- `assertv_core H T P`, change target to (T -> target) if P has type T. -/
meta constant assertv_core : name → expr → expr → tactic unit
/-- `define_core H T`, adds a new goal for T, and change target to `let H : T := ?M in target` in the current goal. -/
meta constant define_core : name → expr → tactic unit
/-- `definev_core H T P`, change target to `let H : T := P in target` if P has type T. -/
meta constant definev_core : name → expr → expr → tactic unit
/-- Rotate goals to the left. That is, `rotate_left 1` takes the main goal and puts it to the back of the subgoal list. -/
meta constant rotate_left : nat → tactic unit
/-- Gets a list of metavariables, one for each goal. -/
meta constant get_goals : tactic (list expr)
/-- Replace the current list of goals with the given one. Each expr in the list should be a metavariable. Any assigned metavariables will be ignored.-/
meta constant set_goals : list expr → tactic unit
/-- How to order the new goals made from an `apply` tactic.
Supposing we were applying `e : ∀ (a:α) (p : P(a)), Q`
- `non_dep_first` would produce goals `⊢ P(?m)`, `⊢ α`. It puts the P goal at the front because none of the arguments after `p` in `e` depend on `p`. It doesn't matter what the result `Q` depends on.
- `non_dep_only` would produce goal `⊢ P(?m)`.
- `all` would produce goals `⊢ α`, `⊢ P(?m)`.
-/
inductive new_goals
| non_dep_first | non_dep_only | all
/-- Configuration options for the `apply` tactic.
- `md` sets how aggressively definitions are unfolded.
- `new_goals` is the strategy for ordering new goals.
- `instances` if `tt`, then `apply` tries to synthesize unresolved `[...]` arguments using type class resolution.
- `auto_param` if `tt`, then `apply` tries to synthesize unresolved `(h : p . tac_id)` arguments using tactic `tac_id`.
- `opt_param` if `tt`, then `apply` tries to synthesize unresolved `(a : t := v)` arguments by setting them to `v`.
- `unify` if `tt`, then `apply` is free to assign existing metavariables in the goal when solving unification constraints.
For example, in the goal `|- ?x < succ 0`, the tactic `apply succ_lt_succ` succeeds with the default configuration,
but `apply_with succ_lt_succ {unify := ff}` doesn't since it would require Lean to assign `?x` to `succ ?y` where
`?y` is a fresh metavariable.
-/
structure apply_cfg :=
(md := semireducible)
(approx := tt)
(new_goals := new_goals.non_dep_first)
(instances := tt)
(auto_param := tt)
(opt_param := tt)
(unify := tt)
/-- Apply the expression `e` to the main goal, the unification is performed using the transparency mode in `cfg`.
Supposing `e : Π (a₁:α₁) ... (aₙ:αₙ), P(a₁,...,aₙ)` and the target is `Q`, `apply` will attempt to unify `Q` with `P(?a₁,...?aₙ)`.
All of the metavariables that are not assigned are added as new metavariables.
If `cfg.approx` is `tt`, then fallback to first-order unification, and approximate context during unification.
`cfg.new_goals` specifies which unassigned metavariables become new goals, and their order.
If `cfg.instances` is `tt`, then use type class resolution to instantiate unassigned meta-variables.
The fields `cfg.auto_param` and `cfg.opt_param` are ignored by this tactic (See `tactic.apply`).
It returns a list of all introduced meta variables and the parameter name associated with them, even the assigned ones. -/
meta constant apply_core (e : expr) (cfg : apply_cfg := {}) : tactic (list (name × expr))
/- Create a fresh meta universe variable. -/
meta constant mk_meta_univ : tactic level
/- Create a fresh meta-variable with the given type.
The scope of the new meta-variable is the local context of the main goal. -/
meta constant mk_meta_var : expr → tactic expr
/-- Return the value assigned to the given universe meta-variable.
Fail if argument is not an universe meta-variable or if it is not assigned. -/
meta constant get_univ_assignment : level → tactic level
/-- Return the value assigned to the given meta-variable.
Fail if argument is not a meta-variable or if it is not assigned. -/
meta constant get_assignment : expr → tactic expr
/-- Return true if the given meta-variable is assigned.
Fail if argument is not a meta-variable. -/
meta constant is_assigned : expr → tactic bool
/-- Make a name that is guaranteed to be unique. Eg `_fresh.1001.4667`. These will be different for each run of the tactic. -/
meta constant mk_fresh_name : tactic name
/-- Induction on `h` using recursor `rec`, names for the new hypotheses
are retrieved from `ns`. If `ns` does not have sufficient names, then use the internal binder names
in the recursor.
It returns for each new goal the name of the constructor (if `rec_name` is a builtin recursor),
a list of new hypotheses, and a list of substitutions for hypotheses
depending on `h`. The substitutions map internal names to their replacement terms. If the
replacement is again a hypothesis the user name stays the same. The internal names are only valid
in the original goal, not in the type context of the new goal.
Remark: if `rec_name` is not a builtin recursor, we use parameter names of `rec_name` instead of
constructor names.
If `rec` is none, then the type of `h` is inferred, if it is of the form `C ...`, tactic uses `C.rec` -/
meta constant induction (h : expr) (ns : list name := []) (rec : option name := none) (md := semireducible) : tactic (list (name × list expr × list (name × expr)))
/-- Apply `cases_on` recursor, names for the new hypotheses are retrieved from `ns`.
`h` must be a local constant. It returns for each new goal the name of the constructor, a list of new hypotheses, and a list of
substitutions for hypotheses depending on `h`. The number of new goals may be smaller than the
number of constructors. Some goals may be discarded when the indices to not match.
See `induction` for information on the list of substitutions.
The `cases` tactic is implemented using this one, and it relaxes the restriction of `h`.
Note: There is one "new hypothesis" for every constructor argument. These are
usually local constants, but due to dependent pattern matching, they can also
be arbitrary terms. -/
meta constant cases_core (h : expr) (ns : list name := []) (md := semireducible) : tactic (list (name × list expr × list (name × expr)))
/-- Similar to cases tactic, but does not revert/intro/clear hypotheses. -/
meta constant destruct (e : expr) (md := semireducible) : tactic unit
/-- Generalizes the target with respect to `e`. -/
meta constant generalize (e : expr) (n : name := `_x) (md := semireducible) : tactic unit
/-- instantiate assigned metavariables in the given expression -/
meta constant instantiate_mvars : expr → tactic expr
/-- Add the given declaration to the environment -/
meta constant add_decl : declaration → tactic unit
/--
Changes the environment to the `new_env`.
The new environment does not need to be a descendant of the old one.
Use with care.
-/
meta constant set_env_core : environment → tactic unit
/-- Changes the environment to the `new_env`. `new_env` needs to be a descendant from the current environment. -/
meta constant set_env : environment → tactic unit
/-- `doc_string env d k` returns the doc string for `d` (if available) -/
meta constant doc_string : name → tactic string
/-- Set the docstring for the given declaration. -/
meta constant add_doc_string : name → string → tactic unit
/--
Create an auxiliary definition with name `c` where `type` and `value` may contain local constants and
meta-variables. This function collects all dependencies (universe parameters, universe metavariables,
local constants (aka hypotheses) and metavariables).
It updates the environment in the tactic_state, and returns an expression of the form
(c.{l_1 ... l_n} a_1 ... a_m)
where l_i's and a_j's are the collected dependencies.
-/
meta constant add_aux_decl (c : name) (type : expr) (val : expr) (is_lemma : bool) : tactic expr
/-- Returns a list of all top-level (`/-! ... -/`) docstrings in the active module and imported ones.
The returned object is a list of modules, indexed by `(some filename)` for imported modules
and `none` for the active one, where each module in the list is paired with a list
of `(position_in_file, docstring)` pairs. -/
meta constant olean_doc_strings : tactic (list (option string × (list (pos × string))))
/-- Returns a list of docstrings in the active module. An entry in the list can be either:
- a top-level (`/-! ... -/`) docstring, represented as `(none, docstring)`
- a declaration-specific (`/-- ... -/`) docstring, represented as `(some decl_name, docstring)` -/
meta def module_doc_strings : tactic (list (option name × string)) :=
do
/- Obtain a list of top-level docs in current module. -/
mod_docs ← olean_doc_strings,
let mod_docs: list (list (option name × string)) :=
mod_docs.filter_map (λ d,
if d.1.is_none
then some (d.2.map
(λ pos_doc, ⟨none, pos_doc.2⟩))
else none),
let mod_docs := mod_docs.join,
/- Obtain list of declarations in current module. -/
e ← get_env,
let decls := environment.fold e ([]: list name)
(λ d acc, let n := d.to_name in
if (environment.decl_olean e n).is_none
then n::acc else acc),
/- Map declarations to those which have docstrings. -/
decls ← decls.mfoldl (λa n,
(doc_string n >>=
λ doc, pure $ (some n, doc) :: a)
<|> pure a) [],
pure (mod_docs ++ decls)
/-- Set attribute `attr_name` for constant `c_name` with the given priority.
If the priority is none, then use default -/
meta constant set_basic_attribute (attr_name : name) (c_name : name) (persistent := ff) (prio : option nat := none) : tactic unit
/-- `unset_attribute attr_name c_name` -/
meta constant unset_attribute : name → name → tactic unit
/-- `has_attribute attr_name c_name` succeeds if the declaration `decl_name`
has the attribute `attr_name`. The result is the priority and whether or not
the attribute is persistent. -/
meta constant has_attribute : name → name → tactic (bool × nat)
/-- `copy_attribute attr_name c_name p d_name` copy attribute `attr_name` from
`src` to `tgt` if it is defined for `src`; make it persistent if `p` is `tt`;
if `p` is `none`, the copied attribute is made persistent iff it is persistent on `src` -/
meta def copy_attribute (attr_name : name) (src : name) (tgt : name) (p : option bool := none) : tactic unit :=
try $ do
(p', prio) ← has_attribute attr_name src,
let p := p.get_or_else p',
set_basic_attribute attr_name tgt p (some prio)
/-- Name of the declaration currently being elaborated. -/
meta constant decl_name : tactic name
/-- `save_type_info e ref` save (typeof e) at position associated with ref -/
meta constant save_type_info {elab : bool} : expr → expr elab → tactic unit
meta constant save_info_thunk : pos → (unit → format) → tactic unit
/-- Return list of currently open namespaces -/
meta constant open_namespaces : tactic (list name)
/-- Return tt iff `t` "occurs" in `e`. The occurrence checking is performed using
keyed matching with the given transparency setting.
We say `t` occurs in `e` by keyed matching iff there is a subterm `s`
s.t. `t` and `s` have the same head, and `is_def_eq t s md`
The main idea is to minimize the number of `is_def_eq` checks
performed. -/
meta constant kdepends_on (e t : expr) (md := reducible) : tactic bool
/-- Abstracts all occurrences of the term `t` in `e` using keyed matching.
If `unify` is `ff`, then matching is used instead of unification.
That is, metavariables occurring in `e` are not assigned. -/
meta constant kabstract (e t : expr) (md := reducible) (unify := tt) : tactic expr
/-- Blocks the execution of the current thread for at least `msecs` milliseconds.
This tactic is used mainly for debugging purposes. -/
meta constant sleep (msecs : nat) : tactic unit
/-- Type check `e` with respect to the current goal.
Fails if `e` is not type correct. -/
meta constant type_check (e : expr) (md := semireducible) : tactic unit
open list nat
/-- A `tag` is a list of `names`. These are attached to goals to help tactics track them.-/
def tag : Type := list name
/-- Enable/disable goal tagging. -/
meta constant enable_tags (b : bool) : tactic unit
/-- Return tt iff goal tagging is enabled. -/
meta constant tags_enabled : tactic bool
/-- Tag goal `g` with tag `t`. It does nothing if goal tagging is disabled.
Remark: `set_goal g []` removes the tag -/
meta constant set_tag (g : expr) (t : tag) : tactic unit
/-- Return tag associated with `g`. Return `[]` if there is no tag. -/
meta constant get_tag (g : expr) : tactic tag
/-- By default, Lean only considers local instances in the header of declarations.
This has two main benefits.
1- Results produced by the type class resolution procedure can be easily cached.
2- The set of local instances does not have to be recomputed.
This approach has the following disadvantages:
1- Frozen local instances cannot be reverted.
2- Local instances defined inside of a declaration are not considered during type
class resolution.
This tactic resets the set of local instances. After executing this tactic,
the set of local instances will be recomputed and the cache will be frequently
reset. Note that, the cache is still used when executing a single tactic that
may generate many type class resolution problems (e.g., `simp`). -/
meta constant unfreeze_local_instances : tactic unit
/--
Freeze the current set of local instances.
-/
meta constant freeze_local_instances : tactic unit
/- Return the list of frozen local instances. Return `none` if local instances were not frozen. -/
meta constant frozen_local_instances : tactic (option (list expr))
meta def induction' (h : expr) (ns : list name := []) (rec : option name := none) (md := semireducible) : tactic unit :=
induction h ns rec md >> return ()
/-- Remark: set_goals will erase any solved goal -/
meta def cleanup : tactic unit :=
get_goals >>= set_goals
/-- Auxiliary definition used to implement begin ... end blocks -/
meta def step {α : Type u} (t : tactic α) : tactic unit :=
t >>[tactic] cleanup
meta def istep {α : Type u} (line0 col0 : ℕ) (line col : ℕ) (t : tactic α) : tactic unit :=
λ s, (@scope_trace _ line col (λ _, step t s)).clamp_pos line0 line col
meta def is_prop (e : expr) : tactic bool :=
do t ← infer_type e,
return (t = `(Prop))
/-- Return true iff n is the name of declaration that is a proposition. -/
meta def is_prop_decl (n : name) : tactic bool :=
do env ← get_env,
d ← env.get n,
t ← return $ d.type,
is_prop t
meta def is_proof (e : expr) : tactic bool :=
infer_type e >>= is_prop
meta def whnf_no_delta (e : expr) : tactic expr :=
whnf e transparency.none
/-- Return `e` in weak head normal form with respect to the given transparency setting,
or `e` head is a generalized constructor or inductive datatype. -/
meta def whnf_ginductive (e : expr) (md := semireducible) : tactic expr :=
whnf e md ff
meta def whnf_target : tactic unit :=
target >>= whnf >>= change
/-- Change the target of the main goal.
The input expression must be definitionally equal to the current target.
The tactic does not check whether `e`
is definitionally equal to the current target. The error will only be detected by the kernel type checker. -/
meta def unsafe_change (e : expr) : tactic unit :=
change e ff
/-- Pi or elet introduction.
Given the tactic state `⊢ Π x : α, Y`, ``intro `hello`` will produce the state `hello : α ⊢ Y[x/hello]`.
Returns the new local constant. Similarly for `elet` expressions.
If the target is not a Pi or elet it will try to put it in WHNF.
-/
meta def intro (n : name) : tactic expr :=
do t ← target,
if expr.is_pi t ∨ expr.is_let t then intro_core n
else whnf_target >> intro_core n
/--
A variant of `intro` which makes sure that the introduced hypothesis's name is
unique in the context. If there is no hypothesis named `n` in the context yet,
`intro_fresh n` is the same as `intro n`. If there is already a hypothesis named
`n`, the new hypothesis is named `n_1` (or `n_2` if `n_1` already exists, etc.).
If `offset` is given, the new names are `n_offset`, `n_offset+1` etc.
If `n` is `_`, `intro_fresh n` is the same as `intro1`. The `offset` is ignored
in this case.
-/
meta def intro_fresh (n : name) (offset : option nat := none) : tactic expr :=
if n = `_
then intro `_
else do
n ← get_unused_name n offset,
intro n
/-- Like `intro` except the name is derived from the bound name in the Π. -/
meta def intro1 : tactic expr :=
intro `_
/-- Repeatedly apply `intro1` and return the list of new local constants in order of introduction. -/
meta def intros : tactic (list expr) :=
do t ← target,
match t with
| expr.pi _ _ _ _ := do H ← intro1, Hs ← intros, return (H :: Hs)
| expr.elet _ _ _ _ := do H ← intro1, Hs ← intros, return (H :: Hs)
| _ := return []
end
/-- Same as `intros`, except with the given names for the new hypotheses. Use the name ```_``` to instead use the binder's name.-/
meta def intro_lst (ns : list name) : tactic (list expr) :=
ns.mmap intro
/--
A variant of `intro_lst` which makes sure that the introduced hypotheses' names
are unique in the context. See `intro_fresh`.
-/
meta def intro_lst_fresh (ns : list name) : tactic (list expr) :=
ns.mmap intro_fresh
/-- Introduces new hypotheses with forward dependencies. -/
meta def intros_dep : tactic (list expr) :=
do t ← target,
let proc (b : expr) :=
if b.has_var_idx 0 then
do h ← intro1, hs ← intros_dep, return (h::hs)
else
-- body doesn't depend on new hypothesis
return [],
match t with
| expr.pi _ _ _ b := proc b
| expr.elet _ _ _ b := proc b
| _ := return []
end
meta def introv : list name → tactic (list expr)
| [] := intros_dep
| (n::ns) := do hs ← intros_dep, h ← intro n, hs' ← introv ns, return (hs ++ h :: hs')
/--
`intron' n` introduces `n` hypotheses and returns the resulting local
constants. Fails if there are not at least `n` arguments to introduce. If you do
not need the return value, use `intron`.
-/
meta def intron' (n : ℕ) : tactic (list expr)
:= iterate_exactly n intro1
/--
Like `intron'` but the introduced hypotheses' names are derived from `base`,
i.e. `base`, `base_1` etc. The new names are unique in the context. If `offset`
is given, the new names will be `base_offset`, `base_offset+1` etc.
-/
meta def intron_base (n : ℕ) (base : name) (offset : option nat := none)
: tactic (list expr)
:= iterate_exactly n (intro_fresh base offset)
/--
`intron_with i ns base offset` introduces `i` hypotheses using the names from
`ns`. If `ns` contains less than `i` names, the remaining hypotheses' names are
derived from `base` and `offset` (as with `intron_base`). If `base` is `_`, the
names are derived from the Π binder names.
Returns the introduced local constants and the remaining names from `ns` (if
`ns` contains more than `i` names).
-/
meta def intron_with
: ℕ → list name → opt_param name `_ → opt_param (option ℕ) none
→ tactic (list expr × list name)
| 0 ns _ _ := pure ([], ns)
| (i + 1) [] base offset := do
hs ← intron_base (i + 1) base offset,
pure (hs, [])
| (i + 1) (n :: ns) base offset := do
h ← intro n,
⟨hs, rest⟩ ← intron_with i ns base offset,
pure (h :: hs, rest)
/-- Returns n fully qualified if it refers to a constant, or else fails. -/
meta def resolve_constant (n : name) : tactic name :=
do e ← resolve_name n,
match e with
| expr.const n _ := pure n
| _ := do
e ← to_expr e tt ff,
expr.const n _ ← pure $ e.get_app_fn,
pure n
end
meta def to_expr_strict (q : pexpr) : tactic expr :=
to_expr q
/--
Example: with `x : ℕ, h : P(x) ⊢ T(x)`, `revert x` returns `2` and produces the state ` ⊢ Π x, P(x) → T(x)`.
-/
meta def revert (l : expr) : tactic nat :=
revert_lst [l]
/- Revert "all" hypotheses. Actually, the tactic only reverts
hypotheses occurring after the last frozen local instance.
Recall that frozen local instances cannot be reverted.
We can use `unfreeze_local_instances` to workaround this limitation. -/
meta def revert_all : tactic nat :=
do lctx ← local_context,
lis ← frozen_local_instances,
match lis with
| none := revert_lst lctx
| some [] := revert_lst lctx
/- `hi` is the last local instance. We shoul truncate `lctx` at `hi`. -/
| some (hi::his) := revert_lst $ lctx.foldl (λ r h, if h.local_uniq_name = hi.local_uniq_name then [] else h :: r) []
end
meta def clear_lst : list name → tactic unit
| [] := skip
| (n::ns) := do H ← get_local n, clear H, clear_lst ns
meta def match_not (e : expr) : tactic expr :=
match (expr.is_not e) with
| (some a) := return a
| none := fail "expression is not a negation"
end
meta def match_and (e : expr) : tactic (expr × expr) :=
match (expr.is_and e) with
| (some (α, β)) := return (α, β)
| none := fail "expression is not a conjunction"
end
meta def match_or (e : expr) : tactic (expr × expr) :=
match (expr.is_or e) with
| (some (α, β)) := return (α, β)
| none := fail "expression is not a disjunction"
end
meta def match_iff (e : expr) : tactic (expr × expr) :=
match (expr.is_iff e) with
| (some (lhs, rhs)) := return (lhs, rhs)
| none := fail "expression is not an iff"
end
meta def match_eq (e : expr) : tactic (expr × expr) :=
match (expr.is_eq e) with
| (some (lhs, rhs)) := return (lhs, rhs)
| none := fail "expression is not an equality"
end