-
Notifications
You must be signed in to change notification settings - Fork 0
/
FEM.Process.fst
3177 lines (2901 loc) · 122 KB
/
FEM.Process.fst
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
module FEM.Process
module HS = FStar.HyperStack
module ST = FStar.HyperStack.ST
module B = LowStar.Buffer
open FStar.List
open FStar.Tactics
open FStar.Mul
#push-options "--z3rlimit 15 --fuel 0 --ifuel 1"
(* TODO: move to FStar.Tactics.Util.fst *)
#push-options "--ifuel 1"
val iteri_aux: int -> (int -> 'a -> Tac unit) -> list 'a -> Tac unit
let rec iteri_aux i f x = match x with
| [] -> ()
| a::tl -> f i a; iteri_aux (i+1) f tl
val iteri: (int -> 'a -> Tac unit) -> list 'a -> Tac unit
let iteri f x = iteri_aux 0 f x
#pop-options
val tryPick: ('a -> Tac (option 'b)) -> list 'a -> Tac (option 'b)
let rec tryPick f l = match l with
| [] -> None
| hd::tl ->
match f hd with
| Some x -> Some x
| None -> tryPick f tl
val filter: ('a -> Tac bool) -> list 'a -> Tac (list 'a)
let rec filter f = function
| [] -> []
| hd::tl -> if f hd then hd::(filter f tl) else filter f tl
(* TODO: move to FStar.Tactics.Derived.fst *)
let rec mk_abs (t : term) (args : list binder) : Tac term (decreases args) =
match args with
| [] -> t
| a :: args' ->
let t' = mk_abs t args' in
pack (Tv_Abs a t')
val bv_eq : bv -> bv -> Tot bool
let bv_eq (bv1 bv2 : bv) =
let bvv1 = inspect_bv bv1 in
let bvv2 = inspect_bv bv2 in
(* We don't check for type equality: the fact that no two different binders
* have the same name and index is an invariant which must be enforced -
* and actually we could limit ourselves to checking the index *)
bvv1.bv_ppname = bvv2.bv_ppname && bvv1.bv_index = bvv2.bv_index
val name_eq : name -> name -> Tot bool
let rec name_eq n1 n2 =
match n1, n2 with
| [], [] -> true
| x1::n1', x2::n2' ->
x1 = x2 && name_eq n1' n2'
| _ -> false
val fv_eq : fv -> fv -> Tot bool
let fv_eq fv1 fv2 =
let n1 = inspect_fv fv1 in
let n2 = inspect_fv fv2 in
name_eq n1 n2
// TODO: use everywhere
val fv_eq_name : fv -> name -> Tot bool
let fv_eq_name fv n =
let fvn = inspect_fv fv in
name_eq fvn n
(*** Debugging *)
/// Some debugging facilities
val print_dbg : bool -> string -> Tac unit
let print_dbg debug s =
if debug then print s
/// Return the qualifier of a term as a string
val term_view_construct (t : term_view) : Tac string
let term_view_construct (t : term_view) : Tac string =
match t with
| Tv_Var _ -> "Tv_Var"
| Tv_BVar _ -> "Tv_BVar"
| Tv_FVar _ -> "Tv_FVar"
| Tv_App _ _ -> "Tv_App"
| Tv_Abs _ _ -> "Tv_Abs"
| Tv_Arrow _ _ -> "Tv_Arrow"
| Tv_Type _ -> "Tv_Type"
| Tv_Refine _ _ -> "Tv_Refine"
| Tv_Const _ -> "Tv_Const"
| Tv_Uvar _ _ -> "Tv_Uvar"
| Tv_Let _ _ _ _ _ -> "Tv_Let"
| Tv_Match _ _ -> "Tv_Match"
| Tv_AscribedT _ _ _ -> "Tv_AscribedT"
| Tv_AscribedC _ _ _ -> "Tv_AScribedC"
| _ -> "Tv_Unknown"
val term_construct (t : term) : Tac string
let term_construct (t : term) : Tac string =
term_view_construct (inspect t)
(*** Pretty-printing *)
/// There are many issues linked to terms (pretty) printing.
/// The first issue is that when parsing terms, F* automatically inserts
/// abscriptions, which then clutter the terms printed to the user. The current
/// workaround is to filter those ascriptions in the terms before exploiting them.
val filter_ascriptions : bool -> term -> Tac term
let rec filter_ascriptions dbg t =
print_dbg dbg ("[> filter_ascriptions: " ^ term_view_construct t ^ ": " ^ term_to_string t );
match inspect t with
| Tv_Var _ | Tv_BVar _ | Tv_FVar _ -> t
| Tv_App hd (a,qual) ->
let hd = filter_ascriptions dbg hd in
let a = filter_ascriptions dbg a in
pack (Tv_App hd (a, qual))
| Tv_Abs br body ->
let body = filter_ascriptions dbg body in
pack (Tv_Abs br body)
| Tv_Arrow br c0 -> t (* TODO: we might want to explore that *)
| Tv_Type () -> t
| Tv_Refine bv ref ->
(* TODO: also filter the type of the bv *)
let ref = filter_ascriptions dbg ref in
pack (Tv_Refine bv ref)
| Tv_Const _ -> t
| Tv_Uvar _ _ -> t
| Tv_Let recf attrs bv def body ->
(* The attributes shouldn't need to be filtered *)
let def = filter_ascriptions dbg def in
let body = filter_ascriptions dbg body in
pack (Tv_Let recf attrs bv def body)
| Tv_Match scrutinee branches ->
let scrutinee = filter_ascriptions dbg scrutinee in
(* For the branches: we don't need to explore the patterns *)
let branches = map (fun (pat, tm) -> (pat, filter_ascriptions dbg tm)) branches in
pack (Tv_Match scrutinee branches)
| Tv_AscribedT e _ _
| Tv_AscribedC e _ _ ->
filter_ascriptions dbg e
| _ ->
(* Unknown *)
t
/// Our prettification function. Apply it to all the terms which might be printed
/// back to the user. Note that the time at which the function is applied is
/// important: we can't apply it on all the assertions we export to the user, just
/// before exporting, because we may have inserted ascriptions on purpose, which
/// would then be filtered away.
val prettify_term : bool -> term -> Tac term
let prettify_term dbg t = filter_ascriptions dbg t
(*** General utilities *)
// TODO: use more
val opt_apply (#a #b : Type) (f : a -> Tot b) (x : option a) : Tot (option b)
let opt_apply #a #b f x =
match x with
| None -> None
| Some x' -> Some (f x')
val opt_tapply (#a #b : Type) (f : a -> Tac b) (x : option a) : Tac (option b)
let opt_tapply #a #b f x =
match x with
| None -> None
| Some x' -> Some (f x')
val option_to_string : (#a : Type) -> (a -> Tot string) -> option a -> Tot string
let option_to_string #a f x =
match x with
| None -> "None"
| Some x' -> "Some (" ^ f x' ^ ")"
val list_to_string : #a : Type -> (a -> Tot string) -> list a -> Tot string
let list_to_string #a f ls =
(List.Tot.fold_left (fun s x -> s ^ " (" ^ f x ^ ");") "[" ls) ^ "]"
/// Alternative ``bv_to_string`` function where we print the index of the bv.
/// It can be very useful for debugging.
let abv_to_string bv : Tot string =
let bvv = inspect_bv bv in
bvv.bv_ppname ^ " (%" ^ string_of_int (bvv.bv_index) ^ ") : " ^
term_to_string bvv.bv_sort
let print_binder_info (full : bool) (b : binder) : Tac unit =
let bv, a = inspect_binder b in
let a_str = match a with
| Q_Implicit -> "Implicit"
| Q_Explicit -> "Explicit"
| Q_Meta t -> "Meta: " ^ term_to_string t
| Q_Meta_attr t -> "Meta attribute: " ^ term_to_string t
in
let bview = inspect_bv bv in
if full then
print (
"> print_binder_info:" ^
"\n- name: " ^ (name_of_binder b) ^
"\n- as string: " ^ (binder_to_string b) ^
"\n- aqual: " ^ a_str ^
"\n- ppname: " ^ bview.bv_ppname ^
"\n- index: " ^ string_of_int bview.bv_index ^
"\n- sort: " ^ term_to_string bview.bv_sort
)
else print (binder_to_string b)
let print_binders_info (full : bool) (e:env) : Tac unit =
iter (print_binder_info full) (binders_of_env e)
let acomp_to_string (c:comp) : Tot string =
match inspect_comp c with
| C_Total ret decr ->
"C_Total (" ^ term_to_string ret ^ ")"
| C_GTotal ret decr ->
"C_GTotal (" ^ term_to_string ret ^ ")"
| C_Lemma pre post patterns ->
"C_Lemma (" ^ term_to_string pre ^ ") (" ^ term_to_string post ^ ")"
| C_Eff us eff_name result eff_args ->
let eff_arg_to_string (a : term) : Tot string =
" (" ^ term_to_string a ^ ")"
in
let args_str = List.Tot.map (fun (x, y) -> eff_arg_to_string x) eff_args in
let args_str = List.Tot.fold_left (fun x y -> x ^ y) "" args_str in
"C_Eff (" ^ flatten_name eff_name ^ ") (" ^ term_to_string result ^ ")" ^ args_str
exception MetaAnalysis of string
let mfail str =
raise (MetaAnalysis str)
/// A map linking variables to terms. For now we use a list to define it, because
/// there shouldn't be too many bindings.
type bind_map (a : Type) = list (bv & a)
let bind_map_push (#a:Type) (m:bind_map a) (b:bv) (x:a) = (b,x)::m
let rec bind_map_get (#a:Type) (m:bind_map a) (b:bv) : Tot (option a) =
match m with
| [] -> None
| (b', x)::m' ->
if compare_bv b b' = Order.Eq then Some x else bind_map_get m' b
let rec bind_map_get_from_name (#a:Type) (m:bind_map a) (name:string) :
Tot (option (bv & a)) =
match m with
| [] -> None
| (b', x)::m' ->
let b'v = inspect_bv b' in
if b'v.bv_ppname = name then Some (b', x) else bind_map_get_from_name m' name
noeq type genv =
{
env : env;
(* Whenever we evaluate a let binding, we keep track of the relation between
* the binder and its definition.
* The boolean indicates whether or not the variable is considered abstract. We
* often need to introduce variables which don't appear in the user context, for
* example when we need to deal with a postcondition for Stack or ST, which handles
* the previous and new memory states, and which may not be available in the user
* context, or where we don't always know which variable to use.
* In this case, whenever we output the term, we write its content as an
* asbtraction applied to those missing parameters. For instance, in the
* case of the assertion introduced for a post-condition:
* [> assert((fun h1 h2 -> post) h1 h2);
* Besides the informative goal, the user can replace those parameters (h1
* and h2 above) by the proper ones then normalize the assertion by using
* the appropriate command to get a valid assertion. *)
bmap : bind_map (bool & term);
(* Whenever we introduce a new variable, we check whether it shadows another
* variable because it has the same name, and put it in the below
* list. Of course, for the F* internals such shadowing is not an issue, because
* the index of every variable should be different, but this is very important
* when generating code for the user *)
svars : list bv;
}
let get_env (e:genv) : env = e.env
let get_bind_map (e:genv) : bind_map (bool & term) = e.bmap
let mk_genv env bmap svars : genv = Mkgenv env bmap svars
let mk_init_genv env : genv = mk_genv env [] []
val genv_to_string : genv -> Tot string
let genv_to_string ge =
let binder_to_string (b : binder) : Tot string =
abv_to_string (bv_of_binder b) ^ "\n"
in
let binders_str = List.Tot.map binder_to_string (binders_of_env ge.env) in
let bmap_elem_to_string (e : bv & (bool & term)) : Tot string =
let bv, (abs, t) = e in
"(" ^ abv_to_string bv ^" -> (" ^
string_of_bool abs ^ ", " ^ term_to_string t ^ "))\n"
in
let bmap_str = List.Tot.map bmap_elem_to_string ge.bmap in
let svars_str = List.Tot.map (fun bv -> abv_to_string bv ^ "\n") ge.svars in
let flatten = List.Tot.fold_left (fun x y -> x ^ y) "" in
"> env:\n" ^ flatten binders_str ^
"> bmap:\n" ^ flatten bmap_str ^
"> svars:\n" ^ flatten svars_str
let genv_get (ge:genv) (b:bv) : Tot (option (bool & term)) =
bind_map_get ge.bmap b
let genv_get_from_name (ge:genv) (name:string) : Tot (option (bv & (bool & term))) =
bind_map_get_from_name ge.bmap name
/// Push a binder to a ``genv``. Optionally takes a ``term`` which provides the
/// term the binder is bound to (in a `let _ = _ in` construct for example).
let genv_push_bv (ge:genv) (b:bv) (abs:bool) (t:option term) : Tac genv =
let br = mk_binder b in
let sv = genv_get_from_name ge (name_of_bv b) in
let svars' = if Some? sv then fst (Some?.v sv) :: ge.svars else ge.svars in
let e' = push_binder ge.env br in
let tm = if Some? t then Some?.v t else pack (Tv_Var b) in
let bmap' = bind_map_push ge.bmap b (abs, tm) in
mk_genv e' bmap' svars'
let genv_push_binder (ge:genv) (b:binder) (abs:bool) (t:option term) : Tac genv =
genv_push_bv ge (bv_of_binder b) abs t
/// Check if a binder is shadowed by another more recent binder
let bv_is_shadowed (ge : genv) (bv : bv) : Tot bool =
List.Tot.existsb (bv_eq bv) ge.svars
let binder_is_shadowed (ge : genv) (b : binder) : Tot bool =
bv_is_shadowed ge (bv_of_binder b)
let find_shadowed_bvs (ge : genv) (bl : list bv) : Tot (list (bv & bool)) =
List.Tot.map (fun b -> b, bv_is_shadowed ge b) bl
let find_shadowed_binders (ge : genv) (bl : list binder) : Tot (list (binder & bool)) =
List.Tot.map (fun b -> b, binder_is_shadowed ge b) bl
val bv_is_abstract : genv -> bv -> Tot bool
let bv_is_abstract ge bv =
match genv_get ge bv with
| None -> false
| Some (abs, _) -> abs
val binder_is_abstract : genv -> binder -> Tot bool
let binder_is_abstract ge b =
bv_is_abstract ge (bv_of_binder b)
val genv_abstract_bvs : genv -> Tot (list bv)
let genv_abstract_bvs ge =
let abs = List.Tot.filter (fun (_, (abs, _)) -> abs) ge.bmap in
List.Tot.map (fun (bv, _) -> bv) abs
/// Versions of ``fresh_bv`` and ``fresh_binder`` inspired by the standard library
/// We make sure the name is fresh because we need to be able to generate valid code
/// (it is thus not enough to have a fresh integer).
let rec _fresh_bv binder_names basename i ty : Tac bv =
let name = basename ^ string_of_int i in
(* In worst case the performance is quadratic in the number of binders.
* TODO: fix that, it actually probably happens for anonymous variables ('_') *)
if List.mem name binder_names then _fresh_bv binder_names basename (i+1) ty
else fresh_bv_named name ty
let fresh_bv (e : env) (basename : string) (ty : typ) : Tac bv =
let binders = binders_of_env e in
let binder_names = List.Tot.map name_of_binder binders in
_fresh_bv binder_names basename 0 ty
let fresh_binder (e : env) (basename : string) (ty : typ) : Tac binder =
let bv = fresh_bv e basename ty in
mk_binder bv
let genv_push_fresh_binder (ge : genv) (basename : string) (ty : typ) : Tac (genv & binder) =
let b = fresh_binder ge.env basename ty in
(* TODO: we can have a shortcircuit push (which performs less checks) *)
let ge' = genv_push_binder ge b true None in
ge', b
// TODO: actually we should use push_fresh_bv more
let push_fresh_binder (e : env) (basename : string) (ty : typ) : Tac (env & binder) =
let b = fresh_binder e basename ty in
let e' = push_binder e b in
e', b
let genv_push_fresh_bv (ge : genv) (basename : string) (ty : typ) : Tac (genv & bv) =
let ge', b = genv_push_fresh_binder ge basename ty in
ge', bv_of_binder b
/// Substitutions
/// Custom substitutions using the normalizer. This is the easiest and safest
/// way to perform a substitution: if you want to substitute [v] with [t] in [exp],
/// just normalize [(fun v -> exp) t]. Note that this may be computationally expensive.
val norm_apply_subst : env -> term -> list (bv & term) -> Tac term
val norm_apply_subst_in_comp : env -> comp -> list (bv & term) -> Tac comp
let norm_apply_subst e t subst =
let bl, vl = unzip subst in
let bl = List.Tot.map mk_binder bl in
let t1 = mk_abs t bl in
let t2 = mk_e_app t1 vl in
norm_term_env e [] t2
let norm_apply_subst_in_comp e c subst =
let subst = (fun x -> norm_apply_subst e x subst) in
let subst_in_aqualv a : Tac aqualv =
match a with
| Q_Implicit
| Q_Explicit -> a
| Q_Meta t -> Q_Meta (subst t)
| Q_Meta_attr t -> Q_Meta_attr (subst t)
in
match inspect_comp c with
| C_Total ret decr ->
let ret = subst ret in
let decr = opt_tapply subst decr in
pack_comp (C_Total ret decr)
| C_GTotal ret decr ->
let ret = subst ret in
let decr = opt_tapply subst decr in
pack_comp (C_GTotal ret decr)
| C_Lemma pre post patterns ->
let pre = subst pre in
let post = subst post in
let patterns = subst patterns in
pack_comp (C_Lemma pre post patterns)
| C_Eff us eff_name result eff_args ->
let result = subst result in
let eff_args = map (fun (x, a) -> (subst x, subst_in_aqualv a)) eff_args in
pack_comp (C_Eff us eff_name result eff_args)
/// As substitution with normalization is very expensive, we implemented another
/// technique which works by exploring terms. This is super fast, but as some
/// information not accessible to the meta F* tactics is dropped along the way,
/// it has a big impact on pretty printing. For example, terms like [A /\ B] get
/// printed as [Prims.l_and A B].
val deep_apply_subst : env -> term -> list (bv & term) -> Tac term
// Whenever we encounter a construction which introduces a binder, we need to apply
// the substitution in the binder type. Note that this gives a new binder, with
// which we need to replace the old one in what follows
val deep_apply_subst_in_bv : env -> bv -> list (bv & term) -> Tac (bv & list (bv & term))
val deep_apply_subst_in_binder : env -> binder -> list (bv & term) -> Tac (binder & list (bv & term))
val deep_apply_subst_in_comp : env -> comp -> list (bv & term) -> Tac comp
val deep_apply_subst_in_pattern : env -> pattern -> list (bv & term) -> Tac (pattern & list (bv & term))
let rec deep_apply_subst e t subst =
match inspect t with
| Tv_Var b ->
begin match bind_map_get subst b with
| None -> t
| Some t' -> t'
end
| Tv_BVar b ->
(* Note: Tv_BVar shouldn't happen *)
begin match bind_map_get subst b with
| None -> t
| Some t' -> t'
end
| Tv_FVar _ -> t
| Tv_App hd (a,qual) ->
let hd = deep_apply_subst e hd subst in
let a = deep_apply_subst e a subst in
pack (Tv_App hd (a, qual))
| Tv_Abs br body ->
let body = deep_apply_subst e body subst in
pack (Tv_Abs br body)
| Tv_Arrow br c ->
let br, subst = deep_apply_subst_in_binder e br subst in
let c = deep_apply_subst_in_comp e c subst in
pack (Tv_Arrow br c)
| Tv_Type () -> t
| Tv_Refine bv ref ->
let bv, subst = deep_apply_subst_in_bv e bv subst in
let ref = deep_apply_subst e ref subst in
pack (Tv_Refine bv ref)
| Tv_Const _ -> t
| Tv_Uvar _ _ -> t
| Tv_Let recf attrs bv def body ->
(* No need to substitute in the attributes - that we filter for safety *)
let bv, subst = deep_apply_subst_in_bv e bv subst in
let def = deep_apply_subst e def subst in
let body = deep_apply_subst e body subst in
pack (Tv_Let recf [] bv def body)
| Tv_Match scrutinee branches -> (* TODO: type of pattern variables *)
let scrutinee = deep_apply_subst e scrutinee subst in
(* For the branches: we don't need to explore the patterns *)
let deep_apply_subst_in_branch branch =
let pat, tm = branch in
let pat, subst = deep_apply_subst_in_pattern e pat subst in
let tm = deep_apply_subst e tm subst in
pat, tm
in
let branches = map deep_apply_subst_in_branch branches in
pack (Tv_Match scrutinee branches)
| Tv_AscribedT exp ty tac ->
let exp = deep_apply_subst e exp subst in
let ty = deep_apply_subst e ty subst in
(* no need to apply it on the tactic - that we filter for safety *)
pack (Tv_AscribedT exp ty None)
| Tv_AscribedC exp c tac ->
let exp = deep_apply_subst e exp subst in
let c = deep_apply_subst_in_comp e c subst in
(* no need to apply it on the tactic - that we filter for safety *)
pack (Tv_AscribedC exp c None)
| _ ->
(* Unknown *)
t
and deep_apply_subst_in_bv e bv subst =
let bvv = inspect_bv bv in
let ty = deep_apply_subst e bvv.bv_sort subst in
let bv' = Tactics.fresh_bv_named bvv.bv_ppname ty in
bv', (bv, pack (Tv_Var bv'))::subst
and deep_apply_subst_in_binder e br subst =
let bv, qual = inspect_binder br in
let bv, subst = deep_apply_subst_in_bv e bv subst in
pack_binder bv qual, subst
and deep_apply_subst_in_comp e c subst =
let subst = (fun x -> deep_apply_subst e x subst) in
let subst_in_aqualv a : Tac aqualv =
match a with
| Q_Implicit
| Q_Explicit -> a
| Q_Meta t -> Q_Meta (subst t)
| Q_Meta_attr t -> Q_Meta_attr (subst t)
in
match inspect_comp c with
| C_Total ret decr ->
let ret = subst ret in
let decr = opt_tapply subst decr in
pack_comp (C_Total ret decr)
| C_GTotal ret decr ->
let ret = subst ret in
let decr = opt_tapply subst decr in
pack_comp (C_GTotal ret decr)
| C_Lemma pre post patterns ->
let pre = subst pre in
let post = subst post in
let patterns = subst patterns in
pack_comp (C_Lemma pre post patterns)
| C_Eff us eff_name result eff_args ->
let result = subst result in
let eff_args = map (fun (x, a) -> (subst x, subst_in_aqualv a)) eff_args in
pack_comp (C_Eff us eff_name result eff_args)
and deep_apply_subst_in_pattern e pat subst =
match pat with
| Pat_Constant _ -> pat, subst
| Pat_Cons fv patterns ->
(* The types of the variables in the patterns should be independent of each
* other: we use fold_left only to incrementally update the substitution *)
let patterns, subst =
fold_right (fun (pat, b) (pats, subst) ->
let pat, subst = deep_apply_subst_in_pattern e pat subst in
((pat, b) :: pats, subst)) patterns ([], subst)
in
Pat_Cons fv patterns, subst
| Pat_Var bv ->
let bv, subst = deep_apply_subst_in_bv e bv subst in
Pat_Var bv, subst
| Pat_Wild bv ->
let bv, subst = deep_apply_subst_in_bv e bv subst in
Pat_Wild bv, subst
| Pat_Dot_Term bv t ->
let bv, subst = deep_apply_subst_in_bv e bv subst in
let t = deep_apply_subst e t subst in
Pat_Dot_Term bv t, subst
/// The substitution functions actually used in the rest of the meta F* functions.
/// For now, we use normalization because even though it is sometimes slow it
/// gives prettier terms, and readability is the priority. In order to mitigate
/// the performance issue, we try to minimize the number of calls to those functions,
/// by doing lazy instantiations for example (rather than incrementally apply
/// substitutions in a term, accumulate the substitutions and perform them all at once).
let apply_subst = norm_apply_subst
let apply_subst_in_comp = norm_apply_subst_in_comp
val opt_apply_subst : env -> option term -> list (bv & term) -> Tac (option term)
let opt_apply_subst e opt_t subst =
match opt_t with
| None -> None
| Some t -> Some (apply_subst e t subst)
/// Some constants
let prims_true_qn = "Prims.l_True"
let prims_true_term = `Prims.l_True
let pure_effect_qn = "Prims.PURE"
let pure_hoare_effect_qn = "Prims.Pure"
let stack_effect_qn = "FStar.HyperStack.ST.Stack"
let st_effect_qn = "FStar.HyperStack.ST.ST"
/// Return the qualifier of a comp as a string
val comp_qualifier (c : comp) : Tac string
#push-options "--ifuel 1"
let comp_qualifier (c : comp) : Tac string =
match inspect_comp c with
| C_Total _ _ -> "C_Total"
| C_GTotal _ _ -> "C_GTotal"
| C_Lemma _ _ _ -> "C_Lemma"
| C_Eff _ _ _ _ -> "C_Eff"
#pop-options
/// Effect information: we list the current supported effects
type effect_type =
| E_Total | E_GTotal | E_Lemma | E_PURE | E_Pure | E_Stack | E_ST | E_Unknown
val effect_type_to_string : effect_type -> string
#push-options "--ifuel 1"
let effect_type_to_string ety =
match ety with
| E_Total -> "E_Total"
| E_GTotal -> "E_GTotal"
| E_Lemma -> "E_Lemma"
| E_PURE -> "E_PURE"
| E_Pure -> "E_Pure"
| E_Stack -> "E_Stack"
| E_ST -> "E_ST"
| E_Unknown -> "E_Unknown"
#pop-options
val effect_name_to_type (ename : name) : Tot effect_type
let effect_name_to_type (ename : name) : Tot effect_type =
let ename = flatten_name ename in
if ename = pure_effect_qn then E_PURE
else if ename = pure_hoare_effect_qn then E_Pure
else if ename = stack_effect_qn then E_Stack
else if ename = st_effect_qn then E_ST
else E_Unknown
val effect_type_is_pure : effect_type -> Tot bool
let effect_type_is_pure etype =
match etype with
| E_Total | E_GTotal | E_Lemma | E_PURE | E_Pure -> true
| E_Stack | E_ST | E_Unknown -> false
/// Type information
noeq type type_info = {
ty : typ; (* the type without refinement *)
refin : option term;
}
let mk_type_info = Mktype_info
val type_info_to_string : type_info -> Tot string
let type_info_to_string info =
"Mktype_info (" ^
term_to_string info.ty ^ ") (" ^
option_to_string term_to_string info.refin ^ ")"
let unit_type_info = mk_type_info (`unit) None
val safe_tc (e:env) (t:term) : Tac (option term)
let safe_tc e t =
try Some (tc e t) with | _ -> None
val safe_tcc (e:env) (t:term) : Tac (option comp)
let safe_tcc e t =
try Some (tcc e t) with | _ -> None
let get_type_info_from_type (ty:typ) : Tac type_info =
match inspect ty with
| Tv_Refine bv refin ->
let bview : bv_view = inspect_bv bv in
let raw_type : typ = bview.bv_sort in
let raw_type = prettify_term false raw_type in
let b : binder = mk_binder bv in
let refin = prettify_term false refin in
let refin = pack (Tv_Abs b refin) in
mk_type_info raw_type (Some refin)
| _ ->
let ty = prettify_term false ty in
mk_type_info ty None
#push-options "--ifuel 1"
let get_type_info (e:env) (t:term) : Tac (option type_info) =
match safe_tc e t with
| None -> None
| Some ty -> Some (get_type_info_from_type ty)
#pop-options
val get_total_or_gtotal_ret_type : comp -> Tot (option typ)
let get_total_or_gtotal_ret_type c =
match inspect_comp c with
| C_Total ret_ty _ | C_GTotal ret_ty _ -> Some ret_ty
| _ -> None
val get_comp_ret_type : comp -> Tot typ
let get_comp_ret_type c =
match inspect_comp c with
| C_Total ret_ty _ | C_GTotal ret_ty _
| C_Eff _ _ ret_ty _ -> ret_ty
| C_Lemma _ _ _ -> (`unit)
val is_total_or_gtotal : comp -> Tot bool
let is_total_or_gtotal c =
Some? (get_total_or_gtotal_ret_type c)
val is_unit_type : typ -> Tac bool
let is_unit_type ty =
match inspect ty with
| Tv_FVar fv -> fv_eq_name fv Reflection.Const.unit_lid
| _ -> false
/// This type is used to store typing information.
/// We use it mostly to track what the target type/computation is for a term,
/// while exploring this term. It is especially useful to generate post-conditions,
/// for example. We store the list of abstractions encountered so far at the
/// same time.
/// Note that in order to keep track of the type correctly, whenever we encounter
/// an abstraction in the term, we need to check that the term' type is an arrow,
/// in which case we need to do a substitution (the arrow takes as first parameter
/// which is not the same as the abstraction's binder). As the substitution is costly
/// (we do it by using the normalizer, but the "final" return term is the whole
/// function's body type, which is often super big) we do it lazily: we count how
/// many parameters we have encountered and not substituted, and "flush" when we
/// really need to inspect the typ_or_comp.
// TODO: actually we only need to carry a comp (if typ: consider it total)
(* TODO: remove the instantiation: instantiate incrementally *)
noeq type typ_or_comp =
| TC_Typ : v:typ -> pl:list binder -> num_unflushed:nat -> typ_or_comp
| TC_Comp : v:comp -> pl:list binder -> num_unflushed:nat -> typ_or_comp
let typ_or_comp_to_string (tyc : typ_or_comp) : Tot string =
match tyc with
| TC_Typ v pl num_unflushed ->
"TC_Typ (" ^ term_to_string v ^ ") " ^ list_to_string name_of_binder pl ^
" " ^ string_of_int num_unflushed
| TC_Comp c pl num_unflushed ->
"TC_Comp (" ^ acomp_to_string c ^ ") " ^ list_to_string name_of_binder pl ^
" " ^ string_of_int num_unflushed
/// Return the list of parameters stored in a ``typ_or_comp``
let params_of_typ_or_comp (c : typ_or_comp) : list binder =
match c with
| TC_Typ _ pl _ | TC_Comp _ pl _ -> pl
let num_unflushed_of_typ_or_comp (c : typ_or_comp) : nat =
match c with
| TC_Typ _ _ n | TC_Comp _ _ n -> n
/// Compute a ``typ_or_comp`` from the type of a term
// TODO: try to get a more precise comp
val safe_typ_or_comp : bool -> env -> term ->
Tac (opt:option typ_or_comp{Some? opt ==> TC_Comp? (Some?.v opt)})
let safe_typ_or_comp dbg e t =
match safe_tcc e t with
| None ->
print_dbg dbg ("[> safe_typ_or_comp:" ^
"\n-term: " ^ term_to_string t ^
"\n-comp: None");
None
| Some c ->
print_dbg dbg ("[> safe_typ_or_comp:" ^
"\n-term: " ^ term_to_string t ^
"\n-comp: " ^ acomp_to_string c);
Some (TC_Comp c [] 0)
val subst_bv_in_comp : env -> bv -> term -> comp -> Tac comp
let subst_bv_in_comp e b t c =
apply_subst_in_comp e c [(b, t)]
val subst_binder_in_comp : env -> binder -> term -> comp -> Tac comp
let subst_binder_in_comp e b t c =
subst_bv_in_comp e (bv_of_binder b) t c
/// Utility for computations: unfold a type until it is of the form Tv_Arrow _ _,
/// fail otherwise
val unfold_until_arrow : env -> typ -> Tac typ
let rec unfold_until_arrow e ty0 =
if Tv_Arrow? (inspect ty0) then ty0
else
begin
(* Start by normalizing the term - note that this operation is expensive *)
let ty = norm_term_env e [] ty0 in
(* Helper to unfold top-level identifiers *)
let unfold_fv (fv : fv) : Tac term =
let ty = pack (Tv_FVar fv) in
let fvn = flatten_name (inspect_fv fv) in
(* unfold the top level binding, check that it has changed, and recurse *)
let ty' = norm_term_env e [delta_only [fvn]] ty in
(* I'm not confident about using eq_term here *)
begin match inspect ty' with
| Tv_FVar fv' ->
if flatten_name (inspect_fv fv') = fvn
then mfail ("unfold_until_arrow: could not unfold: " ^ term_to_string ty0) else ty'
| _ -> ty'
end
in
(* Inspect *)
match inspect ty with
| Tv_Arrow _ _ -> ty
| Tv_FVar fv ->
(* Try to unfold the top-level identifier and recurse *)
let ty' = unfold_fv fv in
unfold_until_arrow e ty'
| Tv_App _ _ ->
(* Strip all the parameters, try to unfold the head and recurse *)
let hd, args = collect_app ty in
begin match inspect hd with
| Tv_FVar fv ->
let hd' = unfold_fv fv in
let ty' = mk_app hd' args in
unfold_until_arrow e ty'
| _ -> mfail ("unfold_until_arrow: could not unfold: " ^ term_to_string ty0)
end
| Tv_Refine bv ref ->
(* Continue with the type of bv *)
let ty' = type_of_bv bv in
unfold_until_arrow e ty'
| Tv_AscribedT body _ _
| Tv_AscribedC body _ _ ->
unfold_until_arrow e body
| _ ->
(* Other situations: don't know what to do *)
mfail ("unfold_until_arrow: could not unfold: " ^ term_to_string ty0)
end
/// Instantiate a comp
val inst_comp_once : env -> comp -> term -> Tac comp
let inst_comp_once e c t =
let ty = get_comp_ret_type c in
let ty' = unfold_until_arrow e ty in
begin match inspect ty' with
| Tv_Arrow b1 c1 ->
subst_binder_in_comp e b1 t c1
| _ -> (* Inconsistent state *)
mfail "inst_comp_once: inconsistent state"
end
val inst_comp : env -> comp -> list term -> Tac comp
let rec inst_comp e c tl =
match tl with
| [] -> c
| t :: tl' ->
let c' = try inst_comp_once e c t
with | MetaAnalysis msg -> mfail ("inst_comp: error: " ^ msg)
| err -> raise err
in
inst_comp e c' tl'
/// Update the current ``typ_or_comp`` before going into the body of an abstraction.
/// Explanations:
/// In the case we dive into a term of the form:
/// [> (fun x -> body) : y:ty -> body_type
/// we need to substitute y with x in body_type to get the proper type for body.
/// Note that we checked, and in practice the binders are indeed different.
// TODO: actually, we updated it to do a lazy instantiation
val abs_update_typ_or_comp : binder -> typ_or_comp -> env -> Tac typ_or_comp
let _abs_update_typ (b:binder) (ty:typ) (pl:list binder) (e:env) :
Tac typ_or_comp =
(* Try to reveal an arrow *)
try
let ty' = unfold_until_arrow e ty in
begin match inspect ty' with
| Tv_Arrow b1 c1 ->
let c1' = subst_binder_in_comp e b1 (pack (Tv_Var (bv_of_binder b))) c1 in
TC_Comp c1' (b :: pl) 0
| _ -> (* Inconsistent state *)
mfail "_abs_update_typ: inconsistent state"
end
with
| MetaAnalysis msg ->
mfail ("_abs_update_typ: could not find an arrow in: " ^ term_to_string ty ^ ":\n" ^ msg)
| err -> raise err
let abs_update_typ_or_comp (b:binder) (c : typ_or_comp) (e:env) : Tac typ_or_comp =
match c with
(*| TC_Typ v pl n -> _abs_update_typ b v pl e
| TC_Comp v pl n ->
(* Note that the computation is not necessarily pure, in which case we might
* want to do something with the effect arguments (pre, post...) - for
* now we just ignore them *)
let ty = get_comp_ret_type v in
_abs_update_typ b ty pl e *)
| TC_Typ v pl n -> TC_Typ v (b::pl) (n+1)
| TC_Comp v pl n -> TC_Comp v (b::pl) (n+1)
val abs_update_opt_typ_or_comp : binder -> option typ_or_comp -> env ->
Tac (option typ_or_comp)
let abs_update_opt_typ_or_comp b opt_c e =
match opt_c with
| None -> None
| Some c ->
try
let c = abs_update_typ_or_comp b c e in
Some c
with | MetaAnalysis msg -> None
| err -> raise err
/// Flush the instantiation stored in a ``typ_or_comp``
val flush_typ_or_comp : bool -> env -> typ_or_comp ->
Tac (tyc:typ_or_comp{num_unflushed_of_typ_or_comp tyc = 0})
/// Strip all the arrows we can without doing any instantiation. When we can't
/// strip arrows anymore, do the instantiation at once.
/// We keep track of two list of binders:
/// - the remaining binders
/// - the instantiation corresponding to the arrows we have stripped so far, and
/// which will be applied all at once
let rec _flush_typ_or_comp_comp (dbg : bool) (e:env) (rem : list binder) (inst : list (bv & term))
(c:comp) : Tac comp =
let flush c inst =
let inst = List.rev inst in
apply_subst_in_comp e c inst
in
match rem with
| [] ->
(* No more binders: flush *)
flush c inst
| b :: rem' ->
(* Check if the return type is an arrow, if not flush and normalize *)
let ty = get_comp_ret_type c in
let ty, inst' =
if Tv_Arrow? (inspect ty) then ty, inst
else get_comp_ret_type (flush c inst), []
in
match inspect ty with
| Tv_Arrow b' c' ->
_flush_typ_or_comp_comp dbg e rem' ((bv_of_binder b', pack (Tv_Var (bv_of_binder b)))::inst) c'
| _ ->
mfail ("_flush_typ_or_comp: inconsistent state" ^
"\n-comp: " ^ acomp_to_string c ^
"\n-remaning binders: " ^ list_to_string name_of_binder rem)
let flush_typ_or_comp dbg e tyc =
let flush_comp pl n c : Tac (tyc:typ_or_comp{num_unflushed_of_typ_or_comp tyc = 0}) =
let pl', _ = List.Tot.splitAt n pl in
let pl' = List.rev pl' in
let c = _flush_typ_or_comp_comp dbg e pl' [] c in
TC_Comp c pl 0
in
try begin match tyc with
| TC_Typ ty pl n ->
let c = pack_comp (C_Total ty None) in
flush_comp pl n c
| TC_Comp c pl n -> flush_comp pl n c
end
with | MetaAnalysis msg ->
mfail ("flush_typ_or_comp failed on: " ^ typ_or_comp_to_string tyc ^ ":\n" ^ msg)
| err -> raise err
/// Compute the target ``typ_or_comp`` for an argument by the type of the head:
/// in `hd a`, if `hd` has type `t -> ...`, use `t`
val safe_arg_typ_or_comp : bool -> env -> term ->
Tac (opt:option typ_or_comp{Some? opt ==> TC_Typ? (Some?.v opt)})
let safe_arg_typ_or_comp dbg e hd =
print_dbg dbg ("safe_arg_typ_or_comp: " ^ term_to_string hd);
match safe_tc e hd with
| None -> None
| Some ty ->
print_dbg dbg ("hd type: " ^ term_to_string ty);
let ty =
if Tv_Arrow? (inspect ty) then
begin
print_dbg dbg "no need to unfold the type";
ty
end
else
begin
print_dbg dbg "need to unfold the type";
let ty = unfold_until_arrow e ty in
print_dbg dbg ("result of unfolding : "^ term_to_string ty);
ty
end
in
match inspect ty with
| Tv_Arrow b c -> Some (TC_Typ (type_of_binder b) [] 0)
| _ -> None
/// Exploring a term
let convert_ctrl_flag (flag : ctrl_flag) =
match flag with
| Continue -> Continue
| Skip -> Continue
| Abort -> Abort
/// TODO: for now I need to use universe 0 for type a because otherwise it doesn't
/// type check
/// ctrl_flag:
/// - Continue: continue exploring the term
/// - Skip: don't explore the sub-terms of this term
/// - Abort: stop exploration
/// TODO: we might want a more precise control (like: don't explore the type of the
/// ascription but explore its body)
/// Note that ``explore_term`` doesn't use the environment parameter besides pushing
/// binders and passing it to ``f``, which means that you can give it arbitrary
/// environments, ``explore_term`` itself won't fail (but the passed function might).
let explorer (a : Type) =
a -> genv -> list (genv & term_view) -> option typ_or_comp -> term_view ->
Tac (a & ctrl_flag)
// TODO: use more
let bind_expl (#a : Type) (x : a) (f1 f2 : a -> Tac (a & ctrl_flag)) : Tac (a & ctrl_flag) =