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
|
(************************************************************************)
(* v * The Coq Proof Assistant / The Coq Development Team *)
(* <O___,, * CNRS-Ecole Polytechnique-INRIA Futurs-Universite Paris Sud *)
(* \VV/ **************************************************************)
(* // * This file is distributed under the terms of the *)
(* * GNU Lesser General Public License Version 2.1 *)
(************************************************************************)
(* $Id$ *)
open Tacmach
open Proof_type
open Libobject
open Reductionops
open Term
open Termops
open Names
open Entries
open Libnames
open Nameops
open Util
open Pp
open Printer
open Environ
open Tactics
open Tacticals
open Vernacexpr
open Safe_typing
open Nametab
open Decl_kinds
open Constrintern
let replace = ref (fun _ _ -> assert false)
let register_replace f = replace := f
type reflexive_relation =
{ refl_a: constr ;
refl_aeq: constr;
refl_refl: constr;
refl_sym: constr option
}
type 'a relation_class =
ACReflexive of 'a (* the relation of the reflexive_relation
or the reflexive_relation*)
| ACLeibniz of constr (* the carrier *)
type 'a morphism =
{ args : 'a relation_class list;
output : 'a relation_class;
lem : constr;
}
type funct =
{ f_args : constr list;
f_output : constr
}
type morphism_class =
ACMorphism of reflexive_relation morphism
| ACFunction of funct
let subst_mps_in_relation_class subst =
function
ACReflexive t -> ACReflexive (subst_mps subst t)
| ACLeibniz t -> ACLeibniz (subst_mps subst t)
let constr_relation_class_of_relation_relation_class =
function
ACReflexive relation -> ACReflexive relation.refl_aeq
| ACLeibniz t -> ACLeibniz t
let constr_of c = Constrintern.interp_constr Evd.empty (Global.env()) c
let constant dir s = Coqlib.gen_constant "Setoid_replace" ("Setoids"::dir) s
let init_constant dir s = Coqlib.gen_constant "Setoid_replace" ("Init"::dir) s
let reference dir s = Coqlib.gen_reference "Setoid_replace" ("Setoids"::dir) s
let eval_reference dir s = EvalConstRef (destConst (constant dir s))
let eval_init_reference dir s = EvalConstRef (destConst (init_constant dir s))
let current_constant id =
try
global_reference id
with Not_found ->
anomaly ("Setoid: cannot find " ^ (string_of_id id))
(* From Setoid.v *)
let coq_is_reflexive = lazy(constant ["Setoid"] "is_reflexive")
let coq_Relation_Class = lazy(constant ["Setoid"] "Relation_Class")
let coq_Setoid_Theory = lazy(constant ["Setoid"] "Setoid_Theory")
let coq_Morphism_Theory = lazy(constant ["Setoid"] "Morphism_Theory")
let coq_Build_Morphism_Theory= lazy(constant ["Setoid"] "Build_Morphism_Theory")
let coq_Reflexive = lazy(constant ["Setoid"] "Reflexive")
let coq_Leibniz = lazy(constant ["Setoid"] "Leibniz")
let coq_seq_refl = lazy(constant ["Setoid"] "Seq_refl")
let coq_seq_sym = lazy(constant ["Setoid"] "Seq_sym")
let coq_seq_trans = lazy(constant ["Setoid"] "Seq_trans")
let coq_singl = lazy(constant ["Setoid"] "singl")
let coq_cons = lazy(constant ["Setoid"] "cons")
let coq_equality_morphism_of_setoid_theory =
lazy(constant ["Setoid"] "equality_morphism_of_setoid_theory")
let coq_make_compatibility_goal =
lazy(constant ["Setoid"] "make_compatibility_goal")
let coq_make_compatibility_goal_ref =
lazy(reference ["Setoid"] "make_compatibility_goal")
let coq_make_compatibility_goal_aux_ref =
lazy(reference ["Setoid"] "make_compatibility_goal_aux")
let coq_App = lazy(constant ["Setoid"] "App")
let coq_Toreplace = lazy(constant ["Setoid"] "Toreplace")
let coq_Tokeep = lazy(constant ["Setoid"] "Tokeep")
let coq_Imp = lazy(constant ["Setoid"] "Imp")
let coq_fcl_singl = lazy(constant ["Setoid"] "fcl_singl")
let coq_fcl_cons = lazy(constant ["Setoid"] "fcl_cons")
let coq_setoid_rewrite = lazy(constant ["Setoid"] "setoid_rewrite")
let coq_proj2 = lazy(init_constant ["Logic"] "proj2")
let coq_morphism_theory_of_function =
lazy(constant ["Setoid"] "morphism_theory_of_function")
let coq_morphism_theory_of_predicate =
lazy(constant ["Setoid"] "morphism_theory_of_predicate")
let coq_relation_of_relation_class =
lazy(eval_reference ["Setoid"] "relation_of_relation_class")
let coq_interp = lazy(eval_reference ["Setoid"] "interp")
let coq_Morphism_Context_rect2 =
lazy(eval_reference ["Setoid"] "Morphism_Context_rect2")
let coq_iff = lazy(eval_init_reference ["Logic"] "iff")
let coq_prop_relation =
lazy (
ACReflexive {
refl_a = mkProp ;
refl_aeq = init_constant ["Logic"] "iff" ;
refl_refl = init_constant ["Logic"] "iff_refl";
refl_sym = Some (init_constant ["Logic"] "iff_sym")
})
(************************* Table of declared relations **********************)
(* Relations are stored in a table which is synchronised with the Reset mechanism. *)
let relation_table = ref Gmap.empty
let relation_table_add (s,th) = relation_table := Gmap.add s th !relation_table
let relation_table_find s = Gmap.find s !relation_table
let relation_table_mem s = Gmap.mem s !relation_table
let prrelation s =
str "(" ++ prterm s.refl_a ++ str "," ++ prterm s.refl_aeq ++ str ")"
let prrelation_class =
function
ACReflexive eq ->
(try prrelation (relation_table_find eq)
with Not_found ->
(*CSC: still "setoid" in the error message *)
str "[[ Error: setoid on equality " ++ prterm eq ++ str " not found! ]]")
| ACLeibniz ty -> prterm ty
let prmorphism k m =
prterm k ++ str ": " ++
prlist_with_sep (fun () -> str " -> ") prrelation_class m.args ++
str " -> " ++ prrelation_class m.output
(* A function that gives back the only relation_class on a given carrier *)
(*CSC: this implementation is really inefficient. I should define a new
map to make it efficient. However, is this really worth of? *)
let default_relation_for_carrier a =
let rng = Gmap.rng !relation_table in
match List.filter (fun {refl_a=refl_a} -> refl_a = a) rng with
[] -> ACLeibniz a
| relation::tl ->
if tl <> [] then
msg_warning
(*CSC: still "setoid" in the error message *)
(str "There are several setoids whose carrier is " ++ prterm a ++
str ". The setoid " ++ prrelation relation ++
str " is randomly chosen.") ;
ACReflexive relation
let relation_morphism_of_constr_morphism =
let relation_relation_class_of_constr_relation_class =
function
ACLeibniz t -> ACLeibniz t
| ACReflexive aeq ->
ACReflexive (try relation_table_find aeq with Not_found -> assert false)
in
function mor ->
let args' = List.map relation_relation_class_of_constr_relation_class mor.args in
let output' = relation_relation_class_of_constr_relation_class mor.output in
{mor with args=args' ; output=output'}
let subst_relation subst relation =
let refl_a' = subst_mps subst relation.refl_a in
let refl_aeq' = subst_mps subst relation.refl_aeq in
let refl_refl' = subst_mps subst relation.refl_refl in
let refl_sym' = option_app (subst_mps subst) relation.refl_sym in
if refl_a' == relation.refl_a
&& refl_aeq' == relation.refl_aeq
&& refl_refl' == relation.refl_refl
&& refl_sym' == relation.refl_sym
then
relation
else
{ refl_a = refl_a' ;
refl_aeq = refl_aeq' ;
refl_refl = refl_refl' ;
refl_sym = refl_sym'
}
let equiv_list () = List.map (fun x -> x.refl_aeq) (Gmap.rng !relation_table)
let _ =
Summary.declare_summary "relation-table"
{ Summary.freeze_function = (fun () -> !relation_table);
Summary.unfreeze_function = (fun t -> relation_table := t);
Summary.init_function = (fun () -> relation_table := Gmap .empty);
Summary.survive_module = true;
Summary.survive_section = false }
(* Declare a new type of object in the environment : "relation-theory". *)
let (relation_to_obj, obj_to_relation)=
let cache_set (_,(s, th)) =
let th' =
if relation_table_mem s then
begin
let old_relation = relation_table_find s in
let th' =
{th with refl_sym =
match th.refl_sym with
None -> old_relation.refl_sym
| Some t -> Some t} in
(*CSC: still "setoid" in the error message *)
msg_warning
(str "The setoid " ++ prrelation th' ++ str " is redeclared. " ++
str "The new declaration (reflevity proved by " ++
prterm th'.refl_refl ++
(match th'.refl_sym with
None -> str ""
| Some t -> str " and symmetry proved by " ++ prterm t) ++
str ") replaces the old declaration (reflexivity proved by "++
prterm old_relation.refl_refl ++
(match old_relation.refl_sym with
None -> str ""
| Some t -> str " and symmetry proved by " ++ prterm t) ++
str ").") ;
th'
end
else
th
in
relation_table_add (s,th')
and subst_set (_,subst,(s,th as obj)) =
let s' = subst_mps subst s in
let th' = subst_relation subst th in
if s' == s && th' == th then obj else
(s',th')
and export_set x = Some x
in
declare_object {(default_object "relation-theory") with
cache_function = cache_set;
load_function = (fun i o -> cache_set o);
subst_function = subst_set;
classify_function = (fun (_,x) -> Substitute x);
export_function = export_set}
(******************************* Table of declared morphisms ********************)
(* Setoids are stored in a table which is synchronised with the Reset mechanism. *)
let morphism_table = ref Gmap.empty
let morphism_table_find m = Gmap.find m !morphism_table
let morphism_table_add (m,c) =
let old =
try
morphism_table_find m
with
Not_found -> []
in
try
let old_morph =
List.find
(function mor -> mor.args = c.args && mor.output = c.output) old
in
msg_warning
(str "The morphism " ++ prmorphism m old_morph ++ str " is redeclared. " ++
str "The new declaration whose compatibility is granted by " ++
prterm c.lem ++ str " replaces the old declaration whose" ++
str " compatibility was granted by " ++
prterm old_morph.lem ++ str ".")
with
Not_found -> morphism_table := Gmap.add m (c::old) !morphism_table
let subst_morph subst morph =
let lem' = subst_mps subst morph.lem in
let args' = list_smartmap (subst_mps_in_relation_class subst) morph.args in
let output' = subst_mps_in_relation_class subst morph.output in
if lem' == morph.lem
&& args' == morph.args
&& output' == morph.output
then
morph
else
{ args = args' ;
output = output' ;
lem = lem'
}
let _ =
Summary.declare_summary "morphism-table"
{ Summary.freeze_function = (fun () -> !morphism_table);
Summary.unfreeze_function = (fun t -> morphism_table := t);
Summary.init_function = (fun () -> morphism_table := Gmap .empty);
Summary.survive_module = true;
Summary.survive_section = false }
(* Declare a new type of object in the environment : "morphism-definition". *)
let (morphism_to_obj, obj_to_morphism)=
let cache_set (_,(m, c)) = morphism_table_add (m, c)
and subst_set (_,subst,(m,c as obj)) =
let m' = subst_mps subst m in
let c' = subst_morph subst c in
if m' == m && c' == c then obj else
(m',c')
and export_set x = Some x
in
declare_object {(default_object "morphism-definition") with
cache_function = cache_set;
load_function = (fun i o -> cache_set o);
subst_function = subst_set;
classify_function = (fun (_,x) -> Substitute x);
export_function = export_set}
(************************** Printing relations and morphisms **********************)
(*CSC: still "setoids" in the name *)
let print_setoids () =
Gmap.iter
(fun k relation ->
assert (k=relation.refl_aeq) ;
(*CSC: still "Setoid" in the error message *)
ppnl (str"Setoid " ++ prrelation relation ++
str"; reflexivity granted by " ++ prterm relation.refl_refl ++
(match relation.refl_sym with
None -> str ""
| Some t -> str " symmetry granted by " ++ prterm t)))
!relation_table ;
Gmap.iter
(fun k l ->
List.iter
(fun ({lem=lem} as mor) ->
ppnl (str "Morphism " ++ prmorphism k mor ++
str ". Compatibility granted by " ++
prterm lem ++ str "."))
l) !morphism_table
;;
(************************** Adding a relation to the database *********************)
let int_add_relation a aeq refl sym =
match refl with
None -> assert false (*CSC: unimplemented yet*)
| Some refl ->
let env = Global.env () in
if
(is_conv env Evd.empty (Typing.type_of env Evd.empty refl)
(mkApp ((Lazy.force coq_is_reflexive), [| a; aeq |])))
then
Lib.add_anonymous_leaf
(relation_to_obj
(aeq, { refl_a = a;
refl_aeq = aeq;
refl_refl = refl;
refl_sym = sym}))
else
errorlabstrm "Add Relation Class"
(str "Not a valid proof of reflexivity")
(* The vernac command "Add Relation ..." *)
let add_relation a aeq refl sym =
int_add_relation (constr_of a) (constr_of aeq) (option_app constr_of refl)
(option_app constr_of sym)
(***************** Adding a morphism to the database ****************************)
(* We maintain a table of the currently edited proofs of morphism lemma
in order to add them in the morphism_table when the user does Save *)
let edited = ref Gmap.empty
let new_edited id m =
edited := Gmap.add id m !edited
let is_edited id =
Gmap.mem id !edited
let no_more_edited id =
edited := Gmap.remove id !edited
let what_edited id =
Gmap.find id !edited
let check_is_dependent t n =
let rec aux t i n =
if (i<n)
then (dependent (mkRel i) t) || (aux t (i+1) n)
else false
in aux t 0 n
(*CSC: I do not like this very much. I would prefer relation classes
to be CIC objects. Keeping backward compatibility, however, is annoying. *)
let cic_relation_class_of_relation_class =
function
ACReflexive {refl_a = refl_a; refl_aeq = refl_aeq; refl_refl = refl_refl} ->
mkApp ((Lazy.force coq_Reflexive), [| refl_a ; refl_aeq; refl_refl |])
| ACLeibniz t -> mkApp ((Lazy.force coq_Leibniz), [| t |])
let gen_compat_lemma_statement output args m =
let rec mk_signature =
function
[] -> assert false
| [last] ->
mkApp ((Lazy.force coq_singl), [| Lazy.force coq_Relation_Class; last |])
| he::tl ->
mkApp ((Lazy.force coq_cons),
[| Lazy.force coq_Relation_Class; he ; mk_signature tl |])
in
let output = cic_relation_class_of_relation_class output in
let args= mk_signature (List.map cic_relation_class_of_relation_class args) in
args, output,
mkApp ((Lazy.force coq_make_compatibility_goal), [| args ; output ; m |])
let new_morphism m id hook =
let env = Global.env() in
let typeofm = (Typing.type_of env Evd.empty m) in
let typ = (nf_betaiota typeofm) in
let (argsrev, output) = (decompose_prod typ) in
let args_ty = (List.map snd (List.rev argsrev)) in
if (args_ty=[])
then errorlabstrm "New Morphism"
(str "The term " ++ prterm m ++ str " is not a product")
else if (check_is_dependent typ (List.length args_ty))
then errorlabstrm "New Morphism"
(str "The term " ++ prterm m ++ str" should not be a dependent product")
else
begin
let args = List.map default_relation_for_carrier args_ty in
let output = default_relation_for_carrier output in
let argsconstr,outputconstr,lem =
gen_compat_lemma_statement output args m
in
new_edited id (m,args,argsconstr,output,outputconstr);
Pfedit.start_proof id (IsGlobal (Proof Lemma))
(Declare.clear_proofs (Global.named_context ()))
lem hook;
(* "unfold make_compatibility_goal" *)
Pfedit.by
(Tactics.unfold_constr
(Lazy.force coq_make_compatibility_goal_ref)) ;
(* "unfold make_compatibility_goal_aux" *)
Pfedit.by
(Tactics.unfold_constr
(Lazy.force coq_make_compatibility_goal_aux_ref)) ;
(* "simpl" *)
Pfedit.by Tactics.simpl_in_concl ;
Options.if_verbose msg (Pfedit.pr_open_subgoals ());
end
let add_morphism lemma_infos mor_name (m,args,output) =
let env = Global.env() in
begin
match lemma_infos with
None -> () (* the Morphism_Theory object has alrady been created *)
| Some (lem_name,argsconstr,outputconstr) ->
(* only the compatibility has been proved; we need to declare the
Morphism_Theory object *)
let mext = current_constant lem_name in
ignore (
Declare.declare_constant mor_name
(DefinitionEntry
{const_entry_body =
mkApp ((Lazy.force coq_Build_Morphism_Theory),
[| argsconstr; outputconstr; m ; mext |]);
const_entry_type = None;
const_entry_opaque = false},
IsDefinition))
end ;
let mmor = current_constant mor_name in
let args_constr =
List.map constr_relation_class_of_relation_relation_class args in
let output_constr = constr_relation_class_of_relation_relation_class output in
Lib.add_anonymous_leaf
(morphism_to_obj (m,
{ args = args_constr;
output = output_constr;
lem = mmor }));
Options.if_verbose ppnl (prterm m ++ str " is registered as a morphism")
let morphism_hook stre ref =
let pf_id = id_of_global ref in
let mor_id = id_of_string (string_of_id pf_id ^ "_morphism_theory") in
let (m,args,argsconstr,output,outputconstr) = what_edited pf_id in
if (is_edited pf_id)
then
add_morphism (Some (pf_id,argsconstr,outputconstr)) mor_id
(m,args,output); no_more_edited pf_id
let new_named_morphism id m = new_morphism (constr_of m) id morphism_hook
(************************ Add Setoid ******************************************)
(*CSC: I do not like this. I would prefer more self-describing constant names *)
let gen_morphism_name =
let i = ref 0 in
function () ->
incr i;
make_ident "morphism_of_setoid_equality" (Some !i)
(* The vernac command "Add Setoid" *)
let add_setoid a aeq th =
let a = constr_of a in
let aeq = constr_of aeq in
let th = constr_of th in
let env = Global.env () in
if is_conv env Evd.empty (Typing.type_of env Evd.empty th)
(mkApp ((Lazy.force coq_Setoid_Theory), [| a; aeq |]))
then
begin
let refl = mkApp ((Lazy.force coq_seq_refl), [| a; aeq; th |]) in
let sym = mkApp ((Lazy.force coq_seq_sym), [| a; aeq; th |]) in
int_add_relation a aeq (Some refl) (Some sym) ;
let mor_name = gen_morphism_name () in
let _ =
Declare.declare_constant mor_name
(DefinitionEntry
{const_entry_body =
mkApp
((Lazy.force coq_equality_morphism_of_setoid_theory),
[| a; aeq; th |]) ;
const_entry_type = None;
const_entry_opaque = false},
IsDefinition) in
let aeq_rel =
ACReflexive
{refl_a = a ; refl_aeq = aeq ; refl_refl = refl ; refl_sym = (Some sym)}
in
add_morphism None mor_name
(aeq,[aeq_rel;aeq_rel],Lazy.force coq_prop_relation)
end
else
errorlabstrm "Add Setoid" (str "Not a valid setoid theory")
(****************************** The tactic itself *******************************)
type constr_with_marks =
| MApp of constr * morphism_class * constr_with_marks array
| Toreplace
| Tokeep of constr
| Mimp of constr_with_marks * constr_with_marks
let is_to_replace = function
| Tokeep _ -> false
| Toreplace -> true
| MApp _ -> true
| Mimp _ -> true
let get_mark a =
Array.fold_left (||) false (Array.map is_to_replace a)
type argument =
Toapply of constr (* apply the function to the argument *)
| Toexpand of name * types (* beta-expand the function w.r.t. an argument
of this type *)
let beta_expand c args_rev =
let rec to_expand =
function
[] -> []
| (Toapply _)::tl -> to_expand tl
| (Toexpand (name,s))::tl -> (name,s)::(to_expand tl) in
let rec aux n =
function
[] -> []
| (Toapply arg)::tl -> arg::(aux n tl)
| (Toexpand _)::tl -> (mkRel n)::(aux (n + 1) tl)
in
compose_lam (to_expand args_rev)
(mkApp (c, Array.of_list (List.rev (aux 1 args_rev))))
let relation_of_hypothesis_and_term_to_rewrite gl (_,h) t =
let hypt = pf_type_of gl h in
let (heq, hargs) = decompose_app hypt in
let rec get_all_but_last_two =
function
[]
| [_] -> assert false
| [_;_] -> []
| he::tl -> he::(get_all_but_last_two tl) in
let aeq = mkApp (heq,(Array.of_list (get_all_but_last_two hargs))) in
try
relation_table_find aeq
with Not_found ->
(*CSC: still "setoid" in the error message *)
errorlabstrm "Setoid_rewrite"
(prterm aeq ++ str " is not a setoid equality.")
let mark_occur t in_c =
let rec aux t in_c =
if eq_constr t in_c then
Toreplace
else
match kind_of_term in_c with
| App (c,al) ->
let a = Array.map (aux t) al in
if not (get_mark a) then Tokeep in_c
else
let mor =
try
begin
match morphism_table_find c with
[] -> assert false
| morphism::tl ->
if tl <> [] then
msg_warning
(str "There are several morphisms declared for " ++
prterm c ++
str ". The morphism " ++ prmorphism c morphism ++
str " is randomly chosen.") ;
Some
(ACMorphism
(relation_morphism_of_constr_morphism morphism))
end
with Not_found -> None
in
(match mor with
Some mor -> MApp (c,mor,a)
| None ->
(* the tactic works only if the function type is
made of non-dependent products only. However, here we
can cheat a bit by partially istantiating c to match
the requirement when the arguments to be replaced are
bound by non-dependent products only. *)
let env = Global.env() in
let typeofc = (Typing.type_of env Evd.empty c) in
let typ = nf_betaiota typeofc in
let rec find_non_dependent_function env c c_args_rev typ
f_args_rev a_rev=
function
[] ->
let mor =
ACFunction {f_args=List.rev f_args_rev ; f_output=typ} in
let func = beta_expand c c_args_rev in
MApp (func,mor,Array.of_list (List.rev a_rev))
| (he::tl) as a->
let typnf = Reduction.whd_betadeltaiota env typ in
match kind_of_term typnf with
Cast (typ,_) ->
find_non_dependent_function env c c_args_rev typ
f_args_rev a_rev a
| Prod (name,s,t) ->
let env' = push_rel (name,None,s) env in
begin
match noccurn 1 t, he with
_, Tokeep arg ->
(* generic product, to keep *)
find_non_dependent_function
env' c ((Toapply arg)::c_args_rev)
(subst1 arg t) f_args_rev a_rev tl
| true, _ ->
(* non-dependent product, to replace *)
find_non_dependent_function
env' c ((Toexpand (name,s))::c_args_rev)
(lift 1 t) (s::f_args_rev) (he::a_rev) tl
| false, _ ->
(* dependent product, to replace *)
(*CSC: this limitation is due to the reflexive
implementation and it is hard to lift *)
errorlabstrm "Setoid_replace"
(str "Cannot rewrite in the argument of a " ++
str "dependent product. If you need this " ++
str "feature, please report to the authors.")
end
| _ -> assert false
in
find_non_dependent_function env c [] typ [] []
(Array.to_list a))
| Prod (_, c1, c2) ->
if (dependent (mkRel 1) c2)
then Tokeep in_c
else
let c1m = aux t c1 in
let c2m = aux t c2 in
if ((is_to_replace c1m)||(is_to_replace c2m))
then (Mimp (c1m, c2m))
else Tokeep in_c
| _ -> Tokeep in_c
in aux t in_c
let cic_morphism_context_list_of_list hole_relation =
let rec aux =
function
[] -> assert false
| [out,value] ->
mkApp ((Lazy.force coq_singl), [| Lazy.force coq_Relation_Class ; out |]),
mkApp ((Lazy.force coq_fcl_singl), [| hole_relation; out ; value |])
| (out,value)::tl ->
let outtl, valuetl = aux tl in
mkApp ((Lazy.force coq_cons),
[| Lazy.force coq_Relation_Class ; out ; outtl |]),
mkApp ((Lazy.force coq_fcl_cons),
[| hole_relation ; out ; outtl ; value ; valuetl |])
in aux
let rec cic_type_nelist_of_list =
function
[] -> assert false
| [value] ->
mkApp ((Lazy.force coq_singl), [| mkType (Termops.new_univ ()) ; value |])
| value::tl ->
mkApp ((Lazy.force coq_cons),
[| mkType (Termops.new_univ ()); value; cic_type_nelist_of_list tl |])
let syntactic_but_representation_of_marked_but hole_relation =
let rec aux out =
function
MApp (f, m, args) ->
let morphism_theory, relations =
match m with
ACMorphism { args = args ; lem = lem } -> lem,args
| ACFunction { f_args = f_args ; f_output = f_output } ->
let mt =
if eq_constr out (cic_relation_class_of_relation_class
(Lazy.force coq_prop_relation))
then
mkApp ((Lazy.force coq_morphism_theory_of_predicate),
[| cic_type_nelist_of_list f_args; f|])
else
mkApp ((Lazy.force coq_morphism_theory_of_function),
[| cic_type_nelist_of_list f_args; f_output; f|])
in
mt,List.map (fun x -> ACLeibniz x) f_args in
let cic_relations =
List.map cic_relation_class_of_relation_class relations in
let cic_args_relations,argst =
cic_morphism_context_list_of_list hole_relation
(List.combine cic_relations
(List.map2 (fun t v -> aux t v) cic_relations
(Array.to_list args)))
in
mkApp ((Lazy.force coq_App),
[|hole_relation ; cic_args_relations ; out ; morphism_theory ; argst|])
| Toreplace ->
mkApp ((Lazy.force coq_Toreplace), [| hole_relation |])
| Tokeep c ->
mkApp ((Lazy.force coq_Tokeep), [| hole_relation ; out ; c |])
| Mimp (source, target) ->
mkApp ((Lazy.force coq_Imp),
[| hole_relation ; aux out source ; aux out target |])
in aux
let apply_coq_setoid_rewrite hole_relation c1 c2 (lft2rgt,h) m =
let {refl_aeq=hole_equality; refl_sym=hole_symmetry} = hole_relation in
let hole_relation =
cic_relation_class_of_relation_class (ACReflexive hole_relation) in
let prop_relation =
cic_relation_class_of_relation_class (Lazy.force coq_prop_relation) in
let hyp =
if lft2rgt then h else
match hole_symmetry with
Some sym ->
mkApp (sym, [| c2 ; c1 ; h |])
| None ->
errorlabstrm "Setoid_rewrite"
(str "Rewriting from right to left not permitted since the " ++
str "relation " ++ prterm hole_equality ++ str " is not known " ++
str "to be symmetric. Either declare it as a symmetric " ++
str "relation or use setoid_replace or (setoid_)rewrite from " ++
str "left to right only.")
in
mkApp ((Lazy.force coq_setoid_rewrite),
[| hole_relation ; prop_relation ; c1 ; c2 ;
syntactic_but_representation_of_marked_but hole_relation
prop_relation m ; hyp |])
let relation_rewrite c1 c2 (lft2rgt,cl) gl =
let but = pf_concl gl in
let (hyp,c1,c2) =
let (cl',_) = Clenv.unify_to_subterm cl (c1,but) in
let c1 = Clenv.clenv_instance_term cl' c1 in
let c2 = Clenv.clenv_instance_term cl' c2 in
(lft2rgt,Clenv.clenv_instance_template cl'), c1, c2
in
let input_relation = relation_of_hypothesis_and_term_to_rewrite gl hyp c1 in
let marked_but = mark_occur c1 but in
let th =
apply_coq_setoid_rewrite input_relation c1 c2 hyp marked_but
in
let thty = pf_type_of gl th in
let simplified_thty =
pf_unfoldn [[], Lazy.force coq_iff] gl
(pf_unfoldn [[], Lazy.force coq_relation_of_relation_class] gl thty)
in
let ty1,ty2 =
match destApplication simplified_thty with
(_ (*and*),[|ty1;ty2|]) -> ty1,ty2
| _ -> assert false
in
let th' = mkApp ((Lazy.force coq_proj2), [|ty1; ty2; th|]) in
let hyp2 = Termops.replace_term c1 c2 but in
let simplified_thty' = mkProd (Anonymous, hyp2, lift 1 but) in
(*CSC: the next line does not work because simplified_thty' is not
used to generate the type of the new goals ;-(
Tactics.apply (mkCast (th',simplified_thty')) gl
Thus I am using the following longer (and less accurate) code *)
tclTHENS
(Tactics.apply (mkCast (th',simplified_thty')))
[Tactics.change_in_concl None hyp2] gl
(*CSC: this is another way to proceed, but the generated proof_term
would be much bigger than possible
Tactics.forward true Anonymous (mkCast (th',simplified_thty')) gl
(... followed by the application of the introduced hypothesis ...)
*)
let general_s_rewrite lft2rgt c gl =
let (wc,_) = Evar_refiner.startWalk gl in
let ctype = pf_type_of gl c in
let eqclause =
Clenv.make_clenv_binding wc (c,ctype) Rawterm.NoBindings in
let (equiv, args) =
decompose_app (Clenv.clenv_instance_template_type eqclause) in
let rec get_last_two = function
| [c1;c2] -> (c1, c2)
| x::y::z -> get_last_two (y::z)
| _ -> error "The term provided is not an equivalence" in
let (c1,c2) = get_last_two args in
if lft2rgt
then relation_rewrite c1 c2 (lft2rgt,eqclause) gl
else relation_rewrite c2 c1 (lft2rgt,eqclause) gl
exception Use_replace
(*CSC: the name should be changed *)
let setoid_replace c1 c2 gl =
try
let relation =
match default_relation_for_carrier (pf_type_of gl c1) with
ACReflexive sa -> sa
| ACLeibniz _ -> raise Use_replace
in
let eq = mkApp (relation.refl_aeq, [| c1 ; c2 |]) in
tclTHENS (assert_tac false Anonymous eq)
[onLastHyp (fun id ->
tclTHEN
(general_s_rewrite true (mkVar id))
(clear [id]));
Tacticals.tclIDTAC] gl
with
Use_replace -> (!replace c1 c2) gl
let setoid_rewriteLR = general_s_rewrite true
let setoid_rewriteRL = general_s_rewrite false
|