Source file persistent_env.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
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
open Misc
open Cmi_format
module CU = Compilation_unit
module Consistbl_data = Import_info.Intf.Nonalias.Kind
module Consistbl = Consistbl.Make (CU.Name) (Consistbl_data)
module Style = Misc.Style
let add_delayed_check_forward = ref (fun _ -> assert false)
type error =
| Illegal_renaming of CU.Name.t * CU.Name.t * filepath
| Inconsistent_import of CU.Name.t * filepath * filepath
| Need_recursive_types of CU.Name.t
| Inconsistent_package_declaration_between_imports of
filepath * CU.t * CU.t
| Direct_reference_from_wrong_package of
CU.t * filepath * CU.Prefix.t
| Illegal_import_of_parameter of Global_module.Name.t * filepath
| Not_compiled_as_parameter of Global_module.Name.t
| Imported_module_has_unset_parameter of
{ imported : Global_module.Name.t;
parameter : Global_module.Parameter_name.t;
}
| Imported_module_has_no_such_parameter of
{ imported : CU.Name.t;
valid_parameters : Global_module.Parameter_name.t list;
parameter : Global_module.Parameter_name.t;
value : Global_module.Name.t;
}
| Not_compiled_as_argument of
{ param : Global_module.Parameter_name.t;
value : Global_module.Name.t;
filename : filepath;
}
| Argument_type_mismatch of
{ value : Global_module.Name.t;
filename : filepath;
expected : Global_module.Parameter_name.t;
actual : Global_module.Parameter_name.t;
}
| Unbound_module_as_argument_value of
{ instance: Global_module.Name.t;
value: Global_module.Name.t;
}
exception Error of error
let error err = raise (Error err)
module Persistent_signature = struct
type t =
{ filename : string;
cmi : Cmi_format.cmi_infos_lazy;
visibility : Load_path.visibility }
let load = ref (fun ~allow_hidden ~unit_name ->
let unit_name = CU.Name.to_string unit_name in
match Load_path.find_normalized_with_visibility (unit_name ^ ".cmi") with
| filename, visibility when allow_hidden ->
let cmi = Cmi_cache.read filename in
Some { filename; cmi; visibility}
| filename, (Visible _ as visibility) ->
let cmi = Cmi_cache.read filename in
Some { filename; cmi; visibility}
| _, Hidden
| exception Not_found -> None)
end
type can_load_cmis =
| Can_load_cmis
| Cannot_load_cmis of Lazy_backtrack.log
type global_name_mentioned_by =
| Current
| Other of Global_module.Name.t
type global_name_info = {
gn_global : Global_module.With_precision.t;
gn_mentioned_by : global_name_mentioned_by;
}
type import = {
imp_is_param : bool;
imp_params : Global_module.Parameter_name.t list;
imp_arg_for : Global_module.Parameter_name.t option;
imp_impl : CU.t option;
imp_raw_sign : Signature_with_global_bindings.t;
imp_filename : string;
imp_uid : Shape.Uid.t;
imp_visibility: Load_path.visibility;
imp_crcs : Import_info.Intf.t array;
imp_flags : Cmi_format.pers_flags list;
}
type import_info =
| Missing of { hidden_were_allowed : bool }
| Found of import
type pers_name = {
pn_import : import;
pn_global : Global_module.t;
pn_sign : Subst.Lazy.persistent_signature;
}
type binding =
| Runtime_parameter of Ident.t
| Constant of Compilation_unit.t
type 'a pers_struct_info = {
ps_name_info: pers_name;
ps_binding: binding;
ps_canonical : bool;
ps_val : 'a;
}
module Param_set = Global_module.Parameter_name.Set
type 'a t = {
globals : (Global_module.Name.t, global_name_info) Hashtbl.t;
imports : (CU.Name.t, import_info) Hashtbl.t;
persistent_names : (Global_module.Name.t, pers_name) Hashtbl.t;
persistent_structures :
(Global_module.Name.t, 'a pers_struct_info) Hashtbl.t;
locals_bound_to_runtime_parameters : unit Ident.Tbl.t;
imported_units: CU.Name.Set.t ref;
imported_opaque_units: CU.Name.Set.t ref;
quoted_intfs: CU.Name.Set.t ref;
quoted_impls: CU.Set.t ref;
param_imports : Param_set.t ref;
crc_units: Consistbl.t;
can_load_cmis: can_load_cmis ref;
short_paths_basis: Short_paths.Basis.t ref;
}
let empty () = {
globals = Hashtbl.create 17;
imports = Hashtbl.create 17;
persistent_names = Hashtbl.create 17;
persistent_structures = Hashtbl.create 17;
locals_bound_to_runtime_parameters = Ident.Tbl.create 17;
imported_units = ref CU.Name.Set.empty;
imported_opaque_units = ref CU.Name.Set.empty;
quoted_intfs = ref CU.Name.Set.empty;
quoted_impls = ref CU.Set.empty;
param_imports = ref Param_set.empty;
crc_units = Consistbl.create ();
can_load_cmis = ref Can_load_cmis;
short_paths_basis = ref (Short_paths.Basis.create ());
}
let clear penv =
let {
globals;
imports;
persistent_names;
persistent_structures;
locals_bound_to_runtime_parameters;
imported_units;
imported_opaque_units;
quoted_intfs;
quoted_impls;
param_imports;
crc_units;
can_load_cmis;
short_paths_basis;
} = penv in
Hashtbl.clear globals;
Hashtbl.clear imports;
Hashtbl.clear persistent_names;
Hashtbl.clear persistent_structures;
Ident.Tbl.clear locals_bound_to_runtime_parameters;
imported_units := CU.Name.Set.empty;
imported_opaque_units := CU.Name.Set.empty;
quoted_intfs := CU.Name.Set.empty;
quoted_impls := CU.Set.empty;
param_imports := Param_set.empty;
Consistbl.clear crc_units;
can_load_cmis := Can_load_cmis;
short_paths_basis := Short_paths.Basis.create ();
()
let clear_missing {imports; _} =
let missing_entries =
Hashtbl.fold
(fun name r acc -> match r with
| Missing _ -> name :: acc
| Found _ -> acc)
imports []
in
List.iter (Hashtbl.remove imports) missing_entries
let add_import {imported_units; _} s =
imported_units := CU.Name.Set.add s !imported_units
let rec add_imports_in_name penv (g : Global_module.Name.t) =
add_import penv (g |> CU.Name.of_head_of_global_name);
let add_in_arg ({ param; value } : Global_module.Name.argument) =
add_import penv (param |> CU.Name.of_parameter_name);
add_imports_in_name penv value
in
List.iter add_in_arg g.args
let register_import_as_opaque {imported_opaque_units; _} s =
imported_opaque_units := CU.Name.Set.add s !imported_opaque_units
let find_import_info_in_cache {imports; _} import =
match Hashtbl.find imports import with
| exception Not_found -> None
| Missing _ -> None
| Found imp -> Some imp
let find_name_info_in_cache {persistent_names; _} name =
match Hashtbl.find persistent_names name with
| exception Not_found -> None
| pn -> Some pn
let find_info_in_cache {persistent_structures; _} name =
match Hashtbl.find persistent_structures name with
| exception Not_found -> None
| ps -> Some ps
let find_in_cache penv name =
find_info_in_cache penv name |> Option.map (fun ps -> ps.ps_val)
let register_parameter ({param_imports; _} as penv) modname =
let import = CU.Name.of_parameter_name modname in
begin match find_import_info_in_cache penv import with
| None ->
()
| Some imp ->
if not imp.imp_is_param then
raise (Error (Not_compiled_as_parameter
(Global_module.Name.of_parameter_name modname)))
end;
param_imports := Param_set.add modname !param_imports
let import_crcs penv ~source crcs =
let {crc_units; _} = penv in
let import_crc import_info =
let name = Import_info.Intf.name import_info in
let info = Import_info.Intf.info import_info in
match info with
| None -> ()
| Some (kind, crc) ->
add_import penv name;
Consistbl.check crc_units name kind crc source
in Array.iter import_crc crcs
let check_consistency penv imp =
try import_crcs penv ~source:imp.imp_filename imp.imp_crcs
with Consistbl.Inconsistency {
unit_name = name;
inconsistent_source = source;
original_source = auth;
inconsistent_data = source_kind;
original_data = auth_kind;
} ->
match source_kind, auth_kind with
| Normal source_unit, Normal auth_unit
when not (CU.equal source_unit auth_unit) ->
error (Inconsistent_package_declaration_between_imports(
imp.imp_filename, auth_unit, source_unit))
| (Normal _ | Parameter), _ ->
error (Inconsistent_import(name, auth, source))
let is_registered_parameter_import {param_imports; _} name =
Global_module.Name.mem_parameter_set name !param_imports
let is_parameter_import t modname =
let import = CU.Name.of_head_of_global_name modname in
match find_import_info_in_cache t import with
| Some { imp_is_param; _ } -> imp_is_param
| None -> is_registered_parameter_import t modname
let can_load_cmis penv =
!(penv.can_load_cmis)
let set_can_load_cmis penv setting =
penv.can_load_cmis := setting
let short_paths_basis penv =
!(penv.short_paths_basis)
let without_cmis penv f x =
let log = Lazy_backtrack.log () in
let res =
Misc.(protect_refs
[R (penv.can_load_cmis, Cannot_load_cmis log)]
(fun () -> f x))
in
Lazy_backtrack.backtrack log;
res
let fold {persistent_structures; _} f x =
Hashtbl.fold
(fun name ps x -> if ps.ps_canonical then f name ps.ps_val x else x)
persistent_structures x
let register_pers_for_short_paths penv modname ps components =
let old_style_crcs =
ps.ps_name_info.pn_import.imp_crcs
|> Array.to_list
|> List.map
(fun import ->
let name = Import_info.name import in
let crc = Import_info.crc import in
name, crc)
in
let depends, alias_depends =
List.fold_left
(fun (deps, alias_deps) (name, digest) ->
let name_as_string = Compilation_unit.Name.to_string name in
Short_paths.Basis.add (short_paths_basis penv) name_as_string;
match digest with
| None -> deps, name_as_string :: alias_deps
| Some _ -> name_as_string :: deps, alias_deps)
([], []) old_style_crcs
in
let desc =
Short_paths.Desc.Module.(Fresh (Signature components))
in
let is_deprecated =
List.exists
(function
| Alerts alerts ->
Misc.Stdlib.String.Map.mem "deprecated" alerts ||
Misc.Stdlib.String.Map.mem "ocaml.deprecated" alerts
| _ -> false)
ps.ps_name_info.pn_import.imp_flags
in
let deprecated =
if is_deprecated then Short_paths.Desc.Deprecated
else Short_paths.Desc.Not_deprecated
in
Short_paths.Basis.load (short_paths_basis penv) modname
~depends ~alias_depends desc ps.ps_name_info.pn_import.imp_visibility deprecated
let save_import penv crc modname impl flags filename =
let {crc_units; _} = penv in
List.iter
(function
| Rectypes -> ()
| Alerts _ -> ()
| Opaque -> register_import_as_opaque penv modname)
flags;
Consistbl.check crc_units modname impl crc filename;
add_import penv modname
let acknowledge_import penv ~check modname pers_sig =
let { Persistent_signature.filename; cmi; visibility } = pers_sig in
let found_name = cmi.cmi_name in
let kind = cmi.cmi_kind in
let params = cmi.cmi_params in
let crcs = cmi.cmi_crcs in
let flags = cmi.cmi_flags in
let sign = Signature_with_global_bindings.read_from_cmi cmi in
if not (CU.Name.equal modname found_name) then
error (Illegal_renaming(modname, found_name, filename));
List.iter
(function
| Rectypes ->
if not !Clflags.recursive_types then
error (Need_recursive_types(modname))
| Alerts _ -> ()
| Opaque -> register_import_as_opaque penv modname)
flags;
begin match kind, CU.get_current () with
| Normal { cmi_impl = imported_unit }, Some current_unit ->
let access_allowed =
CU.can_access_by_name imported_unit ~accessed_by:current_unit
in
if not access_allowed then
let prefix = CU.for_pack_prefix current_unit in
error (Direct_reference_from_wrong_package (imported_unit, filename, prefix));
| _, _ -> ()
end;
let is_param =
match kind with
| Normal _ -> false
| Parameter -> true
in
let arg_for, impl =
match kind with
| Normal { cmi_arg_for; cmi_impl } -> cmi_arg_for, Some cmi_impl
| Parameter -> None, None
in
let uid =
match kind with
| Normal { cmi_impl; _ } -> Shape.Uid.of_compilation_unit_id cmi_impl
| Parameter -> Shape.Uid.of_compilation_unit_name modname
in
let {imports; _} = penv in
let import =
{ imp_is_param = is_param;
imp_params = params;
imp_arg_for = arg_for;
imp_impl = impl;
imp_raw_sign = sign;
imp_filename = filename;
imp_uid = uid;
imp_visibility = visibility;
imp_crcs = crcs;
imp_flags = flags;
}
in
if check then check_consistency penv import;
Hashtbl.add imports modname (Found import);
import
let read_import penv ~check modname cmi =
let filename = Unit_info.Artifact.filename cmi in
add_import penv modname;
let cmi = read_cmi_lazy filename in
let pers_sig =
{ Persistent_signature.filename; cmi;
visibility = Visible { cmx_guaranteed = false } }
in
acknowledge_import penv ~check modname pers_sig
let check_visibility ~allow_hidden imp =
match imp.imp_visibility with
| Hidden when not allow_hidden -> raise Not_found
| Hidden | Visible _ -> ()
let find_import ~allow_hidden penv ~check modname =
let {imports; _} = penv in
if CU.Name.equal modname CU.Name.predef_exn then raise Not_found;
match Hashtbl.find imports modname with
| Found imp -> check_visibility ~allow_hidden imp; imp
| Missing { hidden_were_allowed = true } -> raise Not_found
| Missing { hidden_were_allowed = false }
| exception Not_found ->
match can_load_cmis penv with
| Cannot_load_cmis _ -> raise Not_found
| Can_load_cmis ->
let psig =
match !Persistent_signature.load ~allow_hidden ~unit_name:modname with
| Some psig -> psig
| None ->
Hashtbl.replace imports modname
(Missing { hidden_were_allowed = allow_hidden });
raise Not_found
in
add_import penv modname;
acknowledge_import penv ~check modname psig
let remember_global { globals; _ } global ~precision ~mentioned_by =
let global_name = Global_module.to_name global in
match Hashtbl.find globals global_name with
| exception Not_found ->
Hashtbl.add globals global_name
{ gn_global = (global, precision);
gn_mentioned_by = mentioned_by;
}
| { gn_global = old_global;
gn_mentioned_by = first_mentioned_by } ->
let new_global = global, precision in
match
Global_module.With_precision.meet old_global new_global
with
| updated_global ->
if not (old_global == updated_global) then
Hashtbl.replace globals global_name
{ gn_global = updated_global;
gn_mentioned_by = first_mentioned_by }
| exception Global_module.With_precision.Inconsistent ->
let pp_mentioned_by ppf = function
| Current ->
Format_doc.fprintf ppf "this compilation unit"
| Other modname ->
Style.as_inline_code Global_module.Name.print ppf modname
in
Misc.fatal_errorf_doc
"@[<hov>The name %a@ was bound to %a@ by %a@ \
but it is instead bound to %a@ by %a.@]"
(Style.as_inline_code Global_module.Name.print) global_name
(Style.as_inline_code Global_module.With_precision.print) old_global
pp_mentioned_by first_mentioned_by
(Style.as_inline_code Global_module.With_precision.print) new_global
pp_mentioned_by mentioned_by
let rec approximate_global_by_name penv global_name =
let { param_imports; _ } = penv in
let ({ head; args = visible_args } : Global_module.Name.t) = global_name in
let params_not_being_passed, visible_args =
List.fold_left_map
(fun params ({ param; value } : _ Global_module.Argument.t) ->
let params = Param_set.remove param params in
let value = approximate_global_by_name penv value in
let arg : _ Global_module.Argument.t = { param; value } in
params, arg)
!param_imports
visible_args
in
let hidden_args =
Param_set.elements params_not_being_passed
in
let global = Global_module.create_exn head visible_args ~hidden_args in
remember_global penv global ~precision:Approximate ~mentioned_by:Current;
global
let current_unit_is_aux name ~allow_args =
match CU.get_current () with
| None -> false
| Some current ->
match CU.to_global_name current with
| Some { head; args } ->
(args = [] || allow_args)
&& CU.Name.equal name (head |> CU.Name.of_string)
| None -> false
let current_unit_is name =
current_unit_is_aux name ~allow_args:false
let current_unit_is_instance_of name =
current_unit_is_aux name ~allow_args:true
let check_for_unset_parameters penv global =
List.iter
(fun ({ param = parameter; value = arg_value } : Global_module.argument) ->
let value_name = Global_module.to_name arg_value in
if not (is_registered_parameter_import penv value_name) then
error (Imported_module_has_unset_parameter {
imported = Global_module.to_name global;
parameter;
}))
global.Global_module.hidden_args
let rec global_of_global_name penv ~check name ~allow_excess_args =
let load () =
let pn =
find_pers_name ~allow_hidden:true penv ~check name ~allow_excess_args
in
pn.pn_global
in
match Hashtbl.find penv.globals name with
| { gn_global = (global, Exact); _ } -> global
| { gn_global = (_, Approximate); _ } -> load ()
| exception Not_found -> load ()
and compute_global penv modname ~params ~check ~allow_excess_args =
let arg_global_by_param_name =
List.map
(fun ({ param = name; value } : Global_module.Name.argument) ->
match global_of_global_name penv ~check value ~allow_excess_args with
| value -> name, value
| exception Not_found ->
error
(Unbound_module_as_argument_value { instance = modname; value }))
modname.Global_module.Name.args
in
let subst : Global_module.subst =
Global_module.Parameter_name.Map.of_list arg_global_by_param_name
in
if check && modname.Global_module.Name.args <> [] then begin
let compare_by_param param1 (param2, _) =
Global_module.Parameter_name.compare param1 param2
in
Misc.Stdlib.List.merge_iter
~cmp:compare_by_param
params
arg_global_by_param_name
~left_only:
(fun _ ->
())
~right_only:
(fun (param, value) ->
if not allow_excess_args then
raise
(Error (Imported_module_has_no_such_parameter {
imported = CU.Name.of_head_of_global_name modname;
valid_parameters = params;
parameter = param;
value = value |> Global_module.to_name;
})))
~both:
(fun expected_type (_arg_name, arg_value_global) ->
let arg_value = arg_value_global |> Global_module.to_name in
let pn =
find_pers_name ~allow_hidden:true penv ~check arg_value
~allow_excess_args
in
let actual_type =
match pn.pn_import.imp_arg_for with
| None ->
error (Not_compiled_as_argument
{ param = expected_type; value = arg_value;
filename = pn.pn_import.imp_filename })
| Some ty -> ty
in
if not (Global_module.Parameter_name.equal expected_type actual_type)
then begin
raise (Error (Argument_type_mismatch {
value = arg_value;
filename = pn.pn_import.imp_filename;
expected = expected_type;
actual = actual_type;
}))
end)
end;
let global_without_args =
Global_module.create_exn modname.Global_module.Name.head [] ~hidden_args:params
in
let global, _changed = Global_module.subst global_without_args subst in
global
and acknowledge_pers_name penv check global_name import ~allow_excess_args =
let {persistent_names; _} = penv in
let params = import.imp_params in
let global =
compute_global penv global_name ~params ~check ~allow_excess_args
in
let canonical_global_name =
Global_module.to_name global
in
let pn =
match Hashtbl.find_opt persistent_names canonical_global_name with
| Some pn ->
pn
| None ->
acknowledge_new_pers_name penv check canonical_global_name global import
in
if not (Global_module.Name.equal global_name canonical_global_name) then
Hashtbl.add persistent_names global_name pn;
pn
and acknowledge_new_pers_name penv check global_name global import =
check_for_unset_parameters penv global;
let {persistent_names; _} = penv in
let sign = import.imp_raw_sign in
let sign =
let bindings =
List.map
(fun ({ param; value } : Global_module.argument) -> param, value)
global.Global_module.visible_args
in
Signature_with_global_bindings.subst sign bindings
in
Array.iter
(fun (bound_global, precision) ->
remember_global penv bound_global ~precision
~mentioned_by:(Other global_name))
sign.bound_globals;
let pn = { pn_import = import;
pn_global = global;
pn_sign = sign.sign;
} in
if check then check_consistency penv import;
Hashtbl.add persistent_names global_name pn;
remember_global penv global ~precision:Exact ~mentioned_by:Current;
pn
and find_pers_name ~allow_hidden penv ~check name ~allow_excess_args =
let {persistent_names; _} = penv in
match Hashtbl.find persistent_names name with
| pn -> pn
| exception Not_found ->
let unit_name = CU.Name.of_head_of_global_name name in
let import = find_import ~allow_hidden penv ~check unit_name in
acknowledge_pers_name penv check name import ~allow_excess_args
let read_pers_name penv check name filename =
let unit_name = CU.Name.of_head_of_global_name name in
let import = read_import penv ~check unit_name filename in
acknowledge_pers_name penv check name import
let normalize_global_name penv modname =
let new_modname =
global_of_global_name penv modname ~check:true ~allow_excess_args:true
|> Global_module.to_name
in
if Global_module.Name.equal modname new_modname then modname else new_modname
let need_local_ident penv (global : Global_module.t) =
let global_name = global |> Global_module.to_name in
let name = global_name |> CU.Name.of_head_of_global_name in
if is_registered_parameter_import penv global_name
then
true
else if current_unit_is name
then
false
else if current_unit_is_instance_of name
then
false
else if Global_module.is_complete global
then
false
else
true
let make_binding penv (global : Global_module.t) (impl : CU.t option) : binding =
let name = Global_module.to_name global in
if need_local_ident penv global
then Runtime_parameter (Ident.create_local_binding_for_global name)
else
let unit_from_cmi =
match impl with
| Some unit -> unit
| None ->
Misc.fatal_errorf_doc
"Can't bind a parameter statically:@ %a"
Global_module.print global
in
let unit =
match global.visible_args with
| [] ->
assert (Global_module.Name.equal
(unit_from_cmi |> CU.to_global_name_without_prefix)
name);
unit_from_cmi
| _ ->
assert (not (CU.is_packed unit_from_cmi));
CU.of_complete_global_exn global
in
Constant unit
type address =
| Aunit of Compilation_unit.t
| Alocal of Ident.t
| Adot of address * Types.module_representation * int
type 'a sig_reader =
Subst.Lazy.persistent_signature
-> Global_module.Name.t
-> Shape.Uid.t
-> shape:Shape.t
-> address:address
-> flags:Cmi_format.pers_flags list
-> 'a
let acknowledge_new_pers_struct penv modname pers_name val_of_pers_sig short_path_comps =
let {persistent_structures; locals_bound_to_runtime_parameters; _} = penv in
let import = pers_name.pn_import in
let global = pers_name.pn_global in
let sign = pers_name.pn_sign in
let is_param = import.imp_is_param in
let impl = import.imp_impl in
let filename = import.imp_filename in
let uid = import.imp_uid in
let flags = import.imp_flags in
begin match is_param, is_registered_parameter_import penv modname with
| true, false ->
error (Illegal_import_of_parameter(modname, filename))
| false, true ->
error (Not_compiled_as_parameter modname)
| true, true
| false, false -> ()
end;
let binding = make_binding penv global impl in
let address : address =
match binding with
| Runtime_parameter id -> Alocal id
| Constant unit -> Aunit unit
in
let shape =
match import.imp_impl, import.imp_params with
| Some unit, [] -> Shape.for_persistent_unit (CU.full_path_as_string unit)
| _, _ ->
Shape.error ~uid "parameter or parameterised module"
in
let pm = val_of_pers_sig sign modname uid ~shape ~address ~flags in
let ps =
{ ps_name_info = pers_name;
ps_binding = binding;
ps_val = pm;
ps_canonical = true;
}
in
Hashtbl.add persistent_structures modname ps;
register_pers_for_short_paths penv modname ps (short_path_comps modname pm);
begin match binding with
| Runtime_parameter id -> Ident.Tbl.add locals_bound_to_runtime_parameters id ()
| Constant _ -> ()
end;
ps
let acknowledge_pers_struct penv modname pers_name val_of_pers_sig short_path_comps =
let {persistent_structures; _} = penv in
let canonical_modname = Global_module.to_name pers_name.pn_global in
let ps =
match Hashtbl.find_opt persistent_structures canonical_modname with
| Some ps -> ps
| None ->
acknowledge_new_pers_struct penv canonical_modname pers_name
val_of_pers_sig short_path_comps
in
if not (Global_module.Name.equal modname canonical_modname) then
Hashtbl.add persistent_structures modname { ps with ps_canonical = false };
ps
let read_pers_struct penv check modname cmi =
let pers_name =
read_pers_name penv check modname cmi ~allow_excess_args:false
in
pers_name.pn_sign
let find_pers_struct
~allow_hidden penv val_of_pers_sig short_path_comps ~check name ~allow_excess_args =
let {persistent_structures; _} = penv in
match Hashtbl.find persistent_structures name with
| ps -> check_visibility ~allow_hidden ps.ps_name_info.pn_import; ps
| exception Not_found ->
let pers_name =
find_pers_name ~allow_hidden penv ~check name ~allow_excess_args
in
acknowledge_pers_struct penv name pers_name val_of_pers_sig short_path_comps
let describe_prefix ppf prefix =
if CU.Prefix.is_empty prefix then
Format_doc.fprintf ppf "outside of any package"
else
Format_doc.fprintf ppf "package %a" CU.Prefix.print prefix
let check_pers_struct ~allow_hidden penv f1 f2 ~loc name =
let name_as_string = CU.Name.to_string (CU.Name.of_head_of_global_name name) in
try
ignore (find_pers_struct ~allow_hidden penv f1 f2 ~check:false name
~allow_excess_args:true)
with
| Not_found ->
let warn = Warnings.No_cmi_file(name_as_string, None) in
Location.prerr_warning loc warn
| Magic_numbers.Cmi.Error err ->
let msg = Format.asprintf "%a"
Magic_numbers.Cmi.report_error err in
let warn = Warnings.No_cmi_file(name_as_string, Some msg) in
Location.prerr_warning loc warn
| Error err ->
let msg =
match err with
| Illegal_renaming(name, ps_name, filename) ->
Format_doc.doc_printf
" %a@ contains the compiled interface for @ \
%a when %a was expected"
Location.Doc.quoted_filename filename
CU.Name.print_as_inline_code ps_name
CU.Name.print_as_inline_code name
| Inconsistent_import _ ->
assert false
| Need_recursive_types name ->
Format_doc.doc_printf
"%a uses recursive types"
CU.Name.print_as_inline_code name
| Inconsistent_package_declaration_between_imports _ ->
assert false
| Direct_reference_from_wrong_package (unit, _filename, prefix) ->
Format_doc.doc_printf "%a is inaccessible from %a"
CU.print_as_inline_code unit
describe_prefix prefix
| Illegal_import_of_parameter (name, _) ->
Format_doc.doc_printf "%a is a parameter"
(Style.as_inline_code Global_module.Name.print) name
| Not_compiled_as_parameter name ->
Format_doc.doc_printf "%a should be a parameter but isn't"
(Style.as_inline_code Global_module.Name.print) name
| Imported_module_has_unset_parameter { imported; parameter } ->
Format_doc.doc_printf "%a requires argument for %a"
(Style.as_inline_code Global_module.Name.print) imported
(Style.as_inline_code Global_module.Parameter_name.print)
parameter
| Imported_module_has_no_such_parameter { imported; parameter; _ } ->
Format_doc.doc_printf "%a has no parameter %a"
CU.Name.print_as_inline_code imported
(Style.as_inline_code Global_module.Parameter_name.print)
parameter
| Not_compiled_as_argument { value; _ } ->
Format_doc.doc_printf "%a is not compiled as an argument"
(Style.as_inline_code Global_module.Name.print) value
| Argument_type_mismatch { value; expected; actual; _ } ->
Format_doc.doc_printf "%a implements %a, not %a"
(Style.as_inline_code Global_module.Name.print) value
(Style.as_inline_code Global_module.Parameter_name.print) actual
(Style.as_inline_code Global_module.Parameter_name.print) expected
| Unbound_module_as_argument_value { value; _ } ->
Format_doc.doc_printf "Can't find argument %a"
(Style.as_inline_code Global_module.Name.print) value
in
let msg = Format_doc.(asprintf "%a" pp_doc) msg in
let warn = Warnings.No_cmi_file(name_as_string, Some msg) in
Location.prerr_warning loc warn
let read penv modname a =
read_pers_struct penv true modname a
let find ~allow_hidden penv f1 f2 name ~allow_excess_args =
(find_pers_struct ~allow_hidden ~allow_excess_args penv f1 f2 ~check:true
name).ps_val
let check ~allow_hidden penv f1 f2 ~loc name =
let {persistent_structures; _} = penv in
let persistent_structure_visible =
match Hashtbl.find persistent_structures name with
| ps ->
begin
match
check_visibility ~allow_hidden ps.ps_name_info.pn_import
with
| () -> true
| exception Not_found -> false
end
| exception Not_found -> false
in
if not persistent_structure_visible then begin
add_imports_in_name penv name;
let _ : Global_module.t =
approximate_global_by_name penv name
in
if (Warnings.is_active (Warnings.No_cmi_file("", None))) then
!add_delayed_check_forward
(fun () -> check_pers_struct ~allow_hidden penv f1 f2 ~loc name)
end
let crc_of_unit penv name =
match Consistbl.find penv.crc_units name with
| Some (_impl, crc) -> crc
| None ->
let import = find_import ~allow_hidden:true penv ~check:true name in
match Array.find_opt (Import_info.Intf.has_name ~name) import.imp_crcs with
| None -> assert false
| Some import_info ->
match Import_info.crc import_info with
| None -> assert false
| Some crc -> crc
let imports {imported_units; crc_units; _} =
let imports =
Consistbl.extract (CU.Name.Set.elements !imported_units)
crc_units
in
List.map (fun (cu_name, spec) -> Import_info.Intf.create cu_name spec)
imports
let require_intf_for_quote {quoted_intfs; _} name =
quoted_intfs := CU.Name.Set.add name !quoted_intfs
let quoted_intfs { quoted_intfs; _ } = !quoted_intfs
let loaded_transitive_dependencies penv intfs =
let names = ref Compilation_unit.Name.Set.empty in
let rec add_loaded_deps name =
match find_import_info_in_cache penv name with
| None -> ()
| Some { imp_crcs; _ } ->
if not (CU.Name.Set.mem name !names)
then (
names := CU.Name.Set.add name !names;
Array.iter
(fun import_info -> add_loaded_deps (Import_info.name import_info))
imp_crcs)
in
Compilation_unit.Name.Set.iter add_loaded_deps intfs;
!names
let require_impl_for_quote {quoted_impls; _} name =
quoted_impls := CU.Set.add name !quoted_impls
let quoted_impls {quoted_impls; _} = !quoted_impls
let is_imported_parameter penv modname =
match find_info_in_cache penv modname with
| Some pers_struct -> pers_struct.ps_name_info.pn_import.imp_is_param
| None -> false
let runtime_parameter_bindings {persistent_structures; _} =
persistent_structures
|> Hashtbl.to_seq_values
|> Seq.filter_map
(fun ps ->
match ps.ps_binding with
| Runtime_parameter local_ident ->
if ps.ps_canonical then
Some (ps.ps_name_info.pn_global, local_ident)
else
None
| Constant _ -> None)
|> List.of_seq
let is_bound_to_runtime_parameter {locals_bound_to_runtime_parameters; _} id =
Ident.Tbl.mem locals_bound_to_runtime_parameters id
let parameters {param_imports; _} =
Param_set.elements !param_imports
let looked_up {persistent_structures; _} modname =
Hashtbl.mem persistent_structures modname
let is_imported_opaque {imported_opaque_units; _} s =
CU.Name.Set.mem s !imported_opaque_units
let implemented_parameter penv modname =
match find_name_info_in_cache penv modname with
| Some { pn_import = { imp_arg_for; _ }; _ } -> imp_arg_for
| None -> None
let make_cmi penv modname kind sign alerts =
let flags =
List.concat [
if !Clflags.recursive_types then [Cmi_format.Rectypes] else [];
if !Clflags.opaque then [Cmi_format.Opaque] else [];
[Alerts alerts];
]
in
let params =
parameters penv
in
let crcs = imports penv in
let globals =
Hashtbl.to_seq_values penv.globals
|> Seq.filter_map
(fun { gn_global; _ } ->
let global, _precision = gn_global in
if Global_module.is_complete global then
None
else Some gn_global)
|> Array.of_seq
in
{
cmi_name = modname;
cmi_kind = kind;
cmi_globals = globals;
cmi_sign = sign;
cmi_params = params;
cmi_crcs = Array.of_list crcs;
cmi_flags = flags
}
let save_cmi penv psig =
let { Persistent_signature.filename; cmi; _ } = psig in
Misc.try_finally (fun () ->
let {
cmi_name = modname;
cmi_kind = kind;
cmi_flags = flags;
} = cmi in
let crc =
output_to_file_via_temporary
~mode: [Open_binary] filename
(fun temp_filename oc -> output_cmi temp_filename oc cmi) in
let data : Import_info.Intf.Nonalias.Kind.t =
match kind with
| Normal { cmi_impl } -> Normal cmi_impl
| Parameter -> Parameter
in
save_import penv crc modname data flags filename
)
~exceptionally:(fun () -> remove_file filename)
let report_error_doc ppf =
let open Format_doc in
function
| Illegal_renaming(modname, ps_name, filename) -> fprintf ppf
"Wrong file naming: %a@ contains the compiled interface for@ \
%a when %a was expected"
Location.Doc.quoted_filename filename
CU.Name.print_as_inline_code ps_name
CU.Name.print_as_inline_code modname
| Inconsistent_import(name, source1, source2) -> fprintf ppf
"@[<hov>The files %a@ and %a@ \
make inconsistent assumptions@ over interface %a@]"
Location.Doc.quoted_filename source1
Location.Doc.quoted_filename source2
CU.Name.print_as_inline_code name
| Need_recursive_types(import) ->
fprintf ppf
"@[<hov>Invalid import of %a, which uses recursive types.@ \
The compilation flag %a is required@]"
CU.Name.print_as_inline_code import
Style.inline_code "-rectypes"
| Inconsistent_package_declaration_between_imports (filename, unit1, unit2) ->
fprintf ppf
"@[<hov>The file %s@ is imported both as %a@ and as %a.@]"
filename
CU.print_as_inline_code unit1
CU.print_as_inline_code unit2
| Direct_reference_from_wrong_package(unit, filename, prefix) ->
fprintf ppf
"@[<hov>Invalid reference to %a (in file %s) from %a.@ %s]"
CU.print_as_inline_code unit
filename
describe_prefix prefix
"Can only access members of this library's package or a containing package"
| Illegal_import_of_parameter(modname, filename) ->
fprintf ppf
"@[<hov>The file %a@ contains the interface of a parameter.@ \
%a@ is not declared as a parameter for the current unit.@]@.\
@[<hov>@{<hint>Hint@}: \
@[<hov>Compile the current unit with \
@{<inline_code>-parameter %a@}.@]@]"
Location.Doc.quoted_filename filename
(Style.as_inline_code Global_module.Name.print) modname
Global_module.Name.print modname
| Not_compiled_as_parameter modname ->
fprintf ppf
"@[<hov>The module %a@ is a parameter but is not declared as such for the \
current unit.@]@.\
@[<hov>@{<hint>Hint@}: \
@[<hov>Compile the current unit with @{<inline_code>-parameter \
%a@}.@]@]"
(Style.as_inline_code Global_module.Name.print) modname
Global_module.Name.print modname
| Imported_module_has_unset_parameter
{ imported = modname; parameter = param } ->
fprintf ppf
"@[<hov>The module %a@ is not accessible because it takes %a@ \
as a parameter and the current unit does not.@]@.\
@[<hov>@{<hint>Hint@}: \
@[<hov>Pass @{<inline_code>-parameter %a@}@ to add %a@ as a parameter@ \
of the current unit.@]@]"
(Style.as_inline_code Global_module.Name.print) modname
(Style.as_inline_code Global_module.Parameter_name.print) param
Global_module.Parameter_name.print param
(Style.as_inline_code Global_module.Parameter_name.print) param
| Imported_module_has_no_such_parameter
{ valid_parameters; imported = modname; parameter = param; value = _; } ->
let pp_hint ppf () =
match valid_parameters with
| [] ->
fprintf ppf
"Compile %a@ with @{<inline_code>-parameter %a@}@ to make it a \
parameter."
CU.Name.print_as_inline_code modname
Global_module.Parameter_name.print param
| _ ->
let print_params =
Format_doc.pp_print_list ~pp_sep:Format_doc.pp_print_space
(Style.as_inline_code Global_module.Parameter_name.print)
in
fprintf ppf "Parameters for %a:@ @[<hov>%a@]"
CU.Name.print_as_inline_code modname
print_params valid_parameters
in
fprintf ppf
"@[<hov>The module %a@ has no parameter %a.@]@.\
@[<hov>@{<hint>Hint@}: @[<hov>%a@]@]"
CU.Name.print_as_inline_code modname
(Style.as_inline_code Global_module.Parameter_name.print ) param
pp_hint ()
| Not_compiled_as_argument { param; value; filename } ->
fprintf ppf
"@[<hov>The module %a@ cannot be used as an argument for parameter \
%a.@]@.\
@[<hov>@{<hint>Hint@}: \
@[<hov>Compile %a@ with @{<inline_code>-as-argument-for %a@}.@]@]"
(Style.as_inline_code Global_module.Name.print) value
(Style.as_inline_code Global_module.Parameter_name.print ) param
(Style.as_inline_code Location.Doc.filename) filename
Global_module.Parameter_name.print param
| Argument_type_mismatch { value; filename; expected; actual; } ->
fprintf ppf
"@[<hov>The module %a@ is used as an argument for the parameter %a@ \
but %a@ is an argument for %a.@]@.\
@[<hov>@{<hint>Hint@}: \
@[<hov>%a@ was compiled with \
@{<inline_code>-as-argument-for %a@}.@]@]"
(Style.as_inline_code Global_module.Name.print) value
(Style.as_inline_code Global_module.Parameter_name.print ) expected
(Style.as_inline_code Global_module.Name.print) value
(Style.as_inline_code Global_module.Parameter_name.print ) actual
(Style.as_inline_code Location.Doc.filename) filename
Global_module.Parameter_name.print expected
| Unbound_module_as_argument_value { instance; value } ->
fprintf ppf
"@[<hov>Unbound module %a@ in instance %a@]"
(Style.as_inline_code Global_module.Name.print) value
(Style.as_inline_code Global_module.Name.print) instance
let () =
Location.register_error_of_exn
(function
| Error err ->
Some (Location.error_of_printer_file report_error_doc err)
| _ -> None
)
let with_cmis penv f x =
Misc.(protect_refs
[R (penv.can_load_cmis, Can_load_cmis)]
(fun () -> f x))
let forall ~found ~missing t =
Std.Hashtbl.forall t.imports (fun name -> function
| Missing _ -> missing name
| Found import ->
found name import.imp_filename name
)
let report_error = Format_doc.compat report_error_doc