-
Notifications
You must be signed in to change notification settings - Fork 21
/
Copy pathExtract.ml
3155 lines (2983 loc) · 116 KB
/
Extract.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
(** The generic extraction *)
(* Turn the whole module into a functor: it is very annoying to carry the
the formatter everywhere...
*)
open Pure
open PureUtils
open TranslateCore
open Config
open Errors
include ExtractTypes
let fun_or_op_id_to_string (ctx : extraction_ctx) =
PrintPure.fun_or_op_id_to_string (extraction_ctx_to_fmt_env ctx)
let generic_args_to_string (ctx : extraction_ctx) =
PrintPure.generic_args_to_string (extraction_ctx_to_fmt_env ctx)
let texpression_to_string (ctx : extraction_ctx) =
PrintPure.texpression_to_string (extraction_ctx_to_fmt_env ctx) false "" " "
(** Compute the names for all the pure functions generated from a rust function.
*)
let extract_fun_decl_register_names (ctx : extraction_ctx)
(has_decreases_clause : fun_decl -> bool) (def : pure_fun_translation) :
extraction_ctx =
(* Ignore the trait methods **declarations** (rem.: we do not ignore the trait
method implementations): we do not need to refer to them directly. We will
only use their type for the fields of the records we generate for the trait
declarations *)
match def.f.kind with
| TraitDeclItem (_, _, false) -> ctx
| _ -> (
(* Check if the function is builtin *)
let builtin =
let open ExtractBuiltin in
let funs_map = builtin_funs_map () in
match_name_find_opt ctx.trans_ctx def.f.item_meta.name funs_map
in
(* Use the builtin names if necessary *)
match builtin with
| Some (filter_info, fun_info) ->
(* Builtin function: register the filtering information, if there is *)
let ctx =
match filter_info with
| Some keep ->
{
ctx with
funs_filter_type_args_map =
FunDeclId.Map.add def.f.def_id keep
ctx.funs_filter_type_args_map;
}
| _ -> ctx
in
let f = def.f in
let open ExtractBuiltin in
let fun_id = (Pure.FunId (FRegular f.def_id), f.loop_id) in
ctx_add f.item_meta.span (FunId (FromLlbc fun_id))
fun_info.extract_name ctx
| None ->
(* Not builtin *)
(* If this is a trait method implementation, we prefix the name with the
name of the trait implementation. We need to do so because there
can be clashes otherwise. *)
(* Register the decrease clauses, if necessary *)
let register_decreases ctx def =
if has_decreases_clause def then
(* Add the termination measure *)
let ctx = ctx_add_termination_measure def ctx in
(* Add the decreases proof for Lean only *)
match backend () with
| Coq | FStar -> ctx
| HOL4 -> craise __FILE__ __LINE__ def.item_meta.span "Unexpected"
| Lean -> ctx_add_decreases_proof def ctx
else ctx
in
(* We have to register the function itself, and the loops it
may contain (which are extracted as functions) *)
let funs = def.f :: def.loops in
(* Register the decrease clauses *)
let ctx = List.fold_left register_decreases ctx funs in
(* Register the name of the function and the loops *)
let register_fun ctx f = ctx_add_fun_decl f ctx in
let register_funs ctx fl = List.fold_left register_fun ctx fl in
register_funs ctx funs)
(** Simply add the global name to the context. *)
let extract_global_decl_register_names (ctx : extraction_ctx)
(def : A.global_decl) : extraction_ctx =
ctx_add_global_decl_and_body def ctx
(** The following function factorizes the extraction of ADT values.
Note that patterns can introduce new variables: we thus return an extraction
context updated with new bindings.
[is_single_pat]: are we extracting a single pattern (a pattern for a let-binding
or a lambda)?
TODO: we don't need something very generic anymore (some definitions used
to be polymorphic).
*)
let extract_adt_g_value (span : Meta.span)
(extract_value : extraction_ctx -> bool -> 'v -> extraction_ctx)
(fmt : F.formatter) (ctx : extraction_ctx) (is_single_pat : bool)
(inside : bool) (variant_id : VariantId.id option) (field_values : 'v list)
(ty : ty) : extraction_ctx =
let extract_as_tuple () =
(* This is very annoying: in Coq, we can't write [()] for the value of
type [unit], we have to write [tt]. *)
if backend () = Coq && field_values = [] then (
F.pp_print_string fmt "tt";
ctx)
else
(* If there is exactly one value, we don't print the parentheses.
Also, for Coq, we need the special syntax ['(...)] if we destruct
a tuple pattern in a let-binding and the tuple has > 2 values.
*)
let lb, rb =
if List.length field_values = 1 then ("", "")
else if
backend () = Coq && is_single_pat && List.length field_values > 2
then ("'(", ")")
else ("(", ")")
in
F.pp_print_string fmt lb;
let ctx =
Collections.List.fold_left_link
(fun () ->
F.pp_print_string fmt ",";
F.pp_print_space fmt ())
(fun ctx v -> extract_value ctx false v)
ctx field_values
in
F.pp_print_string fmt rb;
ctx
in
match ty with
| TAdt (TTuple, generics) ->
(* Tuple *)
(* For now, we only support fully applied tuple constructors *)
cassert __FILE__ __LINE__
(List.length generics.types = List.length field_values)
span "Only fully applied tuple constructors are currently supported";
cassert __FILE__ __LINE__
(generics.const_generics = [] && generics.trait_refs = [])
span "Only fully applied tuple constructors are currently supported";
extract_as_tuple ()
| TAdt (adt_id, _) ->
(* "Regular" ADT *)
(* We may still extract the ADT as a tuple, if none of the fields are
named *)
if
PureUtils.type_decl_from_type_id_is_tuple_struct
ctx.trans_ctx.type_ctx.type_infos adt_id
then (* Extract as a tuple *)
extract_as_tuple ()
else if
(* If we are generating a pattern for a let-binding and we target Lean,
the syntax is: `let ⟨ x0, ..., xn ⟩ := ...`.
Otherwise, it is: `let Cons x0 ... xn = ...`
*)
is_single_pat && backend () = Lean
then (
F.pp_print_string fmt "⟨";
F.pp_print_space fmt ();
let ctx =
Collections.List.fold_left_link
(fun _ ->
F.pp_print_string fmt ",";
F.pp_print_space fmt ())
(fun ctx v -> extract_value ctx true v)
ctx field_values
in
F.pp_print_space fmt ();
F.pp_print_string fmt "⟩";
ctx)
else
(* We print something of the form: [Cons field0 ... fieldn].
* We could update the code to print something of the form:
* [{ field0=...; ...; fieldn=...; }] in case of structures.
*)
let cons =
match variant_id with
| Some vid -> ctx_get_variant span adt_id vid ctx
| None -> ctx_get_struct span adt_id ctx
in
let use_parentheses = inside && field_values <> [] in
if use_parentheses then F.pp_print_string fmt "(";
F.pp_print_string fmt cons;
let ctx =
Collections.List.fold_left
(fun ctx v ->
F.pp_print_space fmt ();
extract_value ctx true v)
ctx field_values
in
if use_parentheses then F.pp_print_string fmt ")";
ctx
| _ -> craise __FILE__ __LINE__ span "Inconsistent typed value"
(* Extract globals in the same way as variables *)
let extract_global (span : Meta.span) (ctx : extraction_ctx) (fmt : F.formatter)
(inside : bool) (id : A.GlobalDeclId.id) (generics : generic_args) : unit =
let use_brackets = inside && generics <> empty_generic_args in
F.pp_open_hvbox fmt ctx.indent_incr;
if use_brackets then F.pp_print_string fmt "(";
(* Extract the global name *)
F.pp_print_string fmt (ctx_get_global span id ctx);
(* Extract the generics.
We have to lookup the information about the implicit/explicit parameters.
Note that the global declaration (from which we retrieve this information)
may be missing if there were some errors.
*)
let explicit =
Option.map
(fun (d : global_decl) -> d.explicit_info)
(GlobalDeclId.Map.find_opt id ctx.trans_globals)
in
extract_generic_args span ctx fmt TypeDeclId.Set.empty ~explicit generics;
if use_brackets then F.pp_print_string fmt ")";
F.pp_close_box fmt ()
(* Filter the generics of a function if it is builtin *)
let fun_builtin_filter_types (id : FunDeclId.id) (types : 'a list)
(explicit : explicit_info option) (ctx : extraction_ctx) :
('a list * explicit_info option, 'a list * string) Result.result =
match FunDeclId.Map.find_opt id ctx.funs_filter_type_args_map with
| None -> Result.Ok (types, explicit)
| Some filter ->
if List.length filter <> List.length types then (
let decl = FunDeclId.Map.find id ctx.trans_funs in
let err =
"Ill-formed builtin information for function "
^ name_to_string ctx decl.f.item_meta.name
^ ": "
^ string_of_int (List.length filter)
^ " filtering arguments provided for "
^ string_of_int (List.length types)
^ " type arguments"
in
save_error __FILE__ __LINE__ None err;
Result.Error (types, err))
else
let filter_f =
List.filter_map (fun (b, ty) -> if b then Some ty else None)
in
let types = List.combine filter types in
let types = filter_f types in
let filter_f =
List.filter_map (fun (b, x) -> if b then Some x else None)
in
let explicit =
Option.map
(fun e ->
{
e with
explicit_types = filter_f (List.combine filter e.explicit_types);
})
explicit
in
Result.Ok (types, explicit)
(** [inside]: see {!extract_ty}.
[with_type]: do we also generate a type annotation? This is necessary for
backends like Coq when we write lambdas (Coq is not powerful enough to
infer the type).
As a pattern can introduce new variables, we return an extraction context
updated with new bindings.
*)
let rec extract_typed_pattern (span : Meta.span) (ctx : extraction_ctx)
(fmt : F.formatter) (is_let : bool) (inside : bool) ?(with_type = false)
(v : typed_pattern) : extraction_ctx =
if with_type then F.pp_print_string fmt "(";
let is_pattern = true in
let inside = inside && not with_type in
let ctx =
match v.value with
| PatConstant cv ->
extract_literal span fmt is_pattern inside cv;
ctx
| PatVar (v, _) ->
let vname = ctx_compute_var_basename span ctx v.basename v.ty in
let ctx, vname = ctx_add_var span vname v.id ctx in
F.pp_print_string fmt vname;
ctx
| PatDummy ->
F.pp_print_string fmt "_";
ctx
| PatAdt av ->
let extract_value ctx inside v =
extract_typed_pattern span ctx fmt is_let inside v
in
extract_adt_g_value span extract_value fmt ctx is_let inside
av.variant_id av.field_values v.ty
in
if with_type then (
F.pp_print_space fmt ();
F.pp_print_string fmt ":";
F.pp_print_space fmt ();
extract_ty span ctx fmt TypeDeclId.Set.empty false v.ty;
F.pp_print_string fmt ")");
ctx
(** Return true if we need to wrap a succession of let-bindings in a [do ...]
block (because some of them are monadic) *)
let lets_require_wrap_in_do (span : Meta.span)
(lets : (bool * typed_pattern * texpression) list) : bool =
match backend () with
| Lean ->
(* For Lean, we wrap in a block iff at least one of the let-bindings is monadic *)
List.exists (fun (m, _, _) -> m) lets
| HOL4 ->
(* HOL4 is similar to HOL4, but we add a sanity check *)
let wrap_in_do = List.exists (fun (m, _, _) -> m) lets in
if wrap_in_do then
sanity_check __FILE__ __LINE__
(List.for_all (fun (m, _, _) -> m) lets)
span;
wrap_in_do
| FStar | Coq -> false
(** [inside]: controls the introduction of parentheses. See [extract_ty]
TODO: replace the formatting boolean [inside] with something more general?
Also, it seems we don't really use it...
Cases to consider:
- right-expression in a let: [let x = re in _] (never parentheses?)
- next expression in a let: [let x = _ in next_e] (never parentheses?)
- application argument: [f (exp)]
- match/if scrutinee: [if exp then _ else _]/[match exp | _ -> _]
*)
let extract_texpression_errors (fmt : F.formatter) =
match backend () with
| FStar | Coq -> F.pp_print_string fmt "admit"
| Lean -> F.pp_print_string fmt "sorry"
| HOL4 -> F.pp_print_string fmt "(* ERROR: could not generate the code *)"
let rec extract_texpression (span : Meta.span) (ctx : extraction_ctx)
(fmt : F.formatter) (inside : bool) (e : texpression) : unit =
let is_pattern = false in
match e.e with
| Var var_id ->
let var_name = ctx_get_var span var_id ctx in
F.pp_print_string fmt var_name
| CVar var_id ->
let var_name = ctx_get_const_generic_var span var_id ctx in
F.pp_print_string fmt var_name
| Const cv -> extract_literal span fmt is_pattern inside cv
| App _ ->
let app, args = destruct_apps e in
extract_App span ctx fmt inside app args
| Lambda _ ->
let xl, e = destruct_lambdas e in
extract_Lambda (span : Meta.span) ctx fmt inside xl e
| Qualif _ ->
(* We use the app case *)
extract_App span ctx fmt inside e []
| Let (_, _, _, _) -> extract_lets span ctx fmt inside e
| Switch (scrut, body) -> extract_Switch span ctx fmt inside scrut body
| Meta (_, e) -> extract_texpression span ctx fmt inside e
| StructUpdate supd -> extract_StructUpdate span ctx fmt inside e.ty supd
| Loop _ ->
(* The loop nodes should have been eliminated in {!PureMicroPasses} *)
craise __FILE__ __LINE__ span "Unreachable"
| EError (_, _) -> extract_texpression_errors fmt
(* Extract an application *or* a top-level qualif (function extraction has
* to handle top-level qualifiers, so it seemed more natural to merge the
* two cases) *)
and extract_App (span : Meta.span) (ctx : extraction_ctx) (fmt : F.formatter)
(inside : bool) (app : texpression) (args : texpression list) : unit =
(* We don't do the same thing if the app is a top-level identifier (function,
* ADT constructor...) or a "regular" expression *)
match app.e with
| Qualif qualif -> (
(* Top-level qualifier *)
match qualif.id with
| FunOrOp fun_id ->
extract_function_call span ctx fmt inside fun_id qualif.generics args
| Global global_id ->
assert (args = []);
extract_global span ctx fmt inside global_id qualif.generics
| AdtCons adt_cons_id ->
extract_adt_cons span ctx fmt inside adt_cons_id qualif.generics args
| Proj proj ->
extract_field_projector span ctx fmt inside app proj qualif.generics
args
| TraitConst (trait_ref, const_name) ->
extract_trait_ref span ctx fmt TypeDeclId.Set.empty true trait_ref;
let name =
ctx_get_trait_const span trait_ref.trait_decl_ref.trait_decl_id
const_name ctx
in
let add_brackets (s : string) =
if backend () = Coq then "(" ^ s ^ ")" else s
in
F.pp_print_string fmt ("." ^ add_brackets name))
| _ ->
(* "Regular" expression *)
(* Open parentheses *)
if inside then F.pp_print_string fmt "(";
(* Open a box for the application *)
F.pp_open_hovbox fmt ctx.indent_incr;
(* Print the app expression *)
let app_inside = (inside && args = []) || args <> [] in
extract_texpression span ctx fmt app_inside app;
(* Print the arguments *)
List.iter
(fun ve ->
F.pp_print_space fmt ();
extract_texpression span ctx fmt true ve)
args;
(* Close the box for the application *)
F.pp_close_box fmt ();
(* Close parentheses *)
if inside then F.pp_print_string fmt ")"
(** Subcase of the app case: function call *)
and extract_function_call (span : Meta.span) (ctx : extraction_ctx)
(fmt : F.formatter) (inside : bool) (fid : fun_or_op_id)
(generics : generic_args) (args : texpression list) : unit =
log#ldebug
(lazy
("extract_function_call: "
^ fun_or_op_id_to_string ctx fid
^ "\n- generics: "
^ generic_args_to_string ctx generics
^ "\n- args: "
^ String.concat ", " (List.map (texpression_to_string ctx) args)));
match (fid, args) with
| Unop unop, [ arg ] ->
(* A unop can have *at most* one argument (the result can't be a function!).
* Note that the way we generate the translation, we shouldn't get the
* case where we have no argument (all functions are fully instantiated,
* and no AST transformation introduces partial calls). *)
extract_unop span (extract_texpression span ctx fmt) fmt inside unop arg
| Binop (binop, int_ty), [ arg0; arg1 ] ->
(* Number of arguments: similar to unop *)
extract_binop span
(extract_texpression span ctx fmt)
fmt inside binop int_ty arg0 arg1
| Fun fun_id, _ ->
if inside then F.pp_print_string fmt "(";
(* Open a box for the function call *)
F.pp_open_hovbox fmt ctx.indent_incr;
(* Print the function name.
For the function name: the id is not the same depending on whether
we call a trait method and a "regular" function (remark: trait
method *implementations* are considered as regular functions here;
only calls to method of traits which are parameterized in a where
clause have a special treatment.
Remark: the reason why trait method declarations have a special
treatment is that, as traits are extracted to records, we may
allow collisions between trait item names and some other names,
while we do not allow collisions between function names.
# Impl trait refs:
==================
When the trait ref refers to an impl, in
[InterpreterStatement.eval_transparent_function_call_symbolic] we
replace the call to the trait impl method to a call to the function
which implements the trait method (that is, we "forget" that we
called a trait method, and treat it as a regular function call).
# Provided trait methods:
=========================
Calls to provided trait methods also have a special treatment.
For now, we do not allow overriding provided trait methods (methods
for which a default implementation is provided in the trait declaration).
Whenever we translate a provided trait method, we translate it once as
a function which takes a trait ref as input. We have to handle this
case below.
With an example, if in Rust we write:
{[
fn Foo {
fn f(&self) -> u32; // Required
fn ret_true(&self) -> bool { true } // Provided
}
]}
We generate:
{[
structure Foo (Self : Type) = {
f : Self -> result u32
}
let ret_true (Self : Type) (self_clause : Foo Self) (self : Self) : result bool =
true
]}
*)
(match fun_id with
| FromLlbc (TraitMethod (trait_ref, method_name, _fun_decl_id), lp_id) ->
(* We have to check whether the trait method is required or provided *)
let trait_decl_id = trait_ref.trait_decl_ref.trait_decl_id in
let trait_decl =
TraitDeclId.Map.find trait_decl_id ctx.trans_trait_decls
in
let method_id =
PureUtils.trait_decl_get_method trait_decl method_name
in
if not method_id.is_provided then (
(* Required method *)
sanity_check __FILE__ __LINE__ (lp_id = None)
trait_decl.item_meta.span;
extract_trait_ref trait_decl.item_meta.span ctx fmt
TypeDeclId.Set.empty true trait_ref;
let fun_name =
ctx_get_trait_method span trait_ref.trait_decl_ref.trait_decl_id
method_name ctx
in
let add_brackets (s : string) =
if backend () = Coq then "(" ^ s ^ ")" else s
in
F.pp_print_string fmt ("." ^ add_brackets fun_name))
else
(* Provided method: we see it as a regular function call, and use
the function name *)
let fun_id = FromLlbc (FunId (FRegular method_id.id), lp_id) in
let fun_name =
ctx_get_function trait_decl.item_meta.span fun_id ctx
in
F.pp_print_string fmt fun_name;
(* Note that we do not need to print the generics for the trait
declaration: they are always implicit as they can be deduced
from the trait self clause.
Print the trait ref (to instantate the self clause) *)
F.pp_print_space fmt ();
extract_trait_ref trait_decl.item_meta.span ctx fmt
TypeDeclId.Set.empty true trait_ref
| _ ->
let fun_name = ctx_get_function span fun_id ctx in
F.pp_print_string fmt fun_name);
(* Sanity check: HOL4 doesn't support const generics *)
sanity_check __FILE__ __LINE__
(generics.const_generics = [] || backend () <> HOL4)
span;
(* Compute the information about the explicit/implicit input type parameters *)
let explicit =
let lookup is_trait_method fun_decl_id lp_id =
(* Lookup the function to retrieve the signature information *)
let trans_fun = A.FunDeclId.Map.find fun_decl_id ctx.trans_funs in
let trans_fun =
match lp_id with
| None -> trans_fun.f
| Some lp_id -> Pure.LoopId.nth trans_fun.loops lp_id
in
let explicit = trans_fun.signature.explicit_info in
(* If it is a trait method, we need to remove the prefix
which account for the generics of the impl. *)
let explicit =
if is_trait_method then
(* We simply adjust the length of the explicit information to
the number of generic arguments *)
let open Collections.List in
let { explicit_types; explicit_const_generics } = explicit in
{
explicit_types =
drop
(length explicit_types - length generics.types)
explicit_types;
explicit_const_generics =
drop
(length explicit_const_generics
- length generics.const_generics)
explicit_const_generics;
}
else explicit
in
(* *)
Some explicit
in
match fun_id with
| FromLlbc (FunId (FRegular fun_decl_id), lp_id) ->
lookup false fun_decl_id lp_id
| FromLlbc (TraitMethod (_trait_ref, _method_name, fun_decl_id), lp_id)
-> lookup true fun_decl_id lp_id
| FromLlbc (FunId (FBuiltin aid), _) ->
Some
(Builtin.BuiltinFunIdMap.find aid ctx.builtin_sigs).explicit_info
| Pure (UpdateAtIndex Array) ->
Some
{
explicit_types = [ Implicit ];
explicit_const_generics = [ Implicit ];
}
| Pure (UpdateAtIndex Slice) ->
Some { explicit_types = [ Implicit ]; explicit_const_generics = [] }
| Pure _ -> None
in
(* Filter the generics.
We might need to filter some of the type arguments, if the type
is builtin (for instance, we filter the global allocator type
argument for `Vec::new`).
*)
let types_explicit =
match fun_id with
| FromLlbc (FunId (FRegular id), _) ->
fun_builtin_filter_types id generics.types explicit ctx
| _ -> Result.Ok (generics.types, explicit)
in
(match types_explicit with
| Ok (types, explicit) ->
extract_generic_args span ctx fmt TypeDeclId.Set.empty ~explicit
{ generics with types }
| Error (types, err) ->
extract_generic_args span ctx fmt TypeDeclId.Set.empty ~explicit
{ generics with types };
save_error __FILE__ __LINE__ (Some span) err;
F.pp_print_string fmt
"(\"ERROR: ill-formed builtin: invalid number of filtering \
arguments\")");
(* Print the arguments *)
List.iter
(fun ve ->
F.pp_print_space fmt ();
extract_texpression span ctx fmt true ve)
args;
(* Close the box for the function call *)
F.pp_close_box fmt ();
(* Return *)
if inside then F.pp_print_string fmt ")"
| (Unop _ | Binop _), _ ->
craise __FILE__ __LINE__ span
("Unreachable:\n" ^ "Function: " ^ show_fun_or_op_id fid
^ ",\nNumber of arguments: "
^ string_of_int (List.length args)
^ ",\nArguments: "
^ String.concat " " (List.map show_texpression args))
(** Subcase of the app case: ADT constructor *)
and extract_adt_cons (span : Meta.span) (ctx : extraction_ctx)
(fmt : F.formatter) (inside : bool) (adt_cons : adt_cons_id)
(generics : generic_args) (args : texpression list) : unit =
let e_ty = TAdt (adt_cons.adt_id, generics) in
let is_single_pat = false in
(* Sanity check: make sure the expression is not a tuple constructor
with no arguments (the properly extracted expression would be a function) *)
cassert __FILE__ __LINE__
(not (adt_cons.adt_id = TTuple && generics.types != [] && args = []))
span "Unreachable";
let _ =
extract_adt_g_value span
(fun ctx inside e ->
extract_texpression span ctx fmt inside e;
ctx)
fmt ctx is_single_pat inside adt_cons.variant_id args e_ty
in
()
(** Subcase of the app case: ADT field projector. *)
and extract_field_projector (span : Meta.span) (ctx : extraction_ctx)
(fmt : F.formatter) (inside : bool) (original_app : texpression)
(proj : projection) (_generics : generic_args) (args : texpression list) :
unit =
(* We isolate the first argument (if there is), in order to pretty print the
* projection ([x.field] instead of [MkAdt?.field x] *)
match args with
| [ arg ] ->
let is_tuple_struct =
PureUtils.type_decl_from_type_id_is_tuple_struct
ctx.trans_ctx.type_ctx.type_infos proj.adt_id
in
(* Check if we extract the type as a tuple, and it only has one field.
In this case, there is no projection. *)
let num_fields =
match proj.adt_id with
| TAdtId id -> (
let d = TypeDeclId.Map.find id ctx.trans_types in
match d.kind with
| Struct fields -> Some (List.length fields)
| _ -> None)
| _ -> None
in
let has_one_field =
match num_fields with
| Some len -> len = 1
| None -> false
in
if is_tuple_struct && has_one_field then
extract_texpression span ctx fmt inside arg
else
(* Exactly one argument: pretty-print *)
let field_name =
(* Check if we need to extract the type as a tuple *)
if is_tuple_struct then
match backend () with
| FStar | HOL4 | Coq -> FieldId.to_string proj.field_id
| Lean ->
(* Tuples in Lean are syntax sugar for nested products/pairs,
so we need to map the field id accordingly.
We give two possibilities:
- either we use the custom syntax [.#i], like in: [(0, 1).#1]
- or we introduce nested projections which use the field
projectors [.1] and [.2], like in: [(0, 1).2.1]
This necessary in some situations, for instance if we have
in Rust:
{[
struct Tuple(u32, (u32, u32));
]}
The issue comes from the fact that in Lean [A * B * C] and [A * (B *
C)] are the same type. As a result, in Rust, field 1 of [Tuple] is
the pair (an element of type [(u32, u32)]), however in Lean it would
be the first element of the pair (an element of type [u32]). If such
situations happen, we allow to force using the nested projectors by
providing the proper command line argument. TODO: we can actually
check the type to determine exactly when we need to use nested
projectors and when we don't.
When using nested projectors, a field id i maps to:
- (.2)^i if i is the last element of the tuple
- (.2)^i.1 otherwise
where (.2)^i denotes .2 repeated i times.
For example, 3 maps to .2.2.2 if the tuple has 4 fields and
to .2.2.2.1 if it has more than 4 fields.
Note that the first "." is added below.
*)
let field_id = FieldId.to_int proj.field_id in
if !use_nested_tuple_projectors then
(* Nested projection: "2.2.2..." *)
if field_id = 0 then "1"
else
let twos_prefix =
String.concat "." (Collections.List.repeat field_id "2")
in
if field_id + 1 = Option.get num_fields then twos_prefix
else twos_prefix ^ ".1"
else "#" ^ string_of_int field_id
else ctx_get_field span proj.adt_id proj.field_id ctx
in
(* Open a box *)
F.pp_open_hovbox fmt ctx.indent_incr;
(* Extract the expression *)
extract_texpression span ctx fmt true arg;
(* We allow to break where the "." appears (except Lean, it's a syntax error) *)
if backend () <> Lean then F.pp_print_break fmt 0 0;
F.pp_print_string fmt ".";
(* If in Coq, the field projection has to be parenthesized *)
(match backend () with
| FStar | Lean | HOL4 -> F.pp_print_string fmt field_name
| Coq -> F.pp_print_string fmt ("(" ^ field_name ^ ")"));
(* Close the box *)
F.pp_close_box fmt ()
| arg :: args ->
(* Call extract_App again, but in such a way that the first argument is
* isolated *)
extract_App span ctx fmt inside (mk_app span original_app arg) args
| [] ->
(* No argument: shouldn't happen *)
craise __FILE__ __LINE__ span "Unreachable"
and extract_Lambda (span : Meta.span) (ctx : extraction_ctx) (fmt : F.formatter)
(inside : bool) (xl : typed_pattern list) (e : texpression) : unit =
(* Open a box for the abs expression *)
F.pp_open_hovbox fmt ctx.indent_incr;
(* Open parentheses *)
if inside then F.pp_print_string fmt "(";
(* Print the lambda - note that there should always be at least one variable *)
sanity_check __FILE__ __LINE__ (xl <> []) span;
F.pp_print_string fmt "fun";
let with_type = backend () = Coq in
let ctx =
List.fold_left
(fun ctx x ->
F.pp_print_space fmt ();
extract_typed_pattern span ctx fmt true true ~with_type x)
ctx xl
in
F.pp_print_space fmt ();
if backend () = Lean || backend () = Coq then F.pp_print_string fmt "=>"
else F.pp_print_string fmt "->";
F.pp_print_space fmt ();
(* Print the body *)
extract_texpression span ctx fmt false e;
(* Close parentheses *)
if inside then F.pp_print_string fmt ")";
(* Close the box for the abs expression *)
F.pp_close_box fmt ()
and extract_lets (span : Meta.span) (ctx : extraction_ctx) (fmt : F.formatter)
(inside : bool) (e : texpression) : unit =
(* Destruct the lets.
Note that in the case of HOL4, we stop destructing the lets if at some point
the "kind" (monadic or non-monadic) of the lets changes.
We do this because in HOL4 the parsing is not very powerful:
if we mix monadic let-bindings and non monadic let-bindings, we have to
wrap the let-bindings inside a [do ... od] whenever we need to extract
a monadic let-binding non immediately inside a monadic let-binding.
Ex.:
{[
do
x <- ...;
let y = f x in
do
z <- g y;
...
od
od
]}
*)
let lets, next_e =
match backend () with
| HOL4 -> destruct_lets_no_interleave span e
| FStar | Coq | Lean -> destruct_lets e
in
(* Extract the let-bindings *)
let extract_let (ctx : extraction_ctx) (monadic : bool) (lv : typed_pattern)
(re : texpression) : extraction_ctx =
(* Open a box for the let-binding *)
F.pp_open_hovbox fmt ctx.indent_incr;
let ctx =
(* There are two cases:
* - do we use a notation like [x <-- y;]
* - do we use notation with let-bindings
* Note that both notations can be used for monadic let-bindings.
* For instance, in F*, you can write:
* {[
* let* x = y in // monadic
* ...
* ]}
* TODO: cleanup
* *)
if monadic && (backend () = Coq || backend () = HOL4) then (
let ctx = extract_typed_pattern span ctx fmt true true lv in
F.pp_print_space fmt ();
let arrow =
match backend () with
| Coq | HOL4 -> "<-"
| FStar | Lean -> craise __FILE__ __LINE__ span "impossible"
in
F.pp_print_string fmt arrow;
F.pp_print_space fmt ();
extract_texpression span ctx fmt false re;
F.pp_print_string fmt ";";
ctx)
else (
(* Print the "let" *)
if monadic then
match backend () with
| FStar ->
F.pp_print_string fmt "let*";
F.pp_print_space fmt ()
| Coq | Lean ->
F.pp_print_string fmt "let";
F.pp_print_space fmt ()
| HOL4 -> ()
else (
F.pp_print_string fmt "let";
F.pp_print_space fmt ());
let ctx = extract_typed_pattern span ctx fmt true true lv in
F.pp_print_space fmt ();
let eq =
match backend () with
| FStar -> "="
| Coq -> ":="
| Lean -> if monadic then "←" else ":="
| HOL4 -> if monadic then "<-" else "="
in
F.pp_print_string fmt eq;
F.pp_print_space fmt ();
extract_texpression span ctx fmt false re;
(* End the let-binding *)
(match backend () with
| Lean ->
(* In Lean, (monadic) let-bindings don't require to end with anything *)
()
| Coq | FStar | HOL4 ->
F.pp_print_space fmt ();
F.pp_print_string fmt "in");
ctx)
in
(* Close the box for the let-binding *)
F.pp_close_box fmt ();
F.pp_print_space fmt ();
(* Return *)
ctx
in
(* Open a box for the whole expression.
In the case of Lean, we use a vbox so that line breaks are inserted
at the end of every let-binding: let-bindings are indeed not ended
with an "in" keyword.
*)
if backend () = Lean then F.pp_open_vbox fmt 0 else F.pp_open_hvbox fmt 0;
(* Open parentheses *)
if inside && backend () <> Lean then F.pp_print_string fmt "(";
(* If Lean and HOL4, we rely on monadic blocks, so we insert a do and open a new box
immediately *)
let wrap_in_do_od = lets_require_wrap_in_do span lets in
if wrap_in_do_od then (
F.pp_print_string fmt "do";
F.pp_print_space fmt ());
let ctx =
List.fold_left
(fun ctx (monadic, lv, re) -> extract_let ctx monadic lv re)
ctx lets
in
(* Open a box for the next expression *)
F.pp_open_hovbox fmt ctx.indent_incr;
(* Print the next expression *)
extract_texpression span ctx fmt false next_e;
(* Close the box for the next expression *)
F.pp_close_box fmt ();
(* do-box (Lean and HOL4 only) *)
if wrap_in_do_od then
if backend () = HOL4 then (
F.pp_print_space fmt ();
F.pp_print_string fmt "od");
(* Close parentheses *)
if inside && backend () <> Lean then F.pp_print_string fmt ")";
(* Close the box for the whole expression *)
F.pp_close_box fmt ()
and extract_Switch (span : Meta.span) (ctx : extraction_ctx) (fmt : F.formatter)
(_inside : bool) (scrut : texpression) (body : switch_body) : unit =
(* Remark: we don't use the [inside] parameter because we extract matches in
a conservative manner: we always make sure they are parenthesized/delimited
with keywords such as [end] *)
(* Open a box for the whole expression.
In the case of Lean, we rely on indentation to delimit the end of the
branches, and need to insert line breaks: we thus use a vbox.
*)
if backend () = Lean then F.pp_open_vbox fmt 0 else F.pp_open_hvbox fmt 0;
(* Extract the switch *)
(match body with
| If (e_then, e_else) ->
(* Open a box for the [if e] *)
F.pp_open_hovbox fmt ctx.indent_incr;
F.pp_print_string fmt "if";
if backend () = Lean && ctx.use_dep_ite then F.pp_print_string fmt " h:";
F.pp_print_space fmt ();
let scrut_inside =
PureUtils.texpression_requires_parentheses span scrut
in
extract_texpression span ctx fmt scrut_inside scrut;
(* Close the box for the [if e] *)
F.pp_close_box fmt ();
(* Extract the branches *)
let extract_branch (is_then : bool) (e_branch : texpression) : unit =
F.pp_print_space fmt ();
(* Open a box for the then/else+branch *)
F.pp_open_hvbox fmt ctx.indent_incr;
(* Open a box for the then/else + space + opening parenthesis *)
F.pp_open_hovbox fmt 0;
let then_or_else = if is_then then "then" else "else" in
F.pp_print_string fmt then_or_else;
let parenth =
PureUtils.texpression_requires_parentheses span e_branch
in
(* Open the parenthesized expression *)
let print_space_after_parenth =
if parenth then (
match backend () with
| FStar ->
F.pp_print_space fmt ();
F.pp_print_string fmt "begin";
F.pp_print_space fmt
| Coq | Lean | HOL4 ->
F.pp_print_space fmt ();
F.pp_print_string fmt "(";
F.pp_print_cut fmt)
else F.pp_print_space fmt
in
(* Close the box for the then/else + space + opening parenthesis *)
F.pp_close_box fmt ();
print_space_after_parenth ();
(* Open a box for the branch *)
F.pp_open_hovbox fmt ctx.indent_incr;
(* Print the branch expression *)
extract_texpression span ctx fmt false e_branch;
(* Close the box for the branch *)
F.pp_close_box fmt ();
(* Close the parenthesized expression *)
(if parenth then
match backend () with
| FStar ->
F.pp_print_space fmt ();
F.pp_print_string fmt "end"
| Coq | Lean | HOL4 -> F.pp_print_string fmt ")");