forked from ocaml-flambda/flambda-backend
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathprinttyp.ml
3342 lines (2970 loc) · 112 KB
/
printtyp.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
(**************************************************************************)
(* *)
(* OCaml *)
(* *)
(* Xavier Leroy and Jerome Vouillon, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1996 Institut National de Recherche en Informatique et *)
(* en Automatique. *)
(* *)
(* All rights reserved. This file is distributed under the terms of *)
(* the GNU Lesser General Public License version 2.1, with the *)
(* special exception on linking described in the file LICENSE. *)
(* *)
(**************************************************************************)
(* Printing functions *)
open Misc
open Ctype
open Format
open Longident
open Path
open Asttypes
open Types
open Mode
open Btype
open Outcometree
module String = Misc.Stdlib.String
module Int = Misc.Stdlib.Int
module Sig_component_kind = Shape.Sig_component_kind
module Style = Misc.Style
(* Note [When to print jkind annotations]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Jkind annotations are only occasionally necessary to write
(compilation can often infer jkinds), so when should we print
them? This Note addresses all the cases.
Case (C1). The jkind on a type declaration, like
[type 'a t : <<this one>> = ...].
We print the jkind when it cannot be inferred from the rest of what is
printed. Specifically, we print the user-written jkind in any of these
cases:
(C1.1) The type declaration is abstract, has no manifest (i.e.,
it's written without any [=]-signs), and the annotation is not equivalent to value.
In this case, there is no way to know the jkind without the annotation.
(C1.2) The type is [@@unboxed]. If an [@@unboxed] type is recursive, it can
be impossible to deduce the jkind. We thus defer to the user in determining
whether to print the jkind annotation.
(* CR layouts v2.8: remove this case *)
(C1.3) The type has illegal mode crossings. In this case, the jkind is overridden by
the user rather than being inferred from the definition.
Case (C2). The jkind on a type parameter to a type, like
[type ('a : <<this one>>) t = ...].
This jkind is printed if both of the following are true:
(C2.1) The jkind is something other than the default [value].
(* CR layouts reisenberg: update when the default changes *)
(C2.2) The variable has no constraints on it. (If there is a constraint,
the constraint determines the jkind, so printing the jkind is
redundant.)
We *could*, in theory, print this only when it cannot be inferred.
But this amounts to repeating inference. The heuristic also runs into
trouble when considering the possibility of a recursive type. So, in
order to keep the pretty-printer simple, we just always print the
(non-default) annotation.
Another design possibility is to pass in verbosity level as some kind
of flag.
Case (C3). The jkind on a universal type variable, like
[val f : ('a : <<this one>>). 'a -> 'a].
We should print this jkind annotation whenever it is neither the
default [value] nor an unfilled sort variable. (But see (X1) below.)
(* CR layouts reisenberg: update when the default changes *)
This is a challenge, though, because the type in a [val] does not
explicitly quantify its free variables. So we must collect the free
variables, look to see whether any have interesting jkinds, and
print the whole set of variables if any of them do. This is all
implemented in [extract_qtvs], used also in a number of other places
we do quantification (e.g. gadt-syntax constructors).
Exception (X1). When we are still in the process of inferring a type,
there may be an unfilled sort variable. Here is an example:
{[
module M : sig
val f : int -> bool -> char
end = struct
let f true _ = ()
end
]}
The problem is that [f]'s first parameter is conflicted between being
[int] and [bool]. But the second parameter in the [struct] will have
type ['a : <<sort variable>>]. We generally do not want to print this,
however, and so we don't -- except when [-verbose-types] is set.
We imagine that merlin, when run verbosely, will set [-verbose-types].
This will allow an informative type to be printed for e.g. [let f x = x],
which can work with any sort.
*)
(* Print a long identifier *)
let longident = Pprintast.longident
let () = Env.print_longident := longident
(* Print an identifier avoiding name collisions *)
module Out_name = struct
let create x = { printed_name = x }
let print x = x.printed_name
let set out_name x = out_name.printed_name <- x
end
(** Some identifiers may require hiding when printing *)
type bound_ident = { hide:bool; ident:Ident.t }
(* printing environment for path shortening and naming *)
let printing_env = ref Env.empty
(* When printing, it is important to only observe the
current printing environment, without reading any new
cmi present on the file system *)
let in_printing_env f = Env.without_cmis f !printing_env
let human_unique n id = Printf.sprintf "%s/%d" (Ident.name id) n
type namespace = Sig_component_kind.t =
| Value
| Type
| Constructor
| Label
| Module
| Module_type
| Extension_constructor
| Class
| Class_type
module Namespace = struct
let id = function
| Type -> 0
| Module -> 1
| Module_type -> 2
| Class -> 3
| Class_type -> 4
| Extension_constructor | Value | Constructor | Label -> 5
(* we do not handle those component *)
let size = 1 + id Value
let pp ppf x =
Format.pp_print_string ppf (Shape.Sig_component_kind.to_string x)
(** The two functions below should never access the filesystem,
and thus use {!in_printing_env} rather than directly
accessing the printing environment *)
let lookup =
let to_lookup f lid = fst @@ in_printing_env (f (Lident lid)) in
function
| Some Type -> to_lookup Env.find_type_by_name
| Some Module -> to_lookup Env.find_module_by_name
| Some Module_type -> to_lookup Env.find_modtype_by_name
| Some Class -> to_lookup Env.find_class_by_name
| Some Class_type -> to_lookup Env.find_cltype_by_name
| None | Some(Value|Extension_constructor|Constructor|Label) ->
fun _ -> raise Not_found
let location namespace id =
let path = Path.Pident id in
try Some (
match namespace with
| Some Type -> (in_printing_env @@ Env.find_type path).type_loc
| Some Module -> (in_printing_env @@ Env.find_module path).md_loc
| Some Module_type -> (in_printing_env @@ Env.find_modtype path).mtd_loc
| Some Class -> (in_printing_env @@ Env.find_class path).cty_loc
| Some Class_type -> (in_printing_env @@ Env.find_cltype path).clty_loc
| Some (Extension_constructor|Value|Constructor|Label) | None ->
Location.none
) with Not_found -> None
let best_class_namespace = function
| Papply _ | Pdot _ -> Some Module
| Pextra_ty _ -> assert false (* Only in type path *)
| Pident c ->
match location (Some Class) c with
| Some _ -> Some Class
| None -> Some Class_type
end
(** {2 Conflicts printing}
Conflicts arise when multiple items are attributed the same name,
the following module stores the global conflict references and
provides the printing functions for explaining the source of
the conflicts.
*)
module Conflicts = struct
module M = String.Map
type explanation =
{ kind: namespace; name:string; root_name:string; location:Location.t}
let explanations = ref M.empty
let collect_explanation namespace n id =
let name = human_unique n id in
let root_name = Ident.name id in
if not (M.mem name !explanations) then
match Namespace.location (Some namespace) id with
| None -> ()
| Some location ->
let explanation = { kind = namespace; location; name; root_name } in
explanations := M.add name explanation !explanations
let pp_explanation ppf r=
Format.fprintf ppf "@[<v 2>%a:@,Definition of %s %a@]"
Location.print_loc r.location (Sig_component_kind.to_string r.kind)
Style.inline_code r.name
let print_located_explanations ppf l =
Format.fprintf ppf "@[<v>%a@]" (Format.pp_print_list pp_explanation) l
let reset () = explanations := M.empty
let list_explanations () =
let c = !explanations in
reset ();
c |> M.bindings |> List.map snd |> List.sort Stdlib.compare
let print_toplevel_hint ppf l =
let conj ppf () = Format.fprintf ppf " and@ " in
let pp_namespace_plural ppf n = Format.fprintf ppf "%as" Namespace.pp n in
let root_names = List.map (fun r -> r.kind, r.root_name) l in
let unique_root_names = List.sort_uniq Stdlib.compare root_names in
let submsgs = Array.make Namespace.size [] in
let () = List.iter (fun (n,_ as x) ->
submsgs.(Namespace.id n) <- x :: submsgs.(Namespace.id n)
) unique_root_names in
let pp_submsg ppf names =
match names with
| [] -> ()
| [namespace, a] ->
Format.fprintf ppf
"@ \
@[<2>@{<hint>Hint@}: The %a %a has been defined multiple times@ \
in@ this@ toplevel@ session.@ \
Some toplevel values still refer to@ old@ versions@ of@ this@ %a.\
@ Did you try to redefine them?@]"
Namespace.pp namespace
Style.inline_code a Namespace.pp namespace
| (namespace, _) :: _ :: _ ->
Format.fprintf ppf
"@ \
@[<2>@{<hint>Hint@}: The %a %a have been defined multiple times@ \
in@ this@ toplevel@ session.@ \
Some toplevel values still refer to@ old@ versions@ of@ those@ %a.\
@ Did you try to redefine them?@]"
pp_namespace_plural namespace
Format.(pp_print_list ~pp_sep:conj Style.inline_code)
(List.map snd names)
pp_namespace_plural namespace in
Array.iter (pp_submsg ppf) submsgs
let print_explanations ppf =
let ltop, l =
(* isolate toplevel locations, since they are too imprecise *)
let from_toplevel a =
a.location.Location.loc_start.Lexing.pos_fname = "//toplevel//" in
List.partition from_toplevel (list_explanations ())
in
begin match l with
| [] -> ()
| l -> Format.fprintf ppf "@,%a" print_located_explanations l
end;
(* if there are name collisions in a toplevel session,
display at least one generic hint by namespace *)
print_toplevel_hint ppf ltop
let exists () = M.cardinal !explanations >0
end
module Naming_context = struct
module M = String.Map
module S = String.Set
let enabled = ref true
let enable b = enabled := b
(** Name mapping *)
type mapping =
| Need_unique_name of int Ident.Map.t
(** The same name has already been attributed to multiple types.
The [map] argument contains the specific binding time attributed to each
types.
*)
| Uniquely_associated_to of Ident.t * out_name
(** For now, the name [Ident.name id] has been attributed to [id],
[out_name] is used to expand this name if a conflict arises
at a later point
*)
| Associated_to_pervasives of out_name
(** [Associated_to_pervasives out_name] is used when the item
[Stdlib.$name] has been associated to the name [$name].
Upon a conflict, this name will be expanded to ["Stdlib." ^ name ] *)
let hid_start = 0
let add_hid_id id map =
let new_id = 1 + Ident.Map.fold (fun _ -> Int.max) map hid_start in
new_id, Ident.Map.add id new_id map
let find_hid id map =
try Ident.Map.find id map, map with
Not_found -> add_hid_id id map
let pervasives name = "Stdlib." ^ name
let map = Array.make Namespace.size M.empty
let get namespace = map.(Namespace.id namespace)
let set namespace x = map.(Namespace.id namespace) <- x
(* Names used in recursive definitions are not considered when determining
if a name is already attributed in the current environment.
This is a complementary version of hidden_rec_items used by short-path. *)
let protected = ref S.empty
(* When dealing with functor arguments, identity becomes fuzzy because the same
syntactic argument may be represented by different identifiers during the
error processing, we are thus disabling disambiguation on the argument name
*)
let fuzzy = ref S.empty
let with_arg id f =
protect_refs [ R(fuzzy, S.add (Ident.name id) !fuzzy) ] f
let fuzzy_id namespace id = namespace = Module && S.mem (Ident.name id) !fuzzy
let with_hidden ids f =
let update m id = S.add (Ident.name id.ident) m in
protect_refs [ R(protected, List.fold_left update !protected ids)] f
let pervasives_name namespace name =
match namespace, !enabled with
| None, _ | _, true -> Out_name.create name
| Some namespace, false ->
match M.find name (get namespace) with
| Associated_to_pervasives r -> r
| Need_unique_name _ -> Out_name.create (pervasives name)
| Uniquely_associated_to (id',r) ->
let hid, map = add_hid_id id' Ident.Map.empty in
Out_name.set r (human_unique hid id');
Conflicts.collect_explanation namespace hid id';
set namespace @@ M.add name (Need_unique_name map) (get namespace);
Out_name.create (pervasives name)
| exception Not_found ->
let r = Out_name.create name in
set namespace @@ M.add name (Associated_to_pervasives r) (get namespace);
r
(** Lookup for preexisting named item within the current {!printing_env} *)
let env_ident namespace name =
if S.mem name !protected then None else
match Namespace.lookup namespace name with
| Pident id -> Some id
| _ -> None
| exception Not_found -> None
(** Associate a name to the identifier [id] within [namespace] *)
let ident_name_simple namespace id =
match namespace, !enabled with
| None, _ | _, false -> Out_name.create (Ident.name id)
| Some namespace, true ->
if fuzzy_id namespace id then Out_name.create (Ident.name id)
else
let name = Ident.name id in
match M.find name (get namespace) with
| Uniquely_associated_to (id',r) when Ident.same id id' ->
r
| Need_unique_name map ->
let hid, m = find_hid id map in
Conflicts.collect_explanation namespace hid id;
set namespace @@ M.add name (Need_unique_name m) (get namespace);
Out_name.create (human_unique hid id)
| Uniquely_associated_to (id',r) ->
let hid', m = find_hid id' Ident.Map.empty in
let hid, m = find_hid id m in
Out_name.set r (human_unique hid' id');
List.iter (fun (id,hid) -> Conflicts.collect_explanation namespace hid id)
[id, hid; id', hid' ];
set namespace @@ M.add name (Need_unique_name m) (get namespace);
Out_name.create (human_unique hid id)
| Associated_to_pervasives r ->
Out_name.set r ("Stdlib." ^ Out_name.print r);
let hid, m = find_hid id Ident.Map.empty in
set namespace @@ M.add name (Need_unique_name m) (get namespace);
Out_name.create (human_unique hid id)
| exception Not_found ->
let r = Out_name.create name in
set namespace
@@ M.add name (Uniquely_associated_to (id,r) ) (get namespace);
r
(** Same as {!ident_name_simple} but lookup to existing named identifiers
in the current {!printing_env} *)
let ident_name namespace id =
begin match env_ident namespace (Ident.name id) with
| Some id' -> ignore (ident_name_simple namespace id')
| None -> ()
end;
ident_name_simple namespace id
let reset () =
Array.iteri ( fun i _ -> map.(i) <- M.empty ) map
let with_ctx f =
let old = Array.copy map in
try_finally f
~always:(fun () -> Array.blit old 0 map 0 (Array.length map))
end
let ident_name = Naming_context.ident_name
let reset_naming_context = Naming_context.reset
let ident ppf id = pp_print_string ppf
(Out_name.print (Naming_context.ident_name_simple None id))
let namespaced_ident namespace id =
Out_name.print (Naming_context.ident_name (Some namespace) id)
(* Print a path *)
let ident_stdlib = Ident.create_persistent "Stdlib"
let non_shadowed_pervasive = function
| Pdot(Pident id, s) as path ->
Ident.same id ident_stdlib &&
(match in_printing_env (Env.find_type_by_name (Lident s)) with
| (path', _) -> Path.same path path'
| exception Not_found -> true)
| _ -> false
let find_double_underscore s =
let len = String.length s in
let rec loop i =
if i + 1 >= len then
None
else if s.[i] = '_' && s.[i + 1] = '_' then
Some i
else
loop (i + 1)
in
loop 0
let rec module_path_is_an_alias_of env path ~alias_of =
match Env.find_module path env with
| { md_type = Mty_alias path'; _ } ->
Path.same path' alias_of ||
module_path_is_an_alias_of env path' ~alias_of
| _ -> false
| exception Not_found -> false
let expand_longident_head name =
match find_double_underscore name with
| None -> None
| Some i ->
Some
(Ldot
(Lident (String.sub name 0 i),
Unit_info.modulize
(String.sub name (i + 2) (String.length name - i - 2))))
(* Simple heuristic to print Foo__bar.* as Foo.Bar.* when Foo.Bar is an alias
for Foo__bar. This pattern is used by the stdlib. *)
let rec rewrite_double_underscore_paths env p =
match p with
| Pdot (p, s) ->
Pdot (rewrite_double_underscore_paths env p, s)
| Papply (a, b) ->
Papply (rewrite_double_underscore_paths env a,
rewrite_double_underscore_paths env b)
| Pextra_ty (p, extra) ->
Pextra_ty (rewrite_double_underscore_paths env p, extra)
| Pident id ->
let name = Ident.name id in
match expand_longident_head name with
| None -> p
| Some better_lid ->
match Env.find_module_by_name better_lid env with
| exception Not_found -> p
| p', _ ->
if module_path_is_an_alias_of env p' ~alias_of:p then
p'
else
p
let rewrite_double_underscore_paths env p =
if env == Env.empty then
p
else
rewrite_double_underscore_paths env p
let rec rewrite_double_underscore_longidents env (l : Longident.t) =
match l with
| Ldot (l, s) ->
Ldot (rewrite_double_underscore_longidents env l, s)
| Lapply (a, b) ->
Lapply (rewrite_double_underscore_longidents env a,
rewrite_double_underscore_longidents env b)
| Lident name ->
match expand_longident_head name with
| None -> l
| Some l' ->
match Env.find_module_by_name l env, Env.find_module_by_name l' env with
| exception Not_found -> l
| (p, _), (p', _) ->
if module_path_is_an_alias_of env p' ~alias_of:p then
l'
else
l
let rec tree_of_path namespace = function
| Pident id ->
Oide_ident (ident_name namespace id)
| Pdot(_, s) as path when non_shadowed_pervasive path ->
Oide_ident (Naming_context.pervasives_name namespace s)
| Pdot(p, s) ->
Oide_dot (tree_of_path (Some Module) p, s)
| Papply(p1, p2) ->
Oide_apply (tree_of_path (Some Module) p1, tree_of_path (Some Module) p2)
| Pextra_ty (p, extra) -> begin
(* inline record types are syntactically prevented from escaping their
binding scope, and are never shown to users. *)
match extra with
Pcstr_ty s ->
Oide_dot (tree_of_path (Some Type) p, s)
| Pext_ty ->
tree_of_path None p
end
let tree_of_path namespace p =
tree_of_path namespace (rewrite_double_underscore_paths !printing_env p)
let path ppf p =
!Oprint.out_ident ppf (tree_of_path None p)
let string_of_path p =
Format.asprintf "%a" path p
let strings_of_paths namespace p =
reset_naming_context ();
let trees = List.map (tree_of_path namespace) p in
List.map (Format.asprintf "%a" !Oprint.out_ident) trees
let () = Env.print_path := path
let () = Jkind.set_printtyp_path path
(* Print a recursive annotation *)
let tree_of_rec = function
| Trec_not -> Orec_not
| Trec_first -> Orec_first
| Trec_next -> Orec_next
(* Print a raw type expression, with sharing *)
let raw_list pr ppf = function
[] -> fprintf ppf "[]"
| a :: l ->
fprintf ppf "@[<1>[%a%t]@]" pr a
(fun ppf -> List.iter (fun x -> fprintf ppf ";@,%a" pr x) l)
let kind_vars = ref []
let kind_count = ref 0
let string_of_field_kind v =
match field_kind_repr v with
| Fpublic -> "Fpublic"
| Fabsent -> "Fabsent"
| Fprivate -> "Fprivate"
let rec safe_repr v t =
match Transient_expr.coerce t with
{desc = Tlink t} when not (List.memq t v) ->
safe_repr (t::v) t
| t' -> t'
let rec list_of_memo = function
Mnil -> []
| Mcons (_priv, p, _t1, _t2, rem) -> p :: list_of_memo rem
| Mlink rem -> list_of_memo !rem
let print_name ppf = function
None -> fprintf ppf "None"
| Some name -> fprintf ppf "\"%s\"" name
let string_of_label : Types.arg_label -> string = function
Nolabel -> ""
| Labelled s | Position s -> s
| Optional s -> "?"^s
let visited = ref []
let rec raw_type ppf ty =
let ty = safe_repr [] ty in
if List.memq ty !visited then fprintf ppf "{id=%d}" ty.id else begin
visited := ty :: !visited;
fprintf ppf "@[<1>{id=%d;level=%d;scope=%d;desc=@,%a}@]" ty.id ty.level
ty.scope raw_type_desc ty.desc
end
and labeled_type ppf (label, ty) =
begin match label with
| Some s -> fprintf ppf "label=\"%s\" " s
| None -> ()
end;
raw_type ppf ty
and raw_type_list tl = raw_list raw_type tl
and labeled_type_list tl = raw_list labeled_type tl
and raw_lid_type_list tl =
raw_list (fun ppf (lid, typ) ->
fprintf ppf "(@,%a,@,%a)" longident lid raw_type typ)
tl
and raw_type_desc ppf = function
Tvar { name; jkind } ->
fprintf ppf "Tvar (@,%a,@,%a)" print_name name Jkind.format jkind
| Tarrow((l,arg,ret),t1,t2,c) ->
fprintf ppf "@[<hov1>Tarrow((\"%s\",%a,%a),@,%a,@,%a,@,%s)@]"
(string_of_label l)
(Alloc.print ~verbose:true ()) arg
(Alloc.print ~verbose:true ()) ret
raw_type t1 raw_type t2
(if is_commu_ok c then "Cok" else "Cunknown")
| Ttuple tl ->
fprintf ppf "@[<1>Ttuple@,%a@]" labeled_type_list tl
| Tunboxed_tuple tl ->
fprintf ppf "@[<1>Tunboxed_tuple@,%a@]" labeled_type_list tl
| Tconstr (p, tl, abbrev) ->
fprintf ppf "@[<hov1>Tconstr(@,%a,@,%a,@,%a)@]" path p
raw_type_list tl
(raw_list path) (list_of_memo !abbrev)
| Tobject (t, nm) ->
fprintf ppf "@[<hov1>Tobject(@,%a,@,@[<1>ref%t@])@]" raw_type t
(fun ppf ->
match !nm with None -> fprintf ppf " None"
| Some(p,tl) ->
fprintf ppf "(Some(@,%a,@,%a))" path p raw_type_list tl)
| Tfield (f, k, t1, t2) ->
fprintf ppf "@[<hov1>Tfield(@,%s,@,%s,@,%a,@;<0 -1>%a)@]" f
(string_of_field_kind k)
raw_type t1 raw_type t2
| Tnil -> fprintf ppf "Tnil"
| Tlink t -> fprintf ppf "@[<1>Tlink@,%a@]" raw_type t
| Tsubst (t, None) -> fprintf ppf "@[<1>Tsubst@,(%a,None)@]" raw_type t
| Tsubst (t, Some t') ->
fprintf ppf "@[<1>Tsubst@,(%a,@ Some%a)@]" raw_type t raw_type t'
| Tunivar { name; jkind } ->
fprintf ppf "Tunivar (@,%a,@,%a)" print_name name Jkind.format jkind
| Tpoly (t, tl) ->
fprintf ppf "@[<hov1>Tpoly(@,%a,@,%a)@]"
raw_type t
raw_type_list tl
| Tvariant row ->
let Row {fields; more; name; fixed; closed} = row_repr row in
fprintf ppf
"@[<hov1>{@[%s@,%a;@]@ @[%s@,%a;@]@ %s%B;@ %s%a;@ @[<1>%s%t@]}@]"
"row_fields="
(raw_list (fun ppf (l, f) ->
fprintf ppf "@[%s,@ %a@]" l raw_field f))
fields
"row_more=" raw_type more
"row_closed=" closed
"row_fixed=" raw_row_fixed fixed
"row_name="
(fun ppf ->
match name with None -> fprintf ppf "None"
| Some(p,tl) ->
fprintf ppf "Some(@,%a,@,%a)" path p raw_type_list tl)
| Tpackage (p, fl) ->
fprintf ppf "@[<hov1>Tpackage(@,%a,@,%a)@]" path p
raw_lid_type_list fl
and raw_row_fixed ppf = function
| None -> fprintf ppf "None"
| Some Types.Fixed_private -> fprintf ppf "Some Fixed_private"
| Some Types.Rigid -> fprintf ppf "Some Rigid"
| Some Types.Univar t -> fprintf ppf "Some(Univar(%a))" raw_type t
| Some Types.Reified p -> fprintf ppf "Some(Reified(%a))" path p
and raw_field ppf rf =
match_row_field
~absent:(fun _ -> fprintf ppf "RFabsent")
~present:(function
| None ->
fprintf ppf "RFpresent None"
| Some t ->
fprintf ppf "@[<1>RFpresent(Some@,%a)@]" raw_type t)
~either:(fun c tl m e ->
fprintf ppf "@[<hov1>RFeither(%B,@,%a,@,%B,@,@[<1>ref%t@])@]" c
raw_type_list tl m
(fun ppf ->
match e with None -> fprintf ppf " RFnone"
| Some f -> fprintf ppf "@,@[<1>(%a)@]" raw_field f))
rf
let raw_type_expr ppf t =
visited := []; kind_vars := []; kind_count := 0;
raw_type ppf t;
visited := []; kind_vars := []
let () = Btype.print_raw := raw_type_expr
(* Normalize paths *)
type param_subst = Id | Nth of int | Map of int list
let is_nth = function
Nth _ -> true
| _ -> false
let compose l1 = function
| Id -> Map l1
| Map l2 -> Map (List.map (List.nth l1) l2)
| Nth n -> Nth (List.nth l1 n)
let apply_subst s1 tyl =
if tyl = [] then []
(* cf. PR#7543: Typemod.type_package doesn't respect type constructor arity *)
else
match s1 with
Nth n1 -> [List.nth tyl n1]
| Map l1 -> List.map (List.nth tyl) l1
| Id -> tyl
(* In the [Paths] constructor, more preferred paths are stored later in the
list. *)
type best_path = Paths of Path.t list | Best of Path.t
(** Short-paths cache: the five mutable variables below implement a one-slot
cache for short-paths
*)
let printing_old = ref Env.empty
let printing_pers = ref Compilation_unit.Name.Set.empty
(** {!printing_old} and {!printing_pers} are the keys of the one-slot cache *)
let printing_depth = ref 0
let printing_cont = ref ([] : Env.iter_cont list)
let printing_map = ref Path.Map.empty
(**
- {!printing_map} is the main value stored in the cache.
Note that it is evaluated lazily and its value is updated during printing.
- {!printing_dep} is the current exploration depth of the environment,
it is used to determine whenever the {!printing_map} should be evaluated
further before completing a request.
- {!printing_cont} is the list of continuations needed to evaluate
the {!printing_map} one level further (see also {!Env.run_iter_cont})
*)
let rec index l x =
match l with
[] -> raise Not_found
| a :: l -> if eq_type x a then 0 else 1 + index l x
let rec uniq = function
[] -> true
| a :: l -> not (List.memq (a : int) l) && uniq l
let rec normalize_type_path ?(cache=false) env p =
try
let (params, ty, _) = Env.find_type_expansion p env in
match get_desc ty with
Tconstr (p1, tyl, _) ->
if List.length params = List.length tyl
&& List.for_all2 eq_type params tyl
then normalize_type_path ~cache env p1
else if cache || List.length params <= List.length tyl
|| not (uniq (List.map get_id tyl)) then (p, Id)
else
let l1 = List.map (index params) tyl in
let (p2, s2) = normalize_type_path ~cache env p1 in
(p2, compose l1 s2)
| _ ->
(p, Nth (index params ty))
with
Not_found ->
(Env.normalize_type_path None env p, Id)
let same_printing_env env =
let used_pers = Env.used_persistent () in
Env.same_types !printing_old env
&& Compilation_unit.Name.Set.equal !printing_pers used_pers
let set_printing_env env =
printing_env := env;
if !Clflags.real_paths ||
!printing_env == Env.empty ||
same_printing_env env then
()
else begin
(* printf "Reset printing_map@."; *)
printing_old := env;
printing_pers := Env.used_persistent ();
printing_map := Path.Map.empty;
printing_depth := 0;
(* printf "Recompute printing_map.@."; *)
let cont =
Env.iter_types
(fun p (p', _decl) ->
let (p1, s1) = normalize_type_path env p' ~cache:true in
(* Format.eprintf "%a -> %a = %a@." path p path p' path p1 *)
if s1 = Id then
try
let r = Path.Map.find p1 !printing_map in
match !r with
Paths l -> r := Paths (p :: l)
| Best p' -> r := Paths [p; p'] (* assert false *)
with Not_found ->
(* Jane Street: Often the best choice for printing [p1] is
[p1] itself. And often [p1] is a path whose "penalty"
would be reduced if the double-underscore rewrite
applied.
*)
let rewritten_p1 = rewrite_double_underscore_paths env p1 in
printing_map := Path.Map.add p1 (ref (Paths [ p; rewritten_p1 ])) !printing_map)
env in
printing_cont := [cont];
end
let wrap_printing_env env f =
set_printing_env env; reset_naming_context ();
try_finally f ~always:(fun () -> set_printing_env Env.empty)
let wrap_printing_env ~error env f =
if error then Env.without_cmis (wrap_printing_env env) f
else wrap_printing_env env f
let wrap_printing_env_error env f =
let wrap (loc : _ Location.loc) =
{ loc with txt =
(fun fmt -> Env.without_cmis (fun () -> loc.txt fmt) ())
(* CR nroberts: See https://github.com/ocaml-flambda/flambda-backend/pull/2529
for an explanation of why this has drifted from upstream. *)
}
in
let err : Location.error = wrap_printing_env ~error:true env f in
{ Location.kind = err.kind;
main = wrap err.main;
sub = List.map wrap err.sub;
}
let rec lid_of_path = function
Path.Pident id ->
Longident.Lident (Ident.name id)
| Path.Pdot (p1, s) | Path.Pextra_ty (p1, Pcstr_ty s) ->
Longident.Ldot (lid_of_path p1, s)
| Path.Papply (p1, p2) ->
Longident.Lapply (lid_of_path p1, lid_of_path p2)
| Path.Pextra_ty (p, Pext_ty) -> lid_of_path p
let is_unambiguous path env =
let l = Env.find_shadowed_types path env in
List.exists (Path.same path) l || (* concrete paths are ok *)
match l with
[] -> true
| p :: rem ->
(* allow also coherent paths: *)
let normalize p = fst (normalize_type_path ~cache:true env p) in
let p' = normalize p in
List.for_all (fun p -> Path.same (normalize p) p') rem ||
(* also allow repeatedly defining and opening (for toplevel) *)
let id = lid_of_path p in
List.for_all (fun p -> lid_of_path p = id) rem &&
Path.same p (fst (Env.find_type_by_name id env))
let penalty_size = 10
let name_penalty s =
if s <> "" && s.[0] = '_' then
penalty_size
else
match find_double_underscore s with
| None -> 1
| Some _ -> penalty_size
let ambiguity_penalty path env =
if is_unambiguous path env then 0 else penalty_size
let path_size path env =
let rec size = function
Pident id ->
name_penalty (Ident.name id), -Ident.scope id
| Pdot (p, id) | Pextra_ty (p, Pcstr_ty id) ->
let (l, b) = size p in (name_penalty id + l, b)
| Papply (p1, p2) ->
let (l, b) = size p1 in
(l + fst (size p2), b)
| Pextra_ty (p, _) -> size p
in
let l, s = size path in
l + ambiguity_penalty path env, s
let rec get_best_path r env =
match !r with
Best p' -> p'
| Paths [] -> raise Not_found
| Paths l ->
r := Paths [];
List.iter
(fun p ->
(* Format.eprintf "evaluating %a@." path p; *)
match !r with
Best p' when path_size p env >= path_size p' env -> ()
| _ -> r := Best p)
(List.rev l);
get_best_path r env
let best_type_path p =
if !printing_env == Env.empty
then (p, Id)
else if !Clflags.real_paths
then (p, Id)
else
let (p', s) = normalize_type_path !printing_env p in
let get_path () =
try
get_best_path (Path.Map.find p' !printing_map) !printing_env
with Not_found -> rewrite_double_underscore_paths !printing_env p'
in
while !printing_cont <> [] &&
fst (path_size (get_path ()) !printing_env) > !printing_depth
do
printing_cont := List.map snd (Env.run_iter_cont !printing_cont);
incr printing_depth;
done;
let p'' = get_path () in
(* Format.eprintf "%a = %a -> %a@." path p path p' path p''; *)
(p'', s)
(* Print a type expression *)
let proxy ty = Transient_expr.repr (proxy ty)
(* When printing a type scheme, we print weak names. When printing a plain
type, we do not. This type controls that behavior *)
type type_or_scheme = Type | Type_scheme
let is_non_gen mode ty =
match mode with
| Type_scheme -> is_Tvar ty && get_level ty <> generic_level
| Type -> false
let nameable_row row =
row_name row <> None &&
List.for_all
(fun (_, f) ->
match row_field_repr f with
| Reither(c, l, _) ->
row_closed row && if c then l = [] else List.length l = 1
| _ -> true)
(row_fields row)
(* This specialized version of [Btype.iter_type_expr] normalizes and
short-circuits the traversal of the [type_expr], so that it covers only the
subterms that would be printed by the type printer. *)
let printer_iter_type_expr f ty =
match get_desc ty with
| Tconstr(p, tyl, _) ->
let (_p', s) = best_type_path p in
List.iter f (apply_subst s tyl)
| Tvariant row -> begin
match row_name row with
| Some(_p, tyl) when nameable_row row ->
List.iter f tyl
| _ ->
iter_row f row
end
| Tobject (fi, nm) -> begin
match !nm with
| None ->
let fields, _ = flatten_fields fi in
List.iter
(fun (_, kind, ty) ->
if field_kind_repr kind = Fpublic then
f ty)
fields
| Some (_, l) ->
List.iter f (List.tl l)
end
| Tfield(_, kind, ty1, ty2) ->