-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathliftconfig.ml
2004 lines (1934 loc) · 76.9 KB
/
liftconfig.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
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
open Lifting
open Constr
open Environ
open Stateutils
open Caching
open Apputils
open Promotion
open Sigmautils
open Indexing
open Zooming
open Reducers
open Funutils
open Envutils
open Specialization
open Debruijn
open Typehofs
open Hypotheses
open Declarations
open Utilities
open Desugarprod
open Names
open Equtils
open Convertibility
open Unificationutils
open Indutils
open Defutils
open Nameutils
open Hofs
(*
* Lifting configuration: Includes the lifting, types, and cached rules
* for optimizations, as well as interfaces to ask questions about
* the configuration and some initialization code.
*
* This is where lifting constructors and projections live, since those
* are configured ahead of time. Eventually, the bulk of lifting eliminators
* may live here as well.
*)
(* --- Datatype --- *)
(*
* Lifting configuration, along with the types A and B,
* rules for constructors and projections that are configurable by equivalence,
* simplification rules, definitional equality rules,
* a cache for constants encountered as the algorithm traverses,
* and a cache for the constructor rules that refolding determines.
*)
type lift_config =
{
l : lifting;
typs : types * types;
elim_types : types * types;
dep_elims : constr * constr;
dep_constrs : constr array * constr array;
proj_rules :
((constr * constr) list * (types * types) list) *
((constr * constr) list * (types * types) list);
optimize_proj_id_rules :
((constr * (constr -> constr)) list) *
((constr * (constr -> constr)) list);
etas : (constr * constr);
iotas : (constr array * constr array);
cache : temporary_cache;
opaques : temporary_cache
}
(* --- Modifying the configuration --- *)
let reverse c =
{
c with
l = flip_dir c.l;
elim_types = reverse c.elim_types;
dep_elims = reverse c.dep_elims;
dep_constrs = reverse c.dep_constrs;
proj_rules = reverse c.proj_rules;
optimize_proj_id_rules = reverse c.optimize_proj_id_rules;
etas = reverse c.etas;
}
let zoom c =
match c.l.orn.kind with
| Algebraic (indexer, off) ->
let typs = map_tuple shift c.typs in
{ c with typs }
| _ ->
c
(* --- Convenient shorthand --- *)
let convertible env t1 t2 sigma =
if equal t1 t2 then
sigma, true
else
convertible env sigma t1 t2
let rev_tuple = Utilities.reverse
(* --- Recover the lifting --- *)
let get_lifting c = c.l
(* --- Caching --- *)
(*
* Check opaqueness using either local or global cache
*)
let is_opaque c trm =
if is_locally_cached c.opaques trm then
true
else
if equal trm (snd c.dep_elims) then
true
else
lookup_opaque (lift_to c.l, lift_back c.l, trm)
(*
* Configurable caching of constants
*
* Note: The smart global cache works fine if we assume we always lift every
* occurrence of the type. But once we allow for more configurable lifting,
* for example with type-guided search, we will need a smarter smart cache
* to still get this optimization.
*)
let smart_cache c trm lifted =
let l = c.l in
if equal trm lifted then
(* Save the fact that it does not change at all *)
if Options.is_smart_cache () && not (is_opaque c trm) then
save_lifting (lift_to l, lift_back l, trm) trm
else
cache_local c.cache trm trm
else
(* Save the lifted term locally *)
cache_local c.cache trm lifted
(*
* Check if something is in the local cache
*)
let is_cached c trm = is_locally_cached c.cache trm
(*
* Lookup something from the local cache
*)
let lookup_cache c trm = lookup_local_cache c.cache trm
(* --- Types A and B --- *)
(*
* Initialize the types A and B
*)
let initialize_types l env sigma =
let sigma, promote_typ = reduce_type env sigma l.orn.promote in
let (a_i_t, b_i_t) = promotion_type_to_types promote_typ in
match l.orn.kind with
| Algebraic _ ->
let a_t = first_fun a_i_t in
let env_pms = pop_rel_context 1 (zoom_env zoom_product_type env promote_typ) in
let b_t = reconstruct_lambda env_pms (unshift b_i_t) in
sigma, (a_t, b_t)
| CurryRecord ->
let a_t = first_fun a_i_t in
let sigma, b_t = expand_eta env sigma (first_fun b_i_t) in
sigma, (a_t, b_t)
| SwapConstruct _ ->
let a_t = first_fun a_i_t in
let b_t = first_fun b_i_t in
sigma, (a_t, b_t)
| UnpackSigma ->
let env_promote = zoom_env zoom_product_type env promote_typ in
let env_typs = pop_rel_context 1 env_promote in
let a_t = reconstruct_lambda env_typs a_i_t in
let b_t = reconstruct_lambda env_typs (unshift b_i_t) in
sigma, (a_t, b_t)
| Custom typs ->
sigma, typs
let get_types c = c.typs
(*
* Optimization for is_from: things to try rather than trying unification,
* if our type is in a format that is very easy to reason about without
* unification.
*)
let rec optimize_is_from c env typ sigma =
let (a_typ, b_typ) = c.typs in
let goal_typ = if c.l.is_fwd then a_typ else b_typ in
if c.l.is_fwd then
match c.l.orn.kind with
| UnpackSigma ->
(try
let eq_sig_typ = dest_sigT typ in
let sig_typ = dest_sigT eq_sig_typ.index_type in
let rev_typ = sig_typ.packer in
let sigma, rev_typ = expand_eta env sigma rev_typ in
let sigma, typ_args_o = optimize_is_from (reverse c) env rev_typ sigma in
if Option.has_some typ_args_o then
let packer = eq_sig_typ.packer in
let index = unshift (last_arg (zoom_term zoom_lambda_term env packer)) in
let goal_typ = mkAppl (goal_typ, snoc index (Option.get typ_args_o)) in
let sigma, goal_typ = reduce_term env sigma goal_typ in
let goal_packer = (dest_sigT goal_typ).packer in
if equal packer goal_packer then
sigma, typ_args_o
else
sigma, None
else
sigma, None
with _ ->
sigma, None)
| _ ->
if is_or_applies goal_typ typ then
sigma, Some (unfold_args typ)
else
sigma, None
else
match c.l.orn.kind with
| Algebraic _ ->
(try
let sig_typ = dest_sigT typ in
let b_typ = sig_typ.packer in
let unpacked_typ = first_fun (zoom_term zoom_lambda_term env b_typ) in
let elim_typ = fst c.elim_types in
if equal unpacked_typ elim_typ then
let b = zoom_term zoom_lambda_term env b_typ in
let args = unshift_all (deindex c.l (unfold_args b)) in
let sigma, goal_app = reduce_term env sigma (mkAppl (goal_typ, args)) in
let index_type = sig_typ.index_type in
if equal index_type (dest_sigT goal_app).index_type then
sigma, Some args
else
sigma, None
else
sigma, None
with _ ->
sigma, None)
| SwapConstruct _ | Custom _ ->
if is_or_applies goal_typ typ then
sigma, Some (unfold_args typ)
else
sigma, None
| UnpackSigma ->
let goal_ind = first_fun (zoom_term zoom_lambda_term env goal_typ) in
let sigma, typ = reduce_term env sigma typ in
if is_or_applies goal_ind typ then
sigma, Some (unfold_args typ)
else
sigma, None
| _ ->
sigma, None
(*
* Determine whether a type is the type we are ornamenting from
* That is, A when we are promoting, and B when we are forgetting
* Return the arguments to the type if so
*)
let is_from c env typ sigma =
let sigma, args_o = optimize_is_from c env typ sigma in
if Option.has_some args_o then
sigma, args_o
else
let (a_typ, b_typ) = c.typs in
let goal_typ = if c.l.is_fwd then a_typ else b_typ in
e_is_from env goal_typ typ sigma
(*
* Just get all of the unfolded arguments, skipping the check
*)
let from_args c env trm sigma =
Util.on_snd Option.get (is_from c env trm sigma)
(*
* Determine whether a term has the type we are ornamenting from
* Return the arguments to the type if so
*)
let type_is_from c env trm sigma =
try
let sigma, typ = reduce_type env sigma trm in
is_from c env typ sigma
with _ ->
sigma, None
(*
* Just return the arguments to from, assuming term has the right type,
* skipping the check
*)
let type_from_args c env trm sigma =
on_red_type
reduce_term
(fun env sigma typ -> from_args c env typ sigma)
env
sigma
trm
(* --- Eta, iota, and coherence (for definitional equality) --- *)
(*
* Initialize the rules for lifting projections
* This is COHERENCE, but cached
*
* A lot of this will likely go into Eta or Iota soon, at least what is
* not here just for optimizaiton pruposes
*)
let initialize_proj_rules c env sigma =
let l = get_lifting c in
let init c sigma =
let l = { l with is_fwd = true } in
let sigma, lift_typ = reduce_type env sigma (lift_to l) in
let sigma, lift_typ_inv = reduce_type env sigma (lift_back l) in
let env_a, b_typ = zoom_product_type env lift_typ in
let env_b, a_typ = zoom_product_type env lift_typ_inv in
let t = mkRel 1 in
match l.orn.kind with
| Algebraic (indexer, _) ->
(* indexer <-> projT1, id(_typ) <- (rew ... in projT2(_typ) *)
let sigma, b_sig = Util.on_snd dest_sigT (reduce_type env_b sigma t) in
let projT1 = reconstruct_lambda env_b (project_index b_sig t) in
let projT2 = reconstruct_lambda env_b (project_value b_sig t) in
let rew_projT2 =
let index_type = b_sig.index_type in
let env_index = push_local (Anonymous, index_type) env_b in
let env_eq =
let eq_typ =
let at_type = shift index_type in
let trm1 = shift (project_index b_sig t) in
let trm2 = mkRel 1 in
apply_eq { at_type; trm1; trm2 }
in push_local (Anonymous, eq_typ) env_index
in
let rew =
let index_type = shift_by 2 index_type in
let proj_index = shift_by 2 (project_index b_sig t) in
let packer = shift_by 2 b_sig.packer in
let b = shift_by 2 (project_value b_sig t) in
let index = mkRel 2 in
let eq = mkRel 1 in
mkAppl (eq_rect, [index_type; proj_index; packer; b; index; eq])
in reconstruct_lambda env_eq rew
in
let indexer =
let args = mk_n_rels (nb_rel env_a) in
reconstruct_lambda env_a (mkAppl (indexer, args))
in
let id = reconstruct_lambda env_a t in
let rew_id =
let index_type = b_sig.index_type in
let env_index = push_local (Anonymous, index_type) env_a in
let env_eq =
let eq_typ =
let at_type = shift index_type in
let trm1 = mkAppl (indexer, shift_all (mk_n_rels (nb_rel env_a))) in
let trm2 = mkRel 1 in
apply_eq { at_type; trm1; trm2 }
in push_local (Anonymous, eq_typ) env_index
in reconstruct_lambda env_eq (shift_by 2 t)
in
let projT2_typ = reconstruct_lambda (pop_rel_context 1 env_b) (unshift b_sig.packer) in
let env_id_typ = zoom_env zoom_lambda_term env projT2_typ in
let id_typ = reconstruct_lambda env_id_typ a_typ in
let a_rules = [(indexer, projT1)], [] in
let b_rules = [(projT1, indexer); (projT2, id); (rew_projT2, rew_id)], [(projT2_typ, id_typ)] in
sigma, (a_rules, b_rules)
| UnpackSigma ->
let sigma, a_typ = reduce_type env_a sigma t in
let a_sig_sig = dest_sigT a_typ in
let a_inner_typ = a_sig_sig.index_type in
let a_sig = dest_sigT a_inner_typ in
let sigma, (index_type, index) =
let sigma, args = unpack_typ_args env_a a_typ sigma in
sigma, (List.hd args, last args)
in
let p1 =
let packer = a_sig.packer in
let indexer = pack_existT { index_type; packer; index; unpacked = t } in
reconstruct_lambda env_b indexer
in
let proj_bods = projections a_sig_sig t in
let fwd_rules =
(* projT1 -> pack, projT2 -> eq_refl *)
let projT1, projT2 = map_tuple (reconstruct_lambda env_a) proj_bods in
let p2 =
let eq = apply_eq_refl { typ = index_type; trm = index } in
reconstruct_lambda env_b eq
in [(projT1, p1); (projT2, p2)], []
in
let sigma, bwd_rules =
(* pack ... eq_refl -> pack ... (rew ... in projT2) *)
(* in addition, opaque types so they match *)
let p1_typ = reconstruct_lambda (pop_rel_context 2 env_b) (unshift_by 2 a_inner_typ) in
let p2_typ = reconstruct_lambda (pop_rel_context 1 env_b) (unshift a_sig_sig.packer) in
let sigma, projT1 =
let packer = a_sig.packer in
let index = mkRel 2 in
let unpacked =
let proj_index = project_index a_sig (fst proj_bods) in
let b = project_value a_sig (fst proj_bods) in
let eq = snd proj_bods in
mkAppl (eq_rect, [index_type; proj_index; packer; b; index; eq])
in
let proj_bod = pack_existT { index_type; packer; index; unpacked } in
sigma, reconstruct_lambda env_b proj_bod
in sigma, ([(p1, projT1)], [(p1_typ, p1_typ); (p2_typ, p2_typ)])
in sigma, (fwd_rules, bwd_rules)
| CurryRecord ->
(* accessors <-> projections *)
let accessors =
let (a_typ, _) = get_types c in
let ((i, i_index), u) = destInd a_typ in
let accessor_opts = Recordops.lookup_projections (i, i_index) in
let args = mk_n_rels (nb_rel env_a) in
try
List.map (fun a_o -> reconstruct_lambda env_a (mkAppl ((mkConst (Option.get a_o)), args))) accessor_opts
with _ ->
[]
in
let sigma, projections =
let sigma, p_bodies = prod_projections_rec env_b t sigma in
map_state (fun p -> ret (reconstruct_lambda env_b p)) p_bodies sigma
in
if List.length accessors = List.length projections then
let sigma, fwd_rules =
map2_state (fun p1 p2 -> ret (p1, p2)) accessors projections sigma
in
let bwd_rules = List.map rev_tuple fwd_rules in
sigma, ((fwd_rules, []), (bwd_rules, []))
else
let _ =
Feedback.msg_warning
(Pp.str "Can't find record accessors; skipping an optimization")
in sigma, (([], []), ([], []))
| SwapConstruct _ | Custom _ ->
(* no projections *)
sigma, (([], []), ([], []))
in
let sigma, proj_rules = Util.on_snd (map_backward rev_tuple l) (init c sigma) in
sigma, { c with proj_rules }
(*
* Define what it means to lift the identity function, since we must
* preserve definitional equalities.
*)
let initialize_etas c cached env sigma =
let l = get_lifting c in
let sigma, etas =
if Option.has_some cached then
(* Use the cached eta rules *)
let (_, _, etas, _) = Option.get cached in
sigma, etas
else
(* Determine the eta rules and cache them for later *)
let (a_typ, b_typ) = get_types c in
let sigma, fwd_typ = reduce_type env sigma (lift_to l) in
let sigma, bwd_typ = reduce_type env sigma (lift_back l) in
let sigma, eta_a =
let env_eta = zoom_env zoom_product_type env (if l.is_fwd then fwd_typ else bwd_typ) in
let a = mkRel 1 in
match l.orn.kind with
| UnpackSigma ->
(* eta for nested sigT *)
let typ_args = shift_all (mk_n_rels (nb_rel env_eta - 1)) in
let sigma, typ = reduce_term env_eta sigma (mkAppl (a_typ, typ_args)) in
let s_eq_typ = dest_sigT typ in
let index_type = s_eq_typ.index_type in
let packer = s_eq_typ.packer in
let s, unpacked = projections s_eq_typ a in
let sigma, index =
let sigma, typ = reduce_type env_eta sigma s in
let s_typ = dest_sigT typ in
let index_type = s_typ.index_type in
let packer = s_typ.packer in
let index, unpacked = projections s_typ s in
sigma, pack_existT { index_type; packer; index; unpacked}
in
let e = pack_existT {index_type; packer; index; unpacked} in
sigma, reconstruct_lambda env_eta e
| Algebraic _ | CurryRecord | SwapConstruct _ | Custom _ ->
(* identity *)
sigma, reconstruct_lambda env_eta a
in
let sigma, eta_b =
let env_eta = zoom_env zoom_product_type env (if l.is_fwd then bwd_typ else fwd_typ) in
let b = mkRel 1 in
match l.orn.kind with
| Algebraic _ ->
(* eta for sigT *)
let typ_args = shift_all (mk_n_rels (nb_rel env_eta - 1)) in
let sigma, typ = reduce_term env_eta sigma (mkAppl (b_typ, typ_args)) in
let s_typ = dest_sigT typ in
let index_type = s_typ.index_type in
let packer = s_typ.packer in
let index, unpacked = projections s_typ b in
let e = pack_existT {index_type; packer; index; unpacked} in
sigma, reconstruct_lambda env_eta e
| CurryRecord ->
(* eta for nested prod *)
let typ_args = shift_all (mk_n_rels (nb_rel env_eta - 1)) in
let sigma, typ = reduce_term env_eta sigma (mkAppl (b_typ, typ_args)) in
let f = first_fun typ in
let args = unfold_args typ in
let sigma, typ_red = specialize_delta_f env_eta f args sigma in
sigma, reconstruct_lambda env_eta (eta_prod_rec b typ_red)
| UnpackSigma ->
(* rewrite in pack (identity at eq_refl) *)
let sigma, (env_eq, (eq, eq_typ), (b, b_typ)) =
let push_anon t = push_local (Anonymous, t) in
let env_sig = zoom_env zoom_lambda_term env a_typ in
let sigma, (i_b_typ, b_typ, i_b) =
let sig_eq = mkAppl (a_typ, mk_n_rels (nb_rel env_sig)) in
let sigma, sig_eq = reduce_term env_sig sigma sig_eq in
let sigma, typ_args = unpack_typ_args env_sig sig_eq sigma in
sigma, (List.hd typ_args, List.hd (List.tl typ_args), last typ_args)
in
let env_i_b = push_anon i_b_typ env_sig in
let env_b = push_anon (mkAppl (shift b_typ, [mkRel 1])) env_i_b in
let eq_typ =
let at_type = shift_by 2 i_b_typ in
apply_eq { at_type; trm1 = mkRel 2; trm2 = shift_by 2 i_b }
in
let env_eq = push_anon eq_typ env_b in
sigma, (env_eq, (mkRel 1, shift eq_typ), (mkRel 2, shift_by 3 b_typ))
in
let eq_typ_app = dest_eq eq_typ in
let rewrite =
let at_type = eq_typ_app.at_type in
let trm1 = eq_typ_app.trm1 in
let trm2 = eq_typ_app.trm2 in
mkAppl (eq_rect, [at_type; trm1; b_typ; b; trm2; eq])
in sigma, reconstruct_lambda_n env_eq rewrite (nb_rel env)
| SwapConstruct _ | Custom _ ->
(* identity *)
sigma, reconstruct_lambda env_eta b
in
let etas =
let eta_a_n, eta_b_n =
let promote = Constant.canonical (fst (destConst l.orn.promote)) in
let (_, _, lab) = KerName.repr promote in
let base_n = Label.to_id lab in
(with_suffix base_n "eta_a", with_suffix base_n "eta_b")
in
let eta_a, eta_b = ((eta_a_n, eta_a), (eta_b_n, eta_b)) in
try
let eta_a = define_term (fst eta_a) sigma (snd eta_a) true in
let eta_b = define_term (fst eta_b) sigma (snd eta_b) true in
map_tuple UnivGen.constr_of_global (eta_a, eta_b)
with _ ->
snd eta_a, snd eta_b
in save_eta (l.orn.promote, l.orn.forget) etas; sigma, etas
in
let etas = if l.is_fwd then etas else rev_tuple etas in
let env = Global.env () in
let etas = map_tuple (unwrap_definition env) etas in
sigma, { c with etas }
(*
* Define what it means to lift equality proofs.
* TODO implement trivial useless iotas for search procedures
*)
let initialize_iotas c cached env sigma =
let l = get_lifting c in
let sigma, iotas =
if Option.has_some cached then
(* Use the cached rew rules *)
let (_, _, _, iotas) = Option.get cached in
sigma, iotas
else
(* Determine the rew rules and cache them for later *)
let sigma, fwd_typ = reduce_type env sigma (lift_to l) in
let sigma, bwd_typ = reduce_type env sigma (lift_back l) in
let sigma, iota_a =
map_state_array
(fun _ sigma -> (* TODO args should be case of dep_elim later *)
let env_iota = zoom_env zoom_product_type env (if l.is_fwd then fwd_typ else bwd_typ) in
let a = mkRel 1 in
let sigma, a_typ = reduce_type env_iota sigma a in
let iota_a_bod = apply_eq_refl { typ = a_typ; trm = mkRel 1 } in
sigma, reconstruct_lambda_n env_iota iota_a_bod (nb_rel env))
((if l.is_fwd then fst else snd) c.dep_constrs)
sigma
in
let sigma, iota_b =
map_state_array
(fun _ sigma -> (* TODO args should be case of elim later *)
let env_iota = zoom_env zoom_product_type env (if l.is_fwd then bwd_typ else fwd_typ) in
let b = mkRel 1 in
let sigma, b_typ = reduce_type env_iota sigma b in
let iota_b_bod = apply_eq_refl { typ = b_typ; trm = mkRel 1 } in
sigma, reconstruct_lambda_n env_iota iota_b_bod (nb_rel env))
((if l.is_fwd then snd else fst) c.dep_constrs)
sigma
in
let iotas =
let iota_a_n, iota_b_n =
let promote = Constant.canonical (fst (destConst l.orn.promote)) in
let (_, _, lab) = KerName.repr promote in
let base_n = Label.to_id lab in
(with_suffix base_n "iota_a", with_suffix base_n "iota_b")
in
let iota_a, iota_b = ((iota_a_n, iota_a), (iota_b_n, iota_b)) in
let iota_as =
Array.mapi
(fun i rew ->
let n = with_suffix (fst iota_a) (string_of_int i) in
define_term n sigma rew true)
(snd iota_a)
in
let iota_bs =
Array.mapi
(fun i rew ->
let n = with_suffix (fst iota_b) (string_of_int i) in
define_term n sigma rew true)
(snd iota_b)
in map_tuple (Array.map UnivGen.constr_of_global) (iota_as, iota_bs)
in save_iota (l.orn.promote, l.orn.forget) iotas; sigma, iotas
in
let iotas = if l.is_fwd then iotas else rev_tuple iotas in
let env = Global.env () in
let iotas = map_tuple (Array.map (unwrap_definition env)) iotas in
sigma, { c with iotas }
(*
* Get the map of projections for the type
*)
let get_proj_map c = fst c.proj_rules
(* Unification can be slow, so sometimes we can do better ourselves *)
let optimize_is_proj c env trm proj_is sigma =
let l = get_lifting c in
match l.orn.kind with
| UnpackSigma ->
if l.is_fwd then
let args = unfold_args trm in
let isProjT1 = is_or_applies projT1 trm in
let isProjT2 = is_or_applies projT2 trm in
if List.length args = 3 && (isProjT1 || isProjT2) then
let packed = last args in
let sigma, typ_args_o = type_is_from c env packed sigma in
if Option.has_some typ_args_o then
let typ_args = Option.get typ_args_o in
if isProjT1 then
sigma, Some (0, snoc packed typ_args, trm)
else
sigma, Some (1, snoc packed typ_args, trm)
else
sigma, None
else
sigma, None
else
if is_or_applies existT trm then
try
let ex = dest_existT trm in
let packer = ex.packer in
let index = ex.index in
let unpacked = ex.unpacked in
let sigma, typ_args_o = is_from c env packer sigma in
if Option.has_some typ_args_o then
let typ_args = Option.get typ_args_o in
sigma, Some (0, snoc unpacked (snoc index typ_args), trm)
else
sigma, None
with _ ->
sigma, None
else
sigma, None
| _ ->
sigma, None
(* Check if a term applies a given projection *)
let check_is_proj c env trm proj_is sigma =
let sigma, is_proj = optimize_is_proj c env trm proj_is sigma in
if Option.has_some is_proj then
sigma, is_proj
else
match kind trm with
| App _ | Const _ -> (* this check is an optimization *)
let f = first_fun trm in
let rec check_is_proj_i i proj_is =
match proj_is with
| proj_i :: tl ->
let proj_i_f = first_fun (zoom_term zoom_lambda_term env proj_i) in
branch_state
(convertible env proj_i_f) (* this check is an optimization *)
(fun _ sigma ->
try
let sigma, trm_eta = expand_eta env sigma trm in
let env_b, b = zoom_lambda_term env trm_eta in
let args = unfold_args b in
if List.length args = 0 then
check_is_proj_i (i + 1) tl sigma
else
(* attempt unification *)
let sigma, eargs = mk_n_evars (arity proj_i) env_b sigma in
let proj_app = mkAppl (proj_i, eargs) in
let sigma, resolved = unify_resolve_evars env_b b proj_app sigma in
if Option.has_some resolved then
let (_, proj_app) = Option.get resolved in
let args = unfold_args proj_app in
sigma, Some (i, args, trm_eta)
else
check_is_proj_i (i + 1) tl sigma
with _ ->
check_is_proj_i (i + 1) tl sigma)
(fun _ -> check_is_proj_i (i + 1) tl)
f
| _ ->
ret None
in check_is_proj_i 0 proj_is sigma
| _ ->
ret None sigma
(* Check if a term applies any projection *)
let is_proj c env trm sigma =
let proj_term_rules, proj_type_rules = get_proj_map c in
if List.length proj_term_rules = 0 then
sigma, None
else
let rec check proj_maps sigma =
match proj_maps with
| proj_map :: tl ->
let proj_terms = List.map fst proj_map in
let sigma, to_proj_o = check_is_proj c env trm proj_terms sigma in
if Option.has_some to_proj_o then
let i, args, trm_eta = Option.get to_proj_o in
let (_, proj) = List.nth proj_map i in
sigma, Some (proj, args, trm_eta)
else
check tl sigma
| _ ->
sigma, None
in check [proj_term_rules; proj_type_rules] sigma
(*
* Get the lifted eta expansion function
*)
let get_lifted_eta c = snd c.etas
(*
* Check if a term may apply the eta expansion function,
* but don't bother checking the type
*)
let may_apply_eta c env trm =
(* Heuristic for unification without slowdown *)
if equal (zoom_term zoom_lambda_term env (fst c.etas)) (mkRel 1) then
true
else
let l = get_lifting c in
match l.orn.kind with
| Algebraic _ ->
is_or_applies existT trm || is_or_applies (lift_back l) trm
| UnpackSigma ->
if l.is_fwd then
is_or_applies existT trm || is_or_applies (lift_back l) trm
else
true
| CurryRecord ->
is_or_applies pair trm || is_or_applies (lift_back l) trm
| SwapConstruct _ ->
false (* impossible state *)
| Custom _ ->
true (* not enough information without unification *)
(*
* Check if a term applies the eta expansion function function
*)
let applies_eta c env trm sigma =
if may_apply_eta c env trm then
let sigma, typ_args_o = type_is_from c env trm sigma in
let opt_proj_map = snd c.optimize_proj_id_rules in
(* Heuristic for unification again *)
if Option.has_some typ_args_o then
let typ_args = Option.get typ_args_o in
let is_custom = match c.l.orn.kind with | Custom _ -> true | _ -> false in
if (not is_custom) && equal (zoom_term zoom_lambda_term env (fst c.etas)) (mkRel 1) then
sigma, Some (snoc trm typ_args)
else
let l = get_lifting c in
if is_or_applies (lift_back l) trm then
sigma, Some (snoc trm typ_args)
else
match l.orn.kind with
| Algebraic _ ->
let proj_value = snd (last opt_proj_map) in
let proj_arg = proj_value trm in
sigma, Some (snoc proj_arg typ_args)
| UnpackSigma ->
if l.is_fwd then
let projT1, proj_index = List.hd opt_proj_map in
let projT2, proj_value = last opt_proj_map in
let s, h_eq = proj_index trm, proj_value trm in
let sigma, b_sig_eq =
let b_sig_eq_typ = mkAppl (fst (get_types c), typ_args) in
Util.on_snd dest_sigT (reduce_term env sigma b_sig_eq_typ)
in
let i_b, b =
if is_or_applies existT s then
proj_index s, proj_value s
else
projections (dest_sigT b_sig_eq.index_type) s
in sigma, Some (List.append typ_args [i_b; b; h_eq])
else
if is_or_applies eq_rect trm || is_or_applies eq_ind trm || is_or_applies eq_rec trm then
let sigma, trm = expand_eta env sigma trm in
let eq_args = Array.of_list (unfold_args trm) in
let i_b_typ = eq_args.(0) in
let i_b = eq_args.(1) in
let b_typ = eq_args.(2) in
let b = eq_args.(3) in
let i_b' = eq_args.(4) in
let h_eq = eq_args.(5) in
let packed =
let index_type =
let packer =
let unpacked = mkAppl (shift b_typ, [mkRel 1]) in
mkLambda (Anonymous, i_b_typ, unpacked)
in pack_sigT { index_type = i_b_typ; packer }
in
let packer =
let at_type = shift i_b_typ in
let trm1 = project_index (dest_sigT (shift index_type)) (mkRel 1) in
let trm2 = shift i_b' in
mkLambda (Anonymous, index_type, apply_eq { at_type; trm1; trm2 })
in
let index =
let index_type_app = dest_sigT index_type in
let packer = index_type_app.packer in
pack_existT { index_type = i_b_typ; packer; index = i_b; unpacked = b }
in pack_existT { index_type; packer; index; unpacked = h_eq }
in sigma, Some (snoc packed typ_args)
else
let sigma, packed =
if isRel trm then
sigma, trm
else
let sigma, b_sig_eq =
let b_sig_eq_typ = mkAppl (fst (get_types c), typ_args) in
Util.on_snd dest_sigT (reduce_term env sigma b_sig_eq_typ)
in
let index_type = b_sig_eq.index_type in
let packer = b_sig_eq.packer in
let index, unpacked =
let b_sig = dest_sigT index_type in
let index_type = b_sig.index_type in
let index_index = last typ_args in
let index =
if is_or_applies projT1 index_index && is_or_applies projT2 trm then
last_arg trm
else
let packer = b_sig.packer in
let index = last typ_args in
let unpacked = trm in
pack_existT { index_type; packer; index; unpacked }
in
let unpacked = apply_eq_refl { typ = index_type; trm = index_index } in
index, unpacked
in sigma, pack_existT { index_type; packer; index; unpacked }
in sigma, Some (snoc packed typ_args)
| CurryRecord ->
sigma, Some (snoc trm typ_args)
| SwapConstruct _ ->
sigma, None (* impossible state *)
| Custom _ ->
(* attempt unification *)
let eta = fst c.etas in
let sigma, eargs = mk_n_evars (arity eta) env sigma in
let eta_app = mkAppl (eta, eargs) in
let sigma, resolved = unify_resolve_evars env trm eta_app sigma in
if Option.has_some resolved then
let (_, eta_app) = Option.get resolved in
let args = unfold_args eta_app in
sigma, Some args
else
sigma, None
else
sigma, None
else
sigma, None
let get_iota c = fst c.iotas
let get_lifted_iota c = snd c.iotas
(*
* When iota is not rewriting by reflexivity, check if we apply Iota
*)
let applies_iota c env trm sigma =
match c.l.orn.kind with
| Custom _ ->
(* no custom unification yet---require explicit expansion *)
sigma, None
| _ ->
sigma, None
(* --- Smart simplification (for termination and efficiency) --- *)
(*
* Sometimes we can do better than Coq's reduction and simplify eagerly.
* In particular, this happens when we lift to projections of the eta-expanded
* identity functions, like (projT1 (existT _ n v)).
* When subterms recursively refer to the original type, like in UnpackSigma,
* this also helps ensure that the algorithm terminates by simplifying away
* redundant terms.
*)
let initialize_optimize_proj_id_rules c env sigma =
let l = get_lifting c in
let rules_fwd =
match l.orn.kind with
| Algebraic (_, _) ->
let proj1_rule = (fun a -> (dest_existT a).index) in
let proj2_rule = (fun a -> (dest_existT a).unpacked) in
[(projT1, proj1_rule); (projT2, proj2_rule)]
| CurryRecord ->
let proj1_rule = (fun a -> (dest_pair a).Produtils.trm1) in
let proj2_rule = (fun a -> (dest_pair a).Produtils.trm2) in
[(Desugarprod.fst_elim (), proj1_rule); (Desugarprod.snd_elim (), proj2_rule)]
| SwapConstruct _ | UnpackSigma | Custom _ ->
[]
in
let rules_bwd =
match l.orn.kind with
| UnpackSigma ->
(* not the best we can do *)
let proj1_rule = (fun a -> (dest_existT a).index) in
let proj2_rule = (fun a -> (dest_existT a).unpacked) in
[(projT1, proj1_rule); (projT2, proj2_rule)]
| SwapConstruct _ | Algebraic (_, _) | CurryRecord | Custom _ ->
[]
in
let optimize_proj_id_rules =
if l.is_fwd then
(rules_fwd, rules_bwd)
else
(rules_bwd, rules_fwd)
in sigma, { c with optimize_proj_id_rules }
(*
* Determine if we can probably be smarter than Coq and simplify earlier
* If yes, return how
* Otherwise, return None
*
* Sometimes, we need this for termination when lifted terms can be
* self-referrential
*)
let can_reduce_now c env trm =
match kind trm with
| App (_, args) when Array.length args > 0 ->
let proj_packed_map = fst c.optimize_proj_id_rules in
let optimize_proj_packed_o =
try
Some
(List.find
(fun (pr, _) -> is_or_applies pr trm)
proj_packed_map)
with _ ->
None
in
if Option.has_some optimize_proj_packed_o then
let _, reduce = Option.get optimize_proj_packed_o in
Some (fun _ sigma trm -> sigma, reduce trm)
else
None
| _ ->
None
(*
* Custom reduction function for coherence,
* for efficiency and to ensure termination. For example, this may
* simplify projections of existentials.
*)
let reduce_coh c env sigma trm =
let l = get_lifting c in
let rec reduce_arg c env sigma arg =
let sigma, arg = reduce_term env sigma arg in
let how_reduce_o = can_reduce_now c env arg in
if Option.has_some how_reduce_o then
let proj_a = Option.get how_reduce_o in
let arg_inner = last_arg arg in
let sigma, arg_inner = reduce_arg c env sigma arg_inner in
if may_apply_eta (reverse c) env arg_inner then
let sigma, projected = proj_a env sigma arg_inner in
reduce_arg c env sigma projected
else
sigma, arg
else if isApp arg then
let f = first_fun arg in
let args = unfold_args arg in
let sigma, args =
map_state
(fun trm sigma ->
reduce_arg c env sigma trm)
args
sigma
in sigma, mkAppl (f, args)
else
sigma, arg
in
match l.orn.kind with
| UnpackSigma when not l.is_fwd ->
let sigma, trm = reduce_term env sigma trm in
if is_or_applies existT trm then
let ex = dest_existT trm in
let sigma, index = reduce_arg c env sigma ex.index in