Source file tactic.ml
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
(** Handling of tactics. *)
open Lplib open Extra
open Common open Error open Pos
open Parsing open Syntax
open Core open Term open Print
open Proof
open Timed
(** Logging function for tactics. *)
let log = Logger.make 't' "tact" "tactics"
let log = log.pp
(** Number of admitted axioms in the current signature. Used to name the
generated axioms. This reference is reset in {!module:Compile} for each
new compiled module. *)
let admitted_initial_value = min_int
let admitted : int Stdlib.ref = Stdlib.ref admitted_initial_value
let reset_admitted() = Stdlib.(admitted := admitted_initial_value)
(** [add_axiom ss sym_pos m] adds in signature state [ss] a new axiom symbol
of type [!(m.meta_type)] and instantiate [m] with it. WARNING: It does not
check whether the type of [m] contains metavariables. Return updated
signature state [ss] and the new axiom symbol.*)
let add_axiom : Sig_state.t -> popt -> meta -> sym =
fun ss sym_pos m ->
let name =
let i = Stdlib.(incr admitted; !admitted) in
assert (i<=0);
Printf.sprintf "_ax%i" (i + max_int)
in
let sym =
wrn sym_pos "axiom %a: %a" uid name term !(m.meta_type);
let pos = shift Stdlib.(!admitted) sym_pos in
let id = Pos.make pos name in
snd (Sig_state.add_symbol ss
Public Defin Eager true id None !(m.meta_type) [] None)
in
let meta_value =
let vars = Array.init m.meta_arity (new_var_ind "x") in
let ax = add_args (mk_Symb sym) (List.map mk_Vari (Array.to_list vars)) in
bind_mvar vars ax
in
LibMeta.set (new_problem()) m meta_value; sym
(** [admit_meta ss sym_pos m] adds as many axioms as needed in the signature
state [ss] to instantiate the metavariable [m] by a fresh axiom added to
the signature [ss]. *)
let admit_meta : Sig_state.t -> popt -> meta -> unit =
fun ss sym_pos m ->
let rec admit ms m =
assert (not (MetaSet.mem m ms));
LibMeta.iter true (admit (MetaSet.add m ms)) [] !(m.meta_type);
ignore (add_axiom ss sym_pos m)
in
admit MetaSet.empty m
(** [tac_admit ss pos ps gt] admits typing goal [gt]. *)
let tac_admit: Sig_state.t -> popt -> proof_state -> goal_typ -> proof_state =
fun ss sym_pos ps gt ->
admit_meta ss sym_pos gt.goal_meta; remove_solved_goals ps
(** [tac_solve pos ps] tries to simplify the unification goals of the proof
state [ps] and fails if constraints are unsolvable. *)
let tac_solve : popt -> proof_state -> proof_state = fun pos ps ->
if Logger.log_enabled () then log "@[<v>tac_solve@ %a@]" goals ps;
let gs_typ, gs_unif = List.partition is_typ ps.proof_goals in
let p = new_problem() in
let add_meta ms = function
| Unif _ -> ms
| Typ gt -> MetaSet.add gt.goal_meta ms
in
p := {!p with metas = List.fold_left add_meta MetaSet.empty gs_typ
; to_solve = List.rev_map get_constr gs_unif};
if not (Unif.solve_noexn p) then
fatal pos "Unification goals are unsatisfiable.";
let non_instantiated g =
match g with
| Typ gt when !(gt.goal_meta.meta_value) = None ->
Some (Goal.simpl Eval.beta_simplify g)
| _ -> None
in
let gs_typ = List.filter_map non_instantiated gs_typ in
let is_eq_goal_meta m = function
| Typ gt -> m == gt.goal_meta
| _ -> assert false
in
let add_goal m gs =
if List.exists (is_eq_goal_meta m) gs_typ then gs
else Goal.of_meta m :: gs
in
let proof_goals =
gs_typ @ MetaSet.fold add_goal (!p).metas
(List.map (fun c -> Unif c) (!p).unsolved)
in
{ps with proof_goals}
(** [tac_refine pos ps gt gs p t] refines the typing goal [gt] with [t]. [p]
is the set of metavariables created by the scoping of [t]. *)
let tac_refine : ?check:bool ->
popt -> proof_state -> goal_typ -> goal list -> problem -> term
-> proof_state =
fun ?(check=true) pos ps gt gs p t ->
if Logger.log_enabled () then log "@[tac_refine@ %a@]" term t;
let c = Env.to_ctxt gt.goal_hyps in
if LibMeta.occurs gt.goal_meta c t then fatal pos "Circular refinement.";
let t =
if check then
match Infer.check_noexn p c t gt.goal_type with
| None ->
let ids = Ctxt.names c in let term = term_in ids in
fatal pos "%a@ does not have type@ %a." term t term gt.goal_type
| Some t -> t
else t
in
if Logger.log_enabled () then
log (Color.red "%a ≔ %a") meta gt.goal_meta term t;
LibMeta.set p gt.goal_meta (bind_mvar (Env.vars gt.goal_hyps) t);
if Logger.log_enabled () then log "%a" problem p;
tac_solve pos {ps with proof_goals = Proof.add_goals_of_problem p gs}
(** [ind_data t] returns the [ind_data] structure of [s] if [t] is of the
form [s t1 .. tn] with [s] an inductive type. Fails otherwise. *)
let ind_data : popt -> Env.t -> term -> Sign.ind_data = fun pos env a ->
let h, ts = get_args (Eval.whnf (Env.to_ctxt env) a) in
match h with
| Symb s ->
let sign = Path.Map.find s.sym_path Sign.(!loaded) in
begin
try
let ind = SymMap.find s !(sign.sign_ind) in
let _, ts = List.cut ts ind.ind_nb_params in
let ctxt = Env.to_ctxt env in
if LibTerm.distinct_vars ctxt (Array.of_list ts) = None
then fatal pos "%a is not applied to distinct variables." sym s
else ind
with Not_found -> fatal pos "%a is not an inductive type." sym s
end
| _ ->
let ids = Env.names env in let term = term_in ids in
fatal pos "%a is not headed by an inductive type." term a
(** [tac_induction pos ps gt] tries to apply the induction tactic on the
typing goal [gt]. *)
let tac_induction : popt -> proof_state -> goal_typ -> goal list
-> proof_state = fun pos ps ({goal_type;goal_hyps;_} as gt) gs ->
let ctx = Env.to_ctxt goal_hyps in
match Eval.whnf ctx goal_type with
| Prod(a,_) ->
let ind = ind_data pos goal_hyps a in
let n = ind.ind_nb_params + ind.ind_nb_types + ind.ind_nb_cons in
let p = new_problem () in
let metas =
let fresh_meta _ =
let mt = LibMeta.make p ctx mk_Type in
LibMeta.make p ctx mt
in
List.(rev (init (n - 1) fresh_meta))
in
let t = add_args (mk_Symb ind.ind_prop) metas in
tac_refine pos ps gt gs p t
| _ ->
let ids = Ctxt.names ctx in let term = term_in ids in
fatal pos "[%a] is not a product." term goal_type
(** [count_products a] returns the number of consecutive products at
the top of the term [a]. *)
let count_products : ctxt -> term -> int = fun c ->
let rec count acc t =
match Eval.whnf c t with
| Prod(_,b) -> count (acc + 1) (subst b mk_Kind)
| _ -> acc
in count 0
(** [get_prod_ids env do_whnf t] returns the list [v1;..;vn] if [do_whnf] is
true and [whnf t] is of the form [Π v1:A1, .., Π vn:An, u] with [u] not a
product, or if [do_whnf] is false and [t] is of the form [Π v1:A1, .., Π
vn:An, u] with [u] not a product. *)
let get_prod_ids env =
let rec aux acc do_whnf t =
match get_args t with
| Prod(_,b), _ ->
let x,b = unbind b in
aux (base_name x::acc) do_whnf b
| _ ->
if do_whnf then aux acc false (Eval.whnf (Env.to_ctxt env) t)
else List.rev acc
in aux []
(** Builtin tactic names. *)
type tactic =
| T_admit
| T_and
| T_apply
| T_assume
| T_change
| T_fail
| T_generalize
| T_have
| T_induction
| T_orelse
| T_refine
| T_reflexivity
| T_remove
| T_repeat
| T_rewrite
| T_set
| T_simplify
| T_simplify_beta
| T_solve
| T_symmetry
| T_try
| T_why3
type config = (string,tactic) Hashtbl.t
(** [get_config ss pos] build the configuration using [ss]. *)
let get_config (ss:Sig_state.t) (pos:Pos.popt) : config =
let t = Hashtbl.create 17 in
let add n v = let s = Builtin.get ss pos n in Hashtbl.add t s.sym_name v in
add "admit" T_admit;
add "and" T_and;
add "apply" T_apply;
add "assume" T_assume;
add "change" T_change;
add "fail" T_fail;
add "generalize" T_generalize;
add "have" T_have;
add "induction" T_induction;
add "orelse" T_orelse;
add "refine" T_refine;
add "reflexivity" T_reflexivity;
add "remove" T_remove;
add "repeat" T_repeat;
add "rewrite" T_rewrite;
add "set" T_set;
add "simplify" T_simplify;
add "simplify rule off" T_simplify_beta;
add "solve" T_solve;
add "symmetry" T_symmetry;
add "try" T_try;
add "why3" T_why3;
t
(** [p_term pos t] converts the term [t] into a p_term at position [pos]. *)
let p_term (pos:popt): int StrMap.t -> term -> p_term =
let mk = Pos.make pos in
let rec term idmap t = Pos.make pos (term_aux idmap t)
and params idmap x a =
[Some(Pos.make pos (base_name x))],Some(term idmap a),false
and term_aux idmap t :p_term_aux =
match unfold t with
| Type -> P_Type
| Symb s ->
let t = P_Iden(mk(s.sym_path,s.sym_name),true) in
if !(s.sym_nota) = NoNotation then t else P_Wrap (Pos.make pos t)
| Vari v -> P_Iden(mk([],base_name v),false)
| Appl(u,v) -> P_Appl(term idmap u, term idmap v)
| Prod(a,b) ->
let (x,b),idmap' = Print.safe_unbind idmap b in
P_Prod([params idmap x a], term idmap' b)
| Abst(a,b) ->
let (x,b),idmap' = Print.safe_unbind idmap b in
P_Abst([params idmap x a], term idmap' b)
| LLet(a,t,b) ->
let (x,b),idmap' = Print.safe_unbind idmap b in
let id = Pos.make pos (base_name x) in
P_LLet(id,[],Some(term idmap a),term idmap t,term idmap' b)
| _ -> fatal pos "Unhandled term expression: %a." Print.term t
in term
let remove_quotes s = String.sub s 1 (String.length s - 2)
let _ = assert (remove_quotes "\"\"" = "" && remove_quotes "\"ab\"" = "ab")
let p_ident_of_sym (pos:popt) (t:term) :p_ident =
match unfold t with
| Symb s when s.sym_path = Sign.Ghost.path
&& String.is_string_literal s.sym_name ->
Pos.make pos (remove_quotes s.sym_name)
| _ -> fatal pos "Not a string: %a." term t
let p_ident_of_var (pos:popt) (t:term) :p_ident =
match unfold t with
| Vari v -> Pos.make pos (base_name v)
| _ -> fatal pos "Not a variable of the proof context: %a." term t
let p_term_of_string (pos:popt) (t:term): p_term =
match t with
| Symb s when String.is_string_literal s.sym_name ->
begin
let string = remove_quotes s.sym_name in
let fname = match pos with Some{fname=Some fn;_} -> fn | _ -> "" in
let stream = Parsing.Parser.Lp.parse_term_string fname string in
try Stream.next stream with Stream.Failure -> assert false
end
| _ -> fatal pos "refine tactic not applied to a term string literal"
let p_rw_patt_of_string (pos:popt) (t:term): p_rw_patt option =
match t with
| Symb s when String.is_string_literal s.sym_name ->
let string = remove_quotes s.sym_name in
if string = "" then None
else
begin
let fname = match pos with Some{fname=Some fn;_} -> fn | _ -> "" in
let stream = Parsing.Parser.Lp.parse_rwpatt_string fname string in
try Some (Stream.next stream) with Stream.Failure -> assert false
end
| _ -> fatal pos "rewrite tactic not applied to a pattern string literal"
let is_right (pos:popt) (t:term): bool =
match t with
| Symb s when String.is_string_literal s.sym_name ->
begin
match remove_quotes s.sym_name with
| "left" -> false
| "" | "right" -> true
| _ ->
fatal pos "rewrite tactic not applied to side string literal"
end
| _ -> fatal pos "rewrite tactic not applied to a side string literal"
(** [p_tactic t] interprets the term [t] as a tactic. *)
let p_tactic (ss:Sig_state.t) (pos:popt) :int StrMap.t -> term -> p_tactic =
let c = get_config ss pos in
let rec tac idmap t = Pos.make pos (tac_aux idmap t)
and tac_aux idmap t =
match get_args t with
| Symb s, ts ->
begin
try
match Hashtbl.find c s.sym_name, ts with
| T_admit, _ -> P_tac_admit
| T_and, [t1;t2] -> P_tac_and(tac idmap t1, tac idmap t2)
| T_and, _ -> assert false
| T_apply, [_;t] -> P_tac_apply(p_term pos idmap t)
| T_apply, _ -> assert false
| T_assume, [t] -> P_tac_assume [Some(p_ident_of_sym pos t)]
| T_assume, _ -> assert false
| T_change, [_;t] -> P_tac_apply(p_term pos idmap t)
| T_change, _ -> assert false
| T_fail, _ -> P_tac_fail
| T_generalize, [_;t] -> P_tac_generalize(p_ident_of_var pos t)
| T_generalize, _ -> assert false
| T_have, [t1;t2] ->
let prf_sym = Builtin.get ss pos "P" in
let prf = p_term pos idmap (mk_Symb prf_sym) in
let t2 = Pos.make pos (P_Appl(prf, p_term pos idmap t2)) in
P_tac_have(p_ident_of_sym pos t1, t2)
| T_have, _ -> assert false
| T_induction, _ -> P_tac_induction
| T_orelse, [t1;t2] -> P_tac_orelse(tac idmap t1, tac idmap t2)
| T_orelse, _ -> assert false
| T_refine, [t] -> P_tac_refine(p_term_of_string pos t)
| T_refine, _ -> assert false
| T_reflexivity, _ -> P_tac_refl
| T_remove, [_;t] -> P_tac_remove [p_ident_of_var pos t]
| T_remove, _ -> assert false
| T_repeat, [t] -> P_tac_repeat(tac idmap t)
| T_repeat, _ -> assert false
| T_rewrite, [side;pat;_;t] ->
P_tac_rewrite(is_right pos side,
p_rw_patt_of_string pos pat, p_term pos idmap t)
| T_rewrite, _ -> assert false
| T_set, [t1;_;t2] ->
P_tac_set(p_ident_of_sym pos t1, p_term pos idmap t2)
| T_set, _ -> assert false
| T_simplify, _ -> P_tac_simpl SimpAll
| T_simplify_beta, _ -> P_tac_simpl SimpBetaOnly
| T_solve, _ -> P_tac_solve
| T_symmetry, _ -> P_tac_sym
| T_try, [t] -> P_tac_try(tac idmap t)
| T_try, _ -> assert false
| T_why3, _ -> P_tac_why3 None
with Not_found ->
fatal pos "Unhandled tactic expression: %a." term t
end
| _ -> fatal pos "Unhandled tactic expression: %a." term t
in tac
(** [handle ss sym_pos prv ps tac] applies tactic [tac] in the proof state
[ps] and returns the new proof state. *)
let rec handle :
Sig_state.t -> popt -> bool -> proof_state -> p_tactic -> proof_state =
fun ss sym_pos prv ps ({elt;pos} as tac) ->
if Logger.log_enabled () then log "%a" Pretty.tactic tac;
match ps.proof_goals with
| [] -> assert false
| g::gs ->
match elt with
| P_tac_fail -> fatal pos "Call to tactic \"fail\""
| P_tac_query _ -> assert false
| P_tac_simpl SimpAll ->
{ps with proof_goals = Goal.simpl Eval.snf g :: gs}
| P_tac_simpl SimpBetaOnly ->
let tags = [`NoRw; `NoExpand] in
{ps with proof_goals = Goal.simpl (Eval.snf ~tags) g :: gs}
| P_tac_simpl (SimpSym qid) ->
let s = Sig_state.find_sym ~prt:true ~prv:true ss qid in
let g = Goal.simpl (fun _ctx -> Eval.unfold_sym s) g in
{ps with proof_goals = g :: gs}
| P_tac_solve -> tac_solve pos ps
| _ ->
match g with
| Unif _ -> fatal pos "Not a typing goal."
| Typ ({goal_hyps=env;_} as gt) ->
let scope t = Scope.scope_term ~mok:(Proof.meta_of_key ps) prv ss env t in
let assume idopts =
match idopts with
| [] -> ps
| _ -> tac_refine pos ps gt gs (new_problem())
(scope (P.abst_list idopts P.wild))
in
let check id =
if Env.mem id.elt env then fatal id.pos "Identifier already in use." in
match elt with
| P_tac_fail
| P_tac_query _
| P_tac_simpl _
| P_tac_solve -> assert false
| P_tac_admit -> tac_admit ss sym_pos ps gt
| P_tac_apply pt ->
let t = scope pt in
let n =
let c = Env.to_ctxt env in
let p = new_problem () in
match Infer.infer_noexn p c t with
| None ->
let ids = Ctxt.names c in let term = term_in ids in
fatal pos "[%a] is not typable." term t
| Some (_, a) -> count_products c a
in
let t = scope (P.appl_wild pt n) in
let p = new_problem () in
tac_refine pos ps gt gs p t
| P_tac_assume idopts ->
if List.exists ((=) None) idopts then
fatal pos "underscores not allowed in assume";
List.iter (Option.iter check) idopts;
Syntax.check_distinct_idopts idopts;
assume idopts
| P_tac_change pa ->
let vname = "x" in
let vabs = Pos.make pos vname in
let varg = Pos.make pos ([],vname) in
let vparam = [[Some vabs],Some pa,false] in
let mk = Pos.make pos in
let idbody = mk(P_Iden(varg,false)) in
let id = mk(P_Abst(vparam,idbody)) in
let t = mk(P_Appl(id,mk P_Wild)) in
let p = new_problem() in
tac_refine pos ps gt gs p (scope t)
| P_tac_generalize {elt=id; pos=idpos} ->
begin
try
let p = new_problem() in
let e2, x, e1 = List.split (fun (s,_) -> s = id) env in
let u = gt.goal_type in
let q = Env.to_prod [x] (Env.to_prod e2 u) in
let m = LibMeta.fresh p (Env.to_prod e1 q) (List.length e1) in
let me1 = mk_Meta (m, Env.to_terms e1) in
let t =
List.fold_left (fun t (_,(v,_,_)) -> mk_Appl(t, mk_Vari v))
me1 (x :: List.rev e2)
in
tac_refine pos ps gt gs p t
with Not_found -> fatal idpos "Unknown hypothesis %a" uid id;
end
| P_tac_have(id, t) ->
check id;
let p = new_problem() in
let t = scope t in
let c = Env.to_ctxt env in
begin
match Infer.check_noexn p c t mk_Type with
| None ->
let ids = Ctxt.names c in let term = term_in ids in
fatal pos "%a is not of type Type." term t
| Some t ->
let n = List.length env in
let m1 = LibMeta.fresh p (Env.to_prod env t) n in
let v = new_var id.elt in
let env' = Env.add id.elt v t None env in
let m2 = LibMeta.fresh p (Env.to_prod env' gt.goal_type) (n+1) in
let ts = Env.to_terms env in
let u = mk_Meta (m2, Array.append ts [|mk_Meta (m1, ts)|]) in
tac_refine pos ps gt gs p u
end
| P_tac_set(id,t) ->
check id;
let p = new_problem() in
let t = scope t in
let c = Env.to_ctxt env in
begin
match Infer.infer_noexn p c t with
| None ->
let ids = Ctxt.names c in let term = term_in ids in
fatal pos "%a is not typable." term t
| Some (t,b) ->
if Unif.solve_noexn p then begin
let x = new_var id.elt in
let e' = Env.add id.elt x b (Some t) env in
let n = List.length e' in
let v = LibTerm.fold x t gt.goal_type in
let m = LibMeta.fresh (new_problem()) (Env.to_prod e' v) n in
let ts = Env.to_terms env in
let u = mk_Meta (m, Array.append ts [|t|]) in
LibMeta.set p gt.goal_meta (bind_mvar (Env.vars env) u);
let g = Typ {goal_meta=m; goal_hyps=e'; goal_type=v} in
{ps with proof_goals = g :: Proof.add_goals_of_problem p gs}
end else fatal pos "The unification constraints for %a \
to be typable are not satisfiable." term t
end
| P_tac_induction -> tac_induction pos ps gt gs
| P_tac_refine t -> tac_refine pos ps gt gs (new_problem()) (scope t)
| P_tac_refl ->
begin
let cfg = Rewrite.get_eq_config ss pos in
let _,vs = Rewrite.get_eq_data cfg pos gt.goal_type in
let idopts = Env.gen_valid_idopts env (List.map base_name vs) in
let ps = assume idopts in
match ps.proof_goals with
| [] -> assert false
| Unif _::_ -> assert false
| Typ gt::gs ->
let cfg = Rewrite.get_eq_config ss pos in
let (a,l,_),_ = Rewrite.get_eq_data cfg pos gt.goal_type in
let prf = add_args (mk_Symb cfg.symb_refl) [a; l] in
tac_refine pos ps gt gs (new_problem()) prf
end
| P_tac_remove ids ->
let remove g id =
match g with
| Unif _ -> assert false
| Typ gt ->
let k =
try List.pos (fun (s,_) -> s = id.elt) env
with Not_found -> fatal id.pos "Unknown hypothesis."
in
let m = gt.goal_meta in
let n = m.meta_arity - 1 in
let a = cleanup !(m.meta_type) in
let rec codom_binder i a =
match unfold a with
| Prod(_,b) ->
if i <= 0 then b else codom_binder (i-1) (subst b mk_Kind)
| LLet(_,t,b) ->
if i <= 0 then b else codom_binder (i-1) (subst b t)
| _ -> assert false
in
if binder_occur (codom_binder (n - k) a) then
fatal id.pos "%s cannot be removed because of dependencies."
id.elt;
let env' = List.filter (fun (s,_) -> s <> id.elt) env in
let a' = Env.to_prod env' gt.goal_type in
let p = new_problem() in
let m' = LibMeta.fresh p a' n in
let t = mk_Meta(m',Env.to_terms env') in
LibMeta.set p m (bind_mvar (Env.vars env) t);
Goal.of_meta m'
in
Syntax.check_distinct ids;
let n = gt.goal_meta.meta_arity - 1 in
let id_pos id =
try id, n - List.pos (fun (s,_) -> s = id.elt) env
with Not_found -> fatal id.pos "Unknown hypothesis."
in
let cmp (_,k1) (_,k2) = Stdlib.compare k2 k1 in
let ids = List.map fst (List.sort cmp (List.map id_pos ids)) in
let g = List.fold_left remove g ids in
{ps with proof_goals = g::gs}
| P_tac_rewrite(l2r,pat,eq) ->
let pat = Option.map (Scope.scope_rw_patt ss env) pat in
let p = new_problem() in
tac_refine pos ps gt gs p
(Rewrite.rewrite ss p pos gt l2r pat (scope eq))
| P_tac_sym ->
let cfg = Rewrite.get_eq_config ss pos in
let (a,l,r),_ = Rewrite.get_eq_data cfg pos gt.goal_type in
let p = new_problem() in
let prf =
let mt = mk_Appl(mk_Symb cfg.symb_P,
add_args (mk_Symb cfg.symb_eq) [a;r;l]) in
let meta_term = LibMeta.make p (Env.to_ctxt env) mt in
Rewrite.swap cfg a r l meta_term
in
tac_refine pos ps gt gs p prf
| P_tac_why3 cfg ->
begin
let ids = get_prod_ids env false gt.goal_type in
let idopts = Env.gen_valid_idopts env ids in
let ps = assume idopts in
match ps.proof_goals with
| Typ gt::_ ->
Why3_tactic.handle ss pos cfg gt; tac_admit ss sym_pos ps gt
| _ -> assert false
end
| P_tac_try tactic ->
begin
try handle ss sym_pos prv ps tactic
with Fatal(_, _s) -> ps
end
| P_tac_orelse(t1,t2) ->
begin
try handle ss sym_pos prv ps t1
with Fatal(_, _s) -> handle ss sym_pos prv ps t2
end
| P_tac_repeat t ->
begin
try
let nb_goals = List.length ps.proof_goals in
let ps = handle ss sym_pos prv ps t in
if List.length ps.proof_goals < nb_goals then ps
else handle ss sym_pos prv ps tac
with Fatal(_, _s) -> ps
end
| P_tac_and(t1,t2) ->
let ps = handle ss sym_pos prv ps t1 in
handle ss sym_pos prv ps t2
| P_tac_eval pt ->
let t = Eval.snf (Env.to_ctxt env) (scope pt) in
let idmap = get_names g in
handle ss sym_pos prv ps (p_tactic ss pos idmap t)
(** Representation of a tactic output. *)
type tac_output = proof_state * Query.result
(** [handle ss sym_pos prv ps tac] applies tactic [tac] in the proof state
[ps] and returns the new proof state. *)
let handle :
Sig_state.t -> popt -> bool -> proof_state -> p_tactic -> tac_output =
fun ss sym_pos prv ps ({elt;pos} as tac) ->
match elt with
| P_tac_fail -> fatal pos "Call to tactic \"fail\""
| P_tac_query(q) ->
if Logger.log_enabled () then log "%a@." Pretty.tactic tac;
ps, Query.handle ss (Some ps) q
| _ ->
match ps.proof_goals with
| [] -> fatal pos "No remaining goals."
| g::_ ->
if Logger.log_enabled() then
log ("%a@\n" ^^ Color.red "%a")
Proof.Goal.pp_no_hyp g Pretty.tactic tac;
handle ss sym_pos prv ps tac, None
(** [handle sym_pos prv r tac n] applies the tactic [tac] from the previous
tactic output [r] and checks that the number of goals of the new proof
state is compatible with the number [n] of subproofs. *)
let handle :
Sig_state.t -> popt -> bool -> tac_output -> p_tactic -> int -> tac_output =
fun ss sym_pos prv (ps, _) t nb_subproofs ->
let (ps', _) as a = handle ss sym_pos prv ps t in
let nb_goals_before = List.length ps.proof_goals in
let nb_goals_after = List.length ps'.proof_goals in
let nb_newgoals = nb_goals_after - nb_goals_before in
if nb_newgoals <= 0 then
if nb_subproofs = 0 then a
else fatal t.pos "A subproof is given but there is no subgoal."
else if is_destructive t then
match nb_newgoals + 1 - nb_subproofs with
| 0 -> a
| n when n > 0 ->
fatal t.pos "Missing subproofs (%d subproofs for %d subgoals):@.%a"
nb_subproofs (nb_newgoals + 1) goals ps'
| _ ->
fatal t.pos "Too many subproofs (%d subproofs for %d subgoals):@.%a"
nb_subproofs (nb_newgoals + 1) goals ps'
else match nb_newgoals - nb_subproofs with
| 0 -> a
| n when n > 0 ->
fatal t.pos "Missing subproofs (%d subproofs for %d subgoals):@.%a"
nb_subproofs nb_newgoals goals ps'
| _ -> fatal t.pos "Too many subproofs (%d subproofs for %d subgoals)."
nb_subproofs nb_newgoals