My working unpac repository
1(**************************************************************************)
2(* *)
3(* OCaml *)
4(* *)
5(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
6(* *)
7(* Copyright 1996 Institut National de Recherche en Informatique et *)
8(* en Automatique. *)
9(* *)
10(* All rights reserved. This file is distributed under the terms of *)
11(* the GNU Lesser General Public License version 2.1, with the *)
12(* special exception on linking described in the file LICENSE. *)
13(* *)
14(**************************************************************************)
15
16(* Translation from typed abstract syntax to lambda terms,
17 for the module language *)
18
19open Misc
20open Asttypes
21open Path
22open Types
23open Typedtree
24open Lambda
25open Translobj
26open Translcore
27open Translclass
28open Debuginfo.Scoped_location
29
30type unsafe_component =
31 | Unsafe_module_binding
32 | Unsafe_functor
33 | Unsafe_non_function
34 | Unsafe_typext
35
36type unsafe_info =
37 | Unsafe of {
38 reason:unsafe_component;
39 loc:Location.t;
40 path: Path.t
41 }
42 | Unnamed
43
44type error =
45 Circular_dependency of (Ident.t * unsafe_info) list
46| Conflicting_inline_attributes
47
48exception Error of Location.t * error
49
50let cons_opt x_opt xs =
51 match x_opt with
52 | None -> xs
53 | Some x -> x :: xs
54
55(* Keep track of the root path (from the root of the namespace to the
56 currently compiled module expression). Useful for naming extensions. *)
57
58let global_path glob = Some(Pident glob)
59let functor_path path param =
60 match path with
61 None -> None
62 | Some p -> Some(Papply(p, Pident param))
63let field_path path field =
64 match path with
65 None -> None
66 | Some p -> Some(Pdot(p, Ident.name field))
67
68(* Compile type extensions *)
69
70let transl_type_extension ~scopes env rootpath tyext body =
71 List.fold_right
72 (fun ext body ->
73 let lam =
74 transl_extension_constructor ~scopes env
75 (field_path rootpath ext.ext_id) ext
76 in
77 Llet(Strict, Pgenval, ext.ext_id, lam, body))
78 tyext.tyext_constructors
79 body
80
81(* Compile a coercion *)
82
83let rec apply_coercion loc strict restr arg =
84 match restr with
85 Tcoerce_none ->
86 arg
87 | Tcoerce_structure(pos_cc_list, id_pos_list) ->
88 name_lambda strict arg (fun id ->
89 let get_field pos =
90 if pos < 0 then lambda_unit
91 else Lprim(Pfield (pos, Pointer, Mutable), [Lvar id], loc)
92 in
93 let lam =
94 Lprim(Pmakeblock(0, Immutable, None),
95 List.map (apply_coercion_field loc get_field) pos_cc_list,
96 loc)
97 in
98 wrap_id_pos_list loc id_pos_list get_field lam)
99 | Tcoerce_functor(cc_arg, cc_res) ->
100 let param = Ident.create_local "funarg" in
101 let carg = apply_coercion loc Alias cc_arg (Lvar param) in
102 apply_coercion_result loc strict arg [param, Pgenval] [carg] cc_res
103 | Tcoerce_primitive { pc_loc = _; pc_desc; pc_env; pc_type; } ->
104 Translprim.transl_primitive loc pc_desc pc_env pc_type None
105 | Tcoerce_alias (env, path, cc) ->
106 let lam = transl_module_path loc env path in
107 name_lambda strict arg
108 (fun _ -> apply_coercion loc Alias cc lam)
109
110and apply_coercion_field loc get_field (pos, cc) =
111 apply_coercion loc Alias cc (get_field pos)
112
113and apply_coercion_result loc strict funct params args cc_res =
114 match cc_res with
115 | Tcoerce_functor(cc_arg, cc_res) ->
116 let param = Ident.create_local "funarg" in
117 let arg = apply_coercion loc Alias cc_arg (Lvar param) in
118 apply_coercion_result loc strict funct
119 ((param, Pgenval) :: params) (arg :: args) cc_res
120 | _ ->
121 name_lambda strict funct
122 (fun id ->
123 lfunction
124 ~kind:Curried
125 ~params:(List.rev params)
126 ~return:Pgenval
127 ~attr:{ default_function_attribute with
128 is_a_functor = true;
129 stub = true;
130 may_fuse_arity = true; }
131 ~loc
132 ~body:(apply_coercion
133 loc Strict cc_res
134 (Lapply{
135 ap_loc=loc;
136 ap_func=Lvar id;
137 ap_args=List.rev args;
138 ap_tailcall=Default_tailcall;
139 ap_inlined=Default_inline;
140 ap_specialised=Default_specialise;
141 })))
142
143and wrap_id_pos_list loc id_pos_list get_field lam =
144 let fv = free_variables lam in
145 (*Format.eprintf "%a@." Printlambda.lambda lam;
146 Ident.Set.iter (fun id -> Format.eprintf "%a " Ident.print id) fv;
147 Format.eprintf "@.";*)
148 let (lam, _fv, s) =
149 List.fold_left (fun (lam, fv, s) (id',pos,c) ->
150 if Ident.Set.mem id' fv then
151 let id'' = Ident.create_local (Ident.name id') in
152 let rhs = apply_coercion loc Alias c (get_field pos) in
153 let fv_rhs = free_variables rhs in
154 (Llet(Alias, Pgenval, id'', rhs, lam),
155 Ident.Set.union fv fv_rhs,
156 Ident.Map.add id' id'' s)
157 else (lam, fv, s))
158 (lam, fv, Ident.Map.empty) id_pos_list
159 in
160 if s == Ident.Map.empty then lam else Lambda.rename s lam
161
162
163(* Compose two coercions
164 apply_coercion c1 (apply_coercion c2 e) behaves like
165 apply_coercion (compose_coercions c1 c2) e. *)
166
167let rec compose_coercions c1 c2 =
168 match (c1, c2) with
169 (Tcoerce_none, c2) -> c2
170 | (c1, Tcoerce_none) -> c1
171 | (Tcoerce_structure (pc1, ids1), Tcoerce_structure (pc2, ids2)) ->
172 let v2 = Array.of_list pc2 in
173 let ids1 =
174 List.map (fun (id,pos1,c1) ->
175 if pos1 < 0 then (id, pos1, c1)
176 else
177 let (pos2,c2) = v2.(pos1) in
178 (id, pos2, compose_coercions c1 c2))
179 ids1
180 in
181 Tcoerce_structure
182 (List.map
183 (fun pc ->
184 match pc with
185 | _, (Tcoerce_primitive _ | Tcoerce_alias _) ->
186 (* These cases do not take an argument (the position is -1),
187 so they do not need adjusting. *)
188 pc
189 | (p1, c1) ->
190 let (p2, c2) = v2.(p1) in
191 (p2, compose_coercions c1 c2))
192 pc1,
193 ids1 @ ids2)
194 | (Tcoerce_functor(arg1, res1), Tcoerce_functor(arg2, res2)) ->
195 Tcoerce_functor(compose_coercions arg2 arg1,
196 compose_coercions res1 res2)
197 | (c1, Tcoerce_alias (env, path, c2)) ->
198 Tcoerce_alias (env, path, compose_coercions c1 c2)
199 | (_, _) ->
200 fatal_error "Translmod.compose_coercions"
201
202(*
203let apply_coercion a b c =
204 Format.eprintf "@[<2>apply_coercion@ %a@]@." Includemod.print_coercion b;
205 apply_coercion a b c
206
207let compose_coercions c1 c2 =
208 let c3 = compose_coercions c1 c2 in
209 let open Includemod in
210 Format.eprintf "@[<2>compose_coercions@ (%a)@ (%a) =@ %a@]@."
211 print_coercion c1 print_coercion c2 print_coercion c3;
212 c3
213*)
214
215(* Record the primitive declarations occurring in the module compiled *)
216
217let primitive_declarations = ref ([] : Primitive.description list)
218let record_primitive = function
219 | {val_kind=Val_prim p;val_loc} ->
220 Translprim.check_primitive_arity val_loc p;
221 primitive_declarations := p :: !primitive_declarations
222 | _ -> ()
223
224(* Utilities for compiling "module rec" definitions *)
225
226let mod_prim = Lambda.transl_prim "CamlinternalMod"
227
228let undefined_location loc =
229 let (fname, line, char) = Location.get_pos_info loc.Location.loc_start in
230 Lconst(Const_block(0,
231 [Const_immstring fname;
232 const_int line;
233 const_int char]))
234
235exception Initialization_failure of unsafe_info
236
237let init_shape id modl =
238 let rec init_shape_mod path loc env mty =
239 match Mtype.scrape env mty with
240 Mty_ident _
241 | Mty_alias _ ->
242 let info = Unsafe {reason=Unsafe_module_binding;loc; path} in
243 raise (Initialization_failure info)
244 | Mty_signature sg ->
245 Const_block(0, [Const_block(0, init_shape_struct path env sg)])
246 | Mty_functor _ ->
247 (* can we do better? *)
248 let info = Unsafe {reason=Unsafe_functor;loc; path} in
249 raise (Initialization_failure info)
250 and init_shape_struct path env sg =
251 match sg with
252 [] -> []
253 | Sig_value(subid, {val_kind=Val_reg; val_type=ty; val_loc=loc},_) :: rem ->
254 let new_path = Pdot(path, Ident.name subid) in
255 let init_v =
256 match get_desc (Ctype.expand_head env ty) with
257 Tarrow(_,_,_,_) ->
258 const_int 0 (* camlinternalMod.Function *)
259 | Tconstr(p, _, _) when Path.same p Predef.path_lazy_t ->
260 const_int 1 (* camlinternalMod.Lazy *)
261 | _ ->
262 let info =
263 Unsafe {reason=Unsafe_non_function; loc; path=new_path} in
264 raise (Initialization_failure info)
265 in
266 init_v :: init_shape_struct new_path env rem
267 | Sig_value(_, {val_kind=Val_prim _}, _) :: rem ->
268 init_shape_struct path env rem
269 | Sig_value _ :: _rem ->
270 assert false
271 | Sig_type(id, tdecl, _, _) :: rem ->
272 init_shape_struct path (Env.add_type ~check:false id tdecl env) rem
273 | Sig_typext (subid, {ext_loc=loc},_,_) :: _ ->
274 let new_path = Pdot(path, Ident.name subid) in
275 let info = Unsafe {reason=Unsafe_typext; loc; path=new_path} in
276 raise (Initialization_failure info)
277 | Sig_module(id, Mp_present, md, _, _) :: rem ->
278 init_shape_mod (
279 Pdot(path, Ident.name id)) md.md_loc env md.md_type ::
280 init_shape_struct path (Env.add_module_declaration ~check:false
281 id Mp_present md env) rem
282 | Sig_module(id, Mp_absent, md, _, _) :: rem ->
283 init_shape_struct
284 path (Env.add_module_declaration ~check:false
285 id Mp_absent md env) rem
286 | Sig_modtype(id, minfo, _) :: rem ->
287 init_shape_struct path (Env.add_modtype id minfo env) rem
288 | Sig_class _ :: rem ->
289 const_int 2 (* camlinternalMod.Class *)
290 :: init_shape_struct path env rem
291 | Sig_class_type _ :: rem ->
292 init_shape_struct path env rem
293 in
294 try
295 Ok(undefined_location modl.mod_loc,
296 Lconst(
297 init_shape_mod (Path.Pident id) modl.mod_loc modl.mod_env modl.mod_type)
298 )
299 with Initialization_failure reason -> Result.Error(reason)
300
301(* Reorder bindings to honor dependencies. *)
302
303type binding_status =
304 | Undefined
305 | Inprogress of int option (** parent node *)
306 | Defined
307
308type id_or_ignore_loc =
309 | Id of Ident.t
310 | Ignore_loc of Lambda.scoped_location
311
312let extract_unsafe_cycle id status init cycle_start =
313 let info i = match init.(i) with
314 | Result.Error r ->
315 begin match id.(i) with
316 | Id id -> id, r
317 | Ignore_loc _ ->
318 assert false (* Can't refer to something without a name. *)
319 end
320 | Ok _ -> assert false in
321 let rec collect stop l i = match status.(i) with
322 | Inprogress None | Undefined | Defined -> assert false
323 | Inprogress Some i when i = stop -> info i :: l
324 | Inprogress Some i -> collect stop (info i::l) i in
325 collect cycle_start [] cycle_start
326
327let reorder_rec_bindings bindings =
328 let id = Array.of_list (List.map (fun (id,_,_,_) -> id) bindings)
329 and loc = Array.of_list (List.map (fun (_,loc,_,_) -> loc) bindings)
330 and init = Array.of_list (List.map (fun (_,_,init,_) -> init) bindings)
331 and rhs = Array.of_list (List.map (fun (_,_,_,rhs) -> rhs) bindings) in
332 let fv = Array.map Lambda.free_variables rhs in
333 let num_bindings = Array.length id in
334 let status = Array.make num_bindings Undefined in
335 let res = ref [] in
336 let is_unsafe i = match init.(i) with
337 | Ok _ -> false
338 | Result.Error _ -> true in
339 let init_res i = match init.(i) with
340 | Result.Error _ -> None
341 | Ok(a,b) -> Some(a,b) in
342 let rec emit_binding parent i =
343 match status.(i) with
344 Defined -> ()
345 | Inprogress _ ->
346 status.(i) <- Inprogress parent;
347 let cycle = extract_unsafe_cycle id status init i in
348 raise(Error(loc.(i), Circular_dependency cycle))
349 | Undefined ->
350 if is_unsafe i then begin
351 status.(i) <- Inprogress parent;
352 for j = 0 to num_bindings - 1 do
353 match id.(j) with
354 | Id id when Ident.Set.mem id fv.(i) -> emit_binding (Some i) j
355 | _ -> ()
356 done
357 end;
358 res := (id.(i), init_res i, rhs.(i)) :: !res;
359 status.(i) <- Defined in
360 for i = 0 to num_bindings - 1 do
361 match status.(i) with
362 Undefined -> emit_binding None i
363 | Inprogress _ -> assert false
364 | Defined -> ()
365 done;
366 List.rev !res
367
368(* Generate lambda-code for a reordered list of bindings *)
369
370let eval_rec_bindings bindings cont =
371 let rec bind_inits = function
372 [] ->
373 bind_strict bindings
374 | (Ignore_loc _, _, _) :: rem
375 | (_, None, _) :: rem ->
376 bind_inits rem
377 | (Id id, Some(loc, shape), _rhs) :: rem ->
378 Llet(Strict, Pgenval, id,
379 Lapply{
380 ap_loc=Loc_unknown;
381 ap_func=mod_prim "init_mod";
382 ap_args=[loc; shape];
383 ap_tailcall=Default_tailcall;
384 ap_inlined=Default_inline;
385 ap_specialised=Default_specialise;
386 },
387 bind_inits rem)
388 and bind_strict = function
389 [] ->
390 patch_forwards bindings
391 | (Ignore_loc loc, None, rhs) :: rem ->
392 Lsequence(Lprim(Pignore, [rhs], loc), bind_strict rem)
393 | (Id id, None, rhs) :: rem ->
394 Llet(Strict, Pgenval, id, rhs, bind_strict rem)
395 | (_id, Some _, _rhs) :: rem ->
396 bind_strict rem
397 and patch_forwards = function
398 [] ->
399 cont
400 | (Ignore_loc _, _, _rhs) :: rem
401 | (_, None, _rhs) :: rem ->
402 patch_forwards rem
403 | (Id id, Some(_loc, shape), rhs) :: rem ->
404 Lsequence(
405 Lapply {
406 ap_loc=Loc_unknown;
407 ap_func=mod_prim "update_mod";
408 ap_args=[shape; Lvar id; rhs];
409 ap_tailcall=Default_tailcall;
410 ap_inlined=Default_inline;
411 ap_specialised=Default_specialise;
412 },
413 patch_forwards rem)
414 in
415 bind_inits bindings
416
417let compile_recmodule ~scopes compile_rhs bindings cont =
418 eval_rec_bindings
419 (reorder_rec_bindings
420 (List.map
421 (fun {mb_id=id; mb_name; mb_expr=modl; _} ->
422 let id_or_ignore_loc, shape =
423 match id with
424 | None ->
425 let loc = of_location ~scopes mb_name.loc in
426 Ignore_loc loc, Result.Error Unnamed
427 | Some id -> Id id, init_shape id modl
428 in
429 (id_or_ignore_loc, modl.mod_loc, shape, compile_rhs id modl))
430 bindings))
431 cont
432
433(* Code to translate class entries in a structure *)
434
435let transl_class_bindings ~scopes cl_list =
436 let ids = List.map (fun (ci, _) -> ci.ci_id_class) cl_list in
437 (ids,
438 List.map
439 (fun ({ci_id_class=id; ci_expr=cl; ci_virt=vf}, meths) ->
440 let def, rkind = transl_class ~scopes ids id meths cl vf in
441 (id, rkind, def))
442 cl_list)
443
444(* Compile one or more functors, merging curried functors to produce
445 multi-argument functors. Any [@inline] attribute on a functor that is
446 merged must be consistent with any other [@inline] attribute(s) on the
447 functor(s) being merged with. Such an attribute will be placed on the
448 resulting merged functor. *)
449
450let merge_inline_attributes attr1 attr2 loc =
451 match Lambda.merge_inline_attributes attr1 attr2 with
452 | Some attr -> attr
453 | None -> raise (Error (to_location loc, Conflicting_inline_attributes))
454
455let merge_functors ~scopes mexp coercion root_path =
456 let rec merge ~scopes mexp coercion path acc inline_attribute =
457 let finished = acc, mexp, path, coercion, inline_attribute in
458 match mexp.mod_desc with
459 | Tmod_functor (param, body) ->
460 let inline_attribute' =
461 Translattribute.get_inline_attribute mexp.mod_attributes
462 in
463 let arg_coercion, res_coercion =
464 match coercion with
465 | Tcoerce_none -> Tcoerce_none, Tcoerce_none
466 | Tcoerce_functor (arg_coercion, res_coercion) ->
467 arg_coercion, res_coercion
468 | _ -> fatal_error "Translmod.merge_functors: bad coercion"
469 in
470 let loc = of_location ~scopes mexp.mod_loc in
471 let path, param =
472 match param with
473 | Unit -> None, Ident.create_local "*"
474 | Named (None, _, _) ->
475 let id = Ident.create_local "_" in
476 functor_path path id, id
477 | Named (Some id, _, _) -> functor_path path id, id
478 in
479 let inline_attribute =
480 merge_inline_attributes inline_attribute inline_attribute' loc
481 in
482 merge ~scopes body res_coercion path ((param, loc, arg_coercion) :: acc)
483 inline_attribute
484 | _ -> finished
485 in
486 merge ~scopes mexp coercion root_path [] Default_inline
487
488let rec compile_functor ~scopes mexp coercion root_path loc =
489 let functor_params_rev, body, body_path, res_coercion, inline_attribute =
490 merge_functors ~scopes mexp coercion root_path
491 in
492 assert (List.length functor_params_rev >= 1); (* cf. [transl_module] *)
493 let params, body =
494 List.fold_left (fun (params, body) (param, loc, arg_coercion) ->
495 let param' = Ident.rename param in
496 let arg = apply_coercion loc Alias arg_coercion (Lvar param') in
497 let params = (param', Pgenval) :: params in
498 let body = Llet (Alias, Pgenval, param, arg, body) in
499 params, body)
500 ([], transl_module ~scopes res_coercion body_path body)
501 functor_params_rev
502 in
503 lfunction
504 ~kind:Curried
505 ~params
506 ~return:Pgenval
507 ~attr:{
508 inline = inline_attribute;
509 specialise = Default_specialise;
510 local = Default_local;
511 poll = Default_poll;
512 is_a_functor = true;
513 stub = false;
514 tmc_candidate = false;
515 may_fuse_arity = true;
516 }
517 ~loc
518 ~body
519
520(* Compile a module expression *)
521
522and transl_module ~scopes cc rootpath mexp =
523 let loc = of_location ~scopes mexp.mod_loc in
524 match mexp.mod_desc with
525 | Tmod_ident (path,_) ->
526 apply_coercion loc Strict cc
527 (transl_module_path loc mexp.mod_env path)
528 | Tmod_structure str ->
529 transl_struct ~scopes loc [] cc rootpath str
530 | Tmod_functor _ ->
531 oo_wrap mexp.mod_env true (fun () ->
532 compile_functor ~scopes mexp cc rootpath loc) ()
533 | Tmod_apply(funct, arg, ccarg) ->
534 let translated_arg = transl_module ~scopes ccarg None arg in
535 transl_apply ~scopes ~loc ~cc mexp.mod_env funct translated_arg
536 | Tmod_apply_unit funct ->
537 transl_apply ~scopes ~loc ~cc mexp.mod_env funct lambda_unit
538 | Tmod_constraint(arg, _, _, ccarg) ->
539 transl_module ~scopes (compose_coercions cc ccarg) rootpath arg
540 | Tmod_unpack(arg, _) ->
541 apply_coercion loc Strict cc (Translcore.transl_exp ~scopes arg)
542
543and transl_apply ~scopes ~loc ~cc mod_env funct translated_arg =
544 let inlined_attribute =
545 Translattribute.get_inlined_attribute_on_module funct
546 in
547 oo_wrap mod_env true
548 (apply_coercion loc Strict cc)
549 (Lapply{
550 ap_loc=loc;
551 ap_func=transl_module ~scopes Tcoerce_none None funct;
552 ap_args=[translated_arg];
553 ap_tailcall=Default_tailcall;
554 ap_inlined=inlined_attribute;
555 ap_specialised=Default_specialise})
556
557and transl_struct ~scopes loc fields cc rootpath {str_final_env; str_items; _} =
558 transl_structure ~scopes loc fields cc rootpath str_final_env str_items
559
560(* The function transl_structure is called by the bytecode compiler.
561 Some effort is made to compile in top to bottom order, in order to display
562 warning by increasing locations. *)
563and transl_structure ~scopes loc fields cc rootpath final_env = function
564 [] ->
565 let body =
566 match cc with
567 Tcoerce_none ->
568 Lprim(Pmakeblock(0, Immutable, None),
569 List.map (fun id -> Lvar id) (List.rev fields), loc)
570 | Tcoerce_structure(pos_cc_list, id_pos_list) ->
571 (* Do not ignore id_pos_list ! *)
572 (*Format.eprintf "%a@.@[" Includemod.print_coercion cc;
573 List.iter (fun l -> Format.eprintf "%a@ " Ident.print l)
574 fields;
575 Format.eprintf "@]@.";*)
576 let v = Array.of_list (List.rev fields) in
577 let get_field pos =
578 if pos < 0 then lambda_unit
579 else Lvar v.(pos)
580 in
581 let ids = List.fold_right Ident.Set.add fields Ident.Set.empty in
582 let lam =
583 Lprim(Pmakeblock(0, Immutable, None),
584 List.map
585 (fun (pos, cc) ->
586 match cc with
587 Tcoerce_primitive p ->
588 Translprim.transl_primitive
589 (of_location ~scopes p.pc_loc)
590 p.pc_desc p.pc_env p.pc_type None
591 | _ -> apply_coercion loc Strict cc (get_field pos))
592 pos_cc_list, loc)
593 and id_pos_list =
594 List.filter (fun (id,_,_) -> not (Ident.Set.mem id ids))
595 id_pos_list
596 in
597 wrap_id_pos_list loc id_pos_list get_field lam
598 | _ ->
599 fatal_error "Translmod.transl_structure"
600 in
601 (* This debugging event provides information regarding the structure
602 items. It is ignored by the OCaml debugger but is used by
603 Js_of_ocaml to preserve variable names. *)
604 if !Clflags.debug && not !Clflags.native_code then
605 Levent(body,
606 {lev_loc = loc;
607 lev_kind = Lev_pseudo;
608 lev_repr = None;
609 lev_env = final_env})
610 else
611 body
612 | item :: rem ->
613 transl_struct_item ~scopes fields rootpath item
614 (fun fields ->
615 transl_structure ~scopes loc fields cc rootpath final_env rem)
616
617and transl_struct_item ~scopes fields rootpath item next =
618 match item.str_desc with
619 | Tstr_eval (expr, _) ->
620 let body = next fields in
621 Lsequence(transl_exp ~scopes expr, body)
622 | Tstr_value(rec_flag, pat_expr_list) ->
623 (* Translate bindings first *)
624 let mk_lam_let =
625 transl_let ~scopes ~in_structure:true rec_flag pat_expr_list in
626 let ext_fields =
627 List.rev_append (let_bound_idents pat_expr_list) fields in
628 (* Then, translate remainder of struct *)
629 let body = next ext_fields in
630 mk_lam_let body
631 | Tstr_primitive descr ->
632 record_primitive descr.val_val;
633 next fields
634 | Tstr_type _ ->
635 next fields
636 | Tstr_typext(tyext) ->
637 let ids = List.map (fun ext -> ext.ext_id) tyext.tyext_constructors in
638 let body = next (List.rev_append ids fields) in
639 transl_type_extension ~scopes item.str_env rootpath tyext body
640 | Tstr_exception ext ->
641 let id = ext.tyexn_constructor.ext_id in
642 let path = field_path rootpath id in
643 let body = next (id::fields) in
644 Llet(Strict, Pgenval, id,
645 transl_extension_constructor ~scopes
646 item.str_env
647 path
648 ext.tyexn_constructor, body)
649 | Tstr_module ({mb_presence=Mp_present} as mb) ->
650 let id = mb.mb_id in
651 (* Translate module first *)
652 let subscopes = match id with
653 | None -> scopes
654 | Some id -> enter_module_definition ~scopes id in
655 let module_body =
656 transl_module ~scopes:subscopes Tcoerce_none
657 (Option.bind id (field_path rootpath)) mb.mb_expr
658 in
659 let module_body =
660 Translattribute.add_inline_attribute module_body mb.mb_loc
661 mb.mb_attributes
662 in
663 (* Translate remainder second *)
664 let body = next (cons_opt id fields) in
665 begin match id with
666 | None ->
667 Lsequence (Lprim(Pignore, [module_body],
668 of_location ~scopes mb.mb_name.loc), body)
669 | Some id ->
670 Llet(pure_module mb.mb_expr, Pgenval, id, module_body, body)
671 end
672 | Tstr_module ({mb_presence=Mp_absent}) ->
673 next fields
674 | Tstr_recmodule bindings ->
675 let ext_fields =
676 List.rev_append (List.filter_map (fun mb -> mb.mb_id) bindings)
677 fields
678 in
679 let body = next ext_fields in
680 let lam =
681 compile_recmodule ~scopes (fun id modl ->
682 match id with
683 | None -> transl_module ~scopes Tcoerce_none None modl
684 | Some id ->
685 transl_module
686 ~scopes:(enter_module_definition ~scopes id)
687 Tcoerce_none (field_path rootpath id) modl
688 ) bindings body
689 in
690 lam
691 | Tstr_class cl_list ->
692 let (ids, class_bindings) = transl_class_bindings ~scopes cl_list in
693 let body = next (List.rev_append ids fields) in
694 Value_rec_compiler.compile_letrec class_bindings body
695 | Tstr_include incl ->
696 let ids = bound_value_identifiers incl.incl_type in
697 let modl = incl.incl_mod in
698 let mid = Ident.create_local "include" in
699 let rec rebind_idents pos newfields = function
700 [] ->
701 next newfields
702 | id :: ids ->
703 let body =
704 rebind_idents (pos + 1) (id :: newfields) ids
705 in
706 Llet(Alias, Pgenval, id,
707 Lprim(Pfield (pos, Pointer, Mutable),
708 [Lvar mid], of_location ~scopes incl.incl_loc), body)
709 in
710 let body = rebind_idents 0 fields ids in
711 Llet(pure_module modl, Pgenval, mid,
712 transl_module ~scopes Tcoerce_none None modl, body)
713
714 | Tstr_open od ->
715 let pure = pure_module od.open_expr in
716 (* this optimization shouldn't be needed because Simplif would
717 actually remove the [Llet] when it's not used.
718 But since [scan_used_globals] runs before Simplif, we need to do
719 it. *)
720 begin match od.open_bound_items with
721 | [] when pure = Alias ->
722 next fields
723 | _ ->
724 let ids = bound_value_identifiers od.open_bound_items in
725 let mid = Ident.create_local "open" in
726 let rec rebind_idents pos newfields = function
727 [] -> next newfields
728 | id :: ids ->
729 let body =
730 rebind_idents (pos + 1) (id :: newfields) ids
731 in
732 Llet(Alias, Pgenval, id,
733 Lprim(Pfield (pos, Pointer, Mutable), [Lvar mid],
734 of_location ~scopes od.open_loc), body)
735 in
736 let body = rebind_idents 0 fields ids in
737 Llet(pure, Pgenval, mid,
738 transl_module ~scopes Tcoerce_none None od.open_expr, body)
739 end
740 | Tstr_modtype _
741 | Tstr_class_type _
742 | Tstr_attribute _ ->
743 next fields
744
745(* Update forward declaration in Translcore *)
746let _ =
747 Translcore.transl_module := transl_module;
748 Translcore.transl_struct_item := transl_struct_item
749
750(* Introduce dependencies on modules referenced only by "external". *)
751
752let scan_used_globals lam =
753 let is_compunit id = not (Ident.is_predef id) in
754 let globals = ref Ident.Set.empty in
755 let rec scan lam =
756 Lambda.iter_head_constructor scan lam;
757 match lam with
758 Lprim ((Pgetglobal id | Psetglobal id), _, _) when (is_compunit id) ->
759 globals := Ident.Set.add id !globals
760 | _ -> ()
761 in
762 scan lam; !globals
763
764let required_globals ~flambda body =
765 let globals = scan_used_globals body in
766 let add_global id req =
767 if not flambda && Ident.Set.mem id globals then
768 req
769 else
770 Ident.Set.add id req
771 in
772 let required =
773 List.fold_left
774 (fun acc path -> add_global (Path.head path) acc)
775 (if flambda then globals else Ident.Set.empty)
776 (Translprim.get_used_primitives ())
777 in
778 let required =
779 List.fold_right add_global (Env.get_required_globals ()) required
780 in
781 Env.reset_required_globals ();
782 Translprim.clear_used_primitives ();
783 required
784
785(* Compile an implementation *)
786
787let module_block_size component_names coercion =
788 match coercion with
789 | Tcoerce_none -> List.length component_names
790 | Tcoerce_structure (l, _) -> List.length l
791 | Tcoerce_functor _
792 | Tcoerce_primitive _
793 | Tcoerce_alias _ -> assert false
794
795let transl_implementation_flambda module_name (str, cc) =
796 reset_labels ();
797 primitive_declarations := [];
798 Translprim.clear_used_primitives ();
799 let module_id = Ident.create_persistent module_name in
800 let scopes = enter_module_definition ~scopes:empty_scopes module_id in
801 let body =
802 Translobj.transl_label_init
803 (fun () -> transl_struct ~scopes Loc_unknown [] cc
804 (global_path module_id) str)
805 in
806 let size =
807 module_block_size (bound_value_identifiers str.str_type) cc in
808 { module_ident = module_id;
809 main_module_block_size = size;
810 required_globals = required_globals ~flambda:true body;
811 code = body }
812
813let transl_implementation module_name (str, cc) =
814 let implementation =
815 transl_implementation_flambda module_name (str, cc)
816 in
817 let code =
818 Lprim (Psetglobal implementation.module_ident, [implementation.code],
819 Loc_unknown)
820 in
821 { implementation with code }
822
823(* Build the list of value identifiers defined by a toplevel structure
824 (excluding primitive declarations). *)
825
826let rec defined_idents = function
827 [] -> []
828 | item :: rem ->
829 match item.str_desc with
830 | Tstr_eval _ -> defined_idents rem
831 | Tstr_value(_rec_flag, pat_expr_list) ->
832 let_bound_idents pat_expr_list @ defined_idents rem
833 | Tstr_primitive _ -> defined_idents rem
834 | Tstr_type _ -> defined_idents rem
835 | Tstr_typext tyext ->
836 List.map (fun ext -> ext.ext_id) tyext.tyext_constructors
837 @ defined_idents rem
838 | Tstr_exception ext -> ext.tyexn_constructor.ext_id :: defined_idents rem
839 | Tstr_module {mb_id = Some id; mb_presence=Mp_present} ->
840 id :: defined_idents rem
841 | Tstr_module ({mb_id = None}
842 |{mb_presence=Mp_absent}) -> defined_idents rem
843 | Tstr_recmodule decls ->
844 List.filter_map (fun mb -> mb.mb_id) decls @ defined_idents rem
845 | Tstr_modtype _ -> defined_idents rem
846 | Tstr_open od ->
847 bound_value_identifiers od.open_bound_items @ defined_idents rem
848 | Tstr_class cl_list ->
849 List.map (fun (ci, _) -> ci.ci_id_class) cl_list @ defined_idents rem
850 | Tstr_class_type _ -> defined_idents rem
851 | Tstr_include incl ->
852 bound_value_identifiers incl.incl_type @ defined_idents rem
853 | Tstr_attribute _ -> defined_idents rem
854
855(* second level idents (module M = struct ... let id = ... end),
856 and all sub-levels idents *)
857let rec more_idents = function
858 [] -> []
859 | item :: rem ->
860 match item.str_desc with
861 | Tstr_eval _ -> more_idents rem
862 | Tstr_value _ -> more_idents rem
863 | Tstr_primitive _ -> more_idents rem
864 | Tstr_type _ -> more_idents rem
865 | Tstr_typext _ -> more_idents rem
866 | Tstr_exception _ -> more_idents rem
867 | Tstr_recmodule _ -> more_idents rem
868 | Tstr_modtype _ -> more_idents rem
869 | Tstr_open od ->
870 let rest = more_idents rem in
871 begin match od.open_expr.mod_desc with
872 | Tmod_structure str -> all_idents str.str_items @ rest
873 | _ -> rest
874 end
875 | Tstr_class _ -> more_idents rem
876 | Tstr_class_type _ -> more_idents rem
877 | Tstr_include{incl_mod={mod_desc =
878 Tmod_constraint ({mod_desc = Tmod_structure str},
879 _, _, _)
880 | Tmod_structure str }} ->
881 all_idents str.str_items @ more_idents rem
882 | Tstr_include _ -> more_idents rem
883 | Tstr_module
884 {mb_presence=Mp_present; mb_expr={mod_desc = Tmod_structure str}}
885 | Tstr_module
886 {mb_presence=Mp_present;
887 mb_expr={mod_desc=
888 Tmod_constraint ({mod_desc = Tmod_structure str}, _, _, _)}} ->
889 all_idents str.str_items @ more_idents rem
890 | Tstr_module _ -> more_idents rem
891 | Tstr_attribute _ -> more_idents rem
892
893and all_idents = function
894 [] -> []
895 | item :: rem ->
896 match item.str_desc with
897 | Tstr_eval _ -> all_idents rem
898 | Tstr_value(_rec_flag, pat_expr_list) ->
899 let_bound_idents pat_expr_list @ all_idents rem
900 | Tstr_primitive _ -> all_idents rem
901 | Tstr_type _ -> all_idents rem
902 | Tstr_typext tyext ->
903 List.map (fun ext -> ext.ext_id) tyext.tyext_constructors
904 @ all_idents rem
905 | Tstr_exception ext -> ext.tyexn_constructor.ext_id :: all_idents rem
906 | Tstr_recmodule decls ->
907 List.filter_map (fun mb -> mb.mb_id) decls @ all_idents rem
908 | Tstr_modtype _ -> all_idents rem
909 | Tstr_open od ->
910 let rest = all_idents rem in
911 begin match od.open_expr.mod_desc with
912 | Tmod_structure str ->
913 bound_value_identifiers od.open_bound_items
914 @ all_idents str.str_items
915 @ rest
916 | _ -> bound_value_identifiers od.open_bound_items @ rest
917 end
918 | Tstr_class cl_list ->
919 List.map (fun (ci, _) -> ci.ci_id_class) cl_list @ all_idents rem
920 | Tstr_class_type _ -> all_idents rem
921
922 | Tstr_include{incl_type;
923 incl_mod={mod_desc =
924 ( Tmod_constraint({mod_desc=Tmod_structure str}, _, _, _)
925 | Tmod_structure str )}} ->
926 bound_value_identifiers incl_type
927 @ all_idents str.str_items
928 @ all_idents rem
929 | Tstr_include incl ->
930 bound_value_identifiers incl.incl_type @ all_idents rem
931
932 | Tstr_module
933 { mb_id = Some id;
934 mb_presence=Mp_present;
935 mb_expr={mod_desc = Tmod_structure str} }
936 | Tstr_module
937 { mb_id = Some id;
938 mb_presence = Mp_present;
939 mb_expr =
940 {mod_desc =
941 Tmod_constraint ({mod_desc = Tmod_structure str}, _, _, _)}} ->
942 id :: all_idents str.str_items @ all_idents rem
943 | Tstr_module {mb_id = Some id;mb_presence=Mp_present} ->
944 id :: all_idents rem
945 | Tstr_module ({mb_id = None} | {mb_presence=Mp_absent}) -> all_idents rem
946 | Tstr_attribute _ -> all_idents rem
947
948
949(* A variant of transl_structure used to compile toplevel structure definitions
950 for the native-code compiler. Store the defined values in the fields
951 of the global as soon as they are defined, in order to reduce register
952 pressure. Also rewrites the defining expressions so that they
953 refer to earlier fields of the structure through the fields of
954 the global, not by their names.
955 "map" is a table from defined idents to (pos in global block, coercion).
956 "prim" is a list of (pos in global block, primitive declaration). *)
957
958let transl_store_subst = ref Ident.Map.empty
959 (** In the native toplevel, this reference is threaded through successive
960 calls of transl_store_structure *)
961
962let nat_toplevel_name id =
963 try match Ident.Map.find id !transl_store_subst with
964 | Lprim(Pfield (pos, _, _),
965 [Lprim(Pgetglobal glob, [], _)], _) -> (glob,pos)
966 | _ -> raise Not_found
967 with Not_found ->
968 fatal_error("Translmod.nat_toplevel_name: " ^ Ident.unique_name id)
969
970let field_of_str loc str =
971 let ids = Array.of_list (defined_idents str.str_items) in
972 fun (pos, cc) ->
973 match cc with
974 | Tcoerce_primitive { pc_loc = _; pc_desc; pc_env; pc_type; } ->
975 Translprim.transl_primitive loc pc_desc pc_env pc_type None
976 | Tcoerce_alias (env, path, cc) ->
977 let lam = transl_module_path loc env path in
978 apply_coercion loc Alias cc lam
979 | _ -> apply_coercion loc Strict cc (Lvar ids.(pos))
980
981
982let transl_store_structure ~scopes glob map prims aliases str =
983 let no_env_update _ _ env = env in
984 let rec transl_store ~scopes rootpath subst cont = function
985 [] ->
986 transl_store_subst := subst;
987 Lambda.subst no_env_update subst cont
988 | item :: rem ->
989 match item.str_desc with
990 | Tstr_eval (expr, _attrs) ->
991 Lsequence(Lambda.subst no_env_update subst
992 (transl_exp ~scopes expr),
993 transl_store ~scopes rootpath subst cont rem)
994 | Tstr_value(rec_flag, pat_expr_list) ->
995 let ids = let_bound_idents pat_expr_list in
996 let lam =
997 transl_let ~scopes ~in_structure:true rec_flag pat_expr_list
998 (store_idents Loc_unknown ids)
999 in
1000 Lsequence(Lambda.subst no_env_update subst lam,
1001 transl_store ~scopes rootpath
1002 (add_idents false ids subst) cont rem)
1003 | Tstr_primitive descr ->
1004 record_primitive descr.val_val;
1005 transl_store ~scopes rootpath subst cont rem
1006 | Tstr_type _ ->
1007 transl_store ~scopes rootpath subst cont rem
1008 | Tstr_typext(tyext) ->
1009 let ids =
1010 List.map (fun ext -> ext.ext_id) tyext.tyext_constructors
1011 in
1012 let lam =
1013 transl_type_extension ~scopes item.str_env rootpath tyext
1014 (store_idents Loc_unknown ids)
1015 in
1016 Lsequence(Lambda.subst no_env_update subst lam,
1017 transl_store ~scopes rootpath
1018 (add_idents false ids subst) cont rem)
1019 | Tstr_exception ext ->
1020 let id = ext.tyexn_constructor.ext_id in
1021 let path = field_path rootpath id in
1022 let loc = of_location ~scopes ext.tyexn_constructor.ext_loc in
1023 let lam =
1024 transl_extension_constructor ~scopes
1025 item.str_env
1026 path
1027 ext.tyexn_constructor
1028 in
1029 Lsequence(Llet(Strict, Pgenval, id,
1030 Lambda.subst no_env_update subst lam,
1031 store_ident loc id),
1032 transl_store ~scopes rootpath
1033 (add_ident false id subst) cont rem)
1034 | Tstr_module
1035 {mb_id=None; mb_name; mb_presence=Mp_present; mb_expr=modl;
1036 mb_loc=loc; mb_attributes} ->
1037 let lam =
1038 Translattribute.add_inline_attribute
1039 (transl_module ~scopes Tcoerce_none None modl)
1040 loc mb_attributes
1041 in
1042 Lsequence(
1043 Lprim(Pignore,[Lambda.subst no_env_update subst lam],
1044 of_location ~scopes mb_name.loc),
1045 transl_store ~scopes rootpath subst cont rem
1046 )
1047 | Tstr_module{mb_id=Some id;mb_loc=loc;mb_presence=Mp_present;
1048 mb_expr={mod_desc = Tmod_structure str}} ->
1049 let loc = of_location ~scopes loc in
1050 let lam =
1051 transl_store
1052 ~scopes:(enter_module_definition ~scopes id)
1053 (field_path rootpath id) subst
1054 lambda_unit str.str_items
1055 in
1056 (* Careful: see next case *)
1057 let subst = !transl_store_subst in
1058 Lsequence(lam,
1059 Llet(Strict, Pgenval, id,
1060 Lambda.subst no_env_update subst
1061 (Lprim(Pmakeblock(0, Immutable, None),
1062 List.map (fun id -> Lvar id)
1063 (defined_idents str.str_items), loc)),
1064 Lsequence(store_ident loc id,
1065 transl_store ~scopes rootpath
1066 (add_ident true id subst)
1067 cont rem)))
1068 | Tstr_module{
1069 mb_id=Some id;mb_loc=loc;mb_presence=Mp_present;
1070 mb_expr= {
1071 mod_desc = Tmod_constraint (
1072 {mod_desc = Tmod_structure str}, _, _,
1073 (Tcoerce_structure (map, _) as _cc))}
1074 } ->
1075 (* Format.printf "coerc id %s: %a@." (Ident.unique_name id)
1076 Includemod.print_coercion cc; *)
1077 let loc = of_location ~scopes loc in
1078 let lam =
1079 transl_store
1080 ~scopes:(enter_module_definition ~scopes id)
1081 (field_path rootpath id) subst
1082 lambda_unit str.str_items
1083 in
1084 (* Careful: see next case *)
1085 let subst = !transl_store_subst in
1086 let field = field_of_str loc str in
1087 Lsequence(lam,
1088 Llet(Strict, Pgenval, id,
1089 Lambda.subst no_env_update subst
1090 (Lprim(Pmakeblock(0, Immutable, None),
1091 List.map field map, loc)),
1092 Lsequence(store_ident loc id,
1093 transl_store ~scopes rootpath
1094 (add_ident true id subst)
1095 cont rem)))
1096 | Tstr_module
1097 {mb_id=Some id; mb_presence=Mp_present; mb_expr=modl;
1098 mb_loc=loc; mb_attributes} ->
1099 let lam =
1100 Translattribute.add_inline_attribute
1101 (transl_module
1102 ~scopes:(enter_module_definition ~scopes id)
1103 Tcoerce_none (field_path rootpath id) modl)
1104 loc mb_attributes
1105 in
1106 (* Careful: the module value stored in the global may be different
1107 from the local module value, in case a coercion is applied.
1108 If so, keep using the local module value (id) in the remainder of
1109 the compilation unit (add_ident true returns subst unchanged).
1110 If not, we can use the value from the global
1111 (add_ident true adds id -> Pgetglobal... to subst). *)
1112 Llet(Strict, Pgenval, id, Lambda.subst no_env_update subst lam,
1113 Lsequence(store_ident (of_location ~scopes loc) id,
1114 transl_store ~scopes rootpath
1115 (add_ident true id subst)
1116 cont rem))
1117 | Tstr_module ({mb_presence=Mp_absent}) ->
1118 transl_store ~scopes rootpath subst cont rem
1119 | Tstr_recmodule bindings ->
1120 let ids = List.filter_map (fun mb -> mb.mb_id) bindings in
1121 compile_recmodule ~scopes
1122 (fun id modl ->
1123 Lambda.subst no_env_update subst
1124 (match id with
1125 | None ->
1126 transl_module ~scopes Tcoerce_none None modl
1127 | Some id ->
1128 transl_module
1129 ~scopes:(enter_module_definition ~scopes id)
1130 Tcoerce_none (field_path rootpath id) modl))
1131 bindings
1132 (Lsequence(store_idents Loc_unknown ids,
1133 transl_store ~scopes rootpath
1134 (add_idents true ids subst) cont rem))
1135 | Tstr_class cl_list ->
1136 let (ids, class_bindings) = transl_class_bindings ~scopes cl_list in
1137 let lam =
1138 Value_rec_compiler.compile_letrec class_bindings
1139 (store_idents Loc_unknown ids)
1140 in
1141 Lsequence(Lambda.subst no_env_update subst lam,
1142 transl_store ~scopes rootpath (add_idents false ids subst)
1143 cont rem)
1144
1145 | Tstr_include({
1146 incl_loc=loc;
1147 incl_mod= {
1148 mod_desc = Tmod_constraint (
1149 ({mod_desc = Tmod_structure str}), _, _,
1150 (Tcoerce_structure _ | Tcoerce_none))}
1151 | ({ mod_desc = Tmod_structure str});
1152 incl_type;
1153 } as incl) ->
1154 let lam =
1155 transl_store ~scopes None subst lambda_unit str.str_items
1156 (* It is tempting to pass rootpath instead of None
1157 in order to give a more precise name to exceptions
1158 in the included structured, but this would introduce
1159 a difference of behavior compared to bytecode. *)
1160 in
1161 let subst = !transl_store_subst in
1162 let field = field_of_str (of_location ~scopes loc) str in
1163 let ids0 = bound_value_identifiers incl_type in
1164 let rec loop ids args =
1165 match ids, args with
1166 | [], [] ->
1167 transl_store ~scopes rootpath (add_idents true ids0 subst)
1168 cont rem
1169 | id :: ids, arg :: args ->
1170 Llet(Alias, Pgenval, id,
1171 Lambda.subst no_env_update subst (field arg),
1172 Lsequence(store_ident (of_location ~scopes loc) id,
1173 loop ids args))
1174 | _ -> assert false
1175 in
1176 let map =
1177 match incl.incl_mod.mod_desc with
1178 | Tmod_constraint (_, _, _, Tcoerce_structure (map, _)) ->
1179 map
1180 | Tmod_structure _
1181 | Tmod_constraint (_, _, _, Tcoerce_none) ->
1182 List.init (List.length ids0) (fun i -> i, Tcoerce_none)
1183 | _ -> assert false
1184 in
1185 Lsequence(lam, loop ids0 map)
1186
1187 | Tstr_include incl ->
1188 let ids = bound_value_identifiers incl.incl_type in
1189 let modl = incl.incl_mod in
1190 let mid = Ident.create_local "include" in
1191 let loc = incl.incl_loc in
1192 let rec store_idents pos = function
1193 | [] -> transl_store
1194 ~scopes rootpath (add_idents true ids subst) cont rem
1195 | id :: idl ->
1196 Llet(Alias, Pgenval, id,
1197 Lprim(Pfield (pos, Pointer, Mutable), [Lvar mid],
1198 of_location ~scopes loc),
1199 Lsequence(store_ident (of_location ~scopes loc) id,
1200 store_idents (pos + 1) idl))
1201 in
1202 Llet(Strict, Pgenval, mid,
1203 Lambda.subst no_env_update subst
1204 (transl_module ~scopes Tcoerce_none None modl),
1205 store_idents 0 ids)
1206 | Tstr_open od ->
1207 begin match od.open_expr.mod_desc with
1208 | Tmod_structure str ->
1209 let lam =
1210 transl_store ~scopes rootpath subst lambda_unit str.str_items
1211 in
1212 let loc = of_location ~scopes od.open_loc in
1213 let ids = Array.of_list (defined_idents str.str_items) in
1214 let ids0 = bound_value_identifiers od.open_bound_items in
1215 let subst = !transl_store_subst in
1216 let rec store_idents pos = function
1217 | [] -> transl_store ~scopes rootpath
1218 (add_idents true ids0 subst) cont rem
1219 | id :: idl ->
1220 Llet(Alias, Pgenval, id, Lvar ids.(pos),
1221 Lsequence(store_ident loc id,
1222 store_idents (pos + 1) idl))
1223 in
1224 Lsequence(lam, Lambda.subst no_env_update subst
1225 (store_idents 0 ids0))
1226 | _ ->
1227 let pure = pure_module od.open_expr in
1228 (* this optimization shouldn't be needed because Simplif would
1229 actually remove the [Llet] when it's not used.
1230 But since [scan_used_globals] runs before Simplif, we need to
1231 do it. *)
1232 match od.open_bound_items with
1233 | [] when pure = Alias ->
1234 transl_store ~scopes rootpath subst cont rem
1235 | _ ->
1236 let ids = bound_value_identifiers od.open_bound_items in
1237 let mid = Ident.create_local "open" in
1238 let loc = of_location ~scopes od.open_loc in
1239 let rec store_idents pos = function
1240 [] -> transl_store ~scopes rootpath
1241 (add_idents true ids subst) cont rem
1242 | id :: idl ->
1243 Llet(Alias, Pgenval, id,
1244 Lprim(Pfield (pos, Pointer, Mutable),
1245 [Lvar mid], loc),
1246 Lsequence(store_ident loc id,
1247 store_idents (pos + 1) idl))
1248 in
1249 Llet(
1250 pure, Pgenval, mid,
1251 Lambda.subst no_env_update subst
1252 (transl_module ~scopes Tcoerce_none None od.open_expr),
1253 store_idents 0 ids)
1254 end
1255 | Tstr_modtype _
1256 | Tstr_class_type _
1257 | Tstr_attribute _ ->
1258 transl_store ~scopes rootpath subst cont rem
1259
1260 and store_ident loc id =
1261 try
1262 let (pos, cc) = Ident.find_same id map in
1263 let init_val = apply_coercion loc Alias cc (Lvar id) in
1264 Lprim(Psetfield(pos, Pointer, Root_initialization),
1265 [Lprim(Pgetglobal glob, [], loc); init_val],
1266 loc)
1267 with Not_found ->
1268 fatal_error("Translmod.store_ident: " ^ Ident.unique_name id)
1269
1270 and store_idents loc idlist =
1271 make_sequence (store_ident loc) idlist
1272
1273 and add_ident may_coerce id subst =
1274 try
1275 let (pos, cc) = Ident.find_same id map in
1276 match cc with
1277 Tcoerce_none ->
1278 Ident.Map.add id
1279 (Lprim(Pfield (pos, Pointer, Immutable),
1280 [Lprim(Pgetglobal glob, [], Loc_unknown)],
1281 Loc_unknown))
1282 subst
1283 | _ ->
1284 if may_coerce then subst else assert false
1285 with Not_found ->
1286 assert false
1287
1288 and add_idents may_coerce idlist subst =
1289 List.fold_right (add_ident may_coerce) idlist subst
1290
1291 and store_primitive (pos, prim) cont =
1292 Lsequence(Lprim(Psetfield(pos, Pointer, Root_initialization),
1293 [Lprim(Pgetglobal glob, [], Loc_unknown);
1294 Translprim.transl_primitive Loc_unknown
1295 prim.pc_desc prim.pc_env prim.pc_type None],
1296 Loc_unknown),
1297 cont)
1298
1299 and store_alias (pos, env, path, cc) =
1300 let path_lam = transl_module_path Loc_unknown env path in
1301 let init_val = apply_coercion Loc_unknown Strict cc path_lam in
1302 Lprim(Psetfield(pos, Pointer, Root_initialization),
1303 [Lprim(Pgetglobal glob, [], Loc_unknown);
1304 init_val],
1305 Loc_unknown)
1306 in
1307 let aliases = make_sequence store_alias aliases in
1308 List.fold_right store_primitive prims
1309 (transl_store ~scopes (global_path glob) !transl_store_subst aliases str)
1310
1311(* Transform a coercion and the list of value identifiers defined by
1312 a toplevel structure into a table [id -> (pos, coercion)],
1313 with [pos] being the position in the global block where the value of
1314 [id] must be stored, and [coercion] the coercion to be applied to it.
1315 A given identifier may appear several times
1316 in the coercion (if it occurs several times in the signature); remember
1317 to assign it the position of its last occurrence.
1318 Identifiers that are not exported are assigned positions at the
1319 end of the block (beyond the positions of all exported idents).
1320 Also compute the total size of the global block,
1321 and the list of all primitives exported as values. *)
1322
1323let build_ident_map restr idlist more_ids =
1324 let rec natural_map pos map prims aliases = function
1325 | [] ->
1326 (map, prims, aliases, pos)
1327 | id :: rem ->
1328 natural_map (pos+1)
1329 (Ident.add id (pos, Tcoerce_none) map) prims aliases rem
1330 in
1331 let (map, prims, aliases, pos) =
1332 match restr with
1333 | Tcoerce_none ->
1334 natural_map 0 Ident.empty [] [] idlist
1335 | Tcoerce_structure (pos_cc_list, _id_pos_list) ->
1336 (* ignore _id_pos_list as the ids are already bound *)
1337 let idarray = Array.of_list idlist in
1338 let rec export_map pos map prims aliases undef = function
1339 | [] ->
1340 natural_map pos map prims aliases undef
1341 | (_source_pos, Tcoerce_primitive p) :: rem ->
1342 export_map (pos + 1) map
1343 ((pos, p) :: prims) aliases undef rem
1344 | (_source_pos, Tcoerce_alias(env, path, cc)) :: rem ->
1345 export_map (pos + 1) map prims
1346 ((pos, env, path, cc) :: aliases) undef rem
1347 | (source_pos, cc) :: rem ->
1348 let id = idarray.(source_pos) in
1349 export_map (pos + 1) (Ident.add id (pos, cc) map)
1350 prims aliases (list_remove id undef) rem
1351 in
1352 export_map 0 Ident.empty [] [] idlist pos_cc_list
1353 | _ ->
1354 fatal_error "Translmod.build_ident_map"
1355 in
1356 natural_map pos map prims aliases more_ids
1357
1358(* Compile an implementation using transl_store_structure
1359 (for the native-code compiler). *)
1360
1361let transl_store_gen ~scopes module_name ({ str_items = str }, restr) topl =
1362 reset_labels ();
1363 primitive_declarations := [];
1364 Translprim.clear_used_primitives ();
1365 let module_id = Ident.create_persistent module_name in
1366 let (map, prims, aliases, size) =
1367 build_ident_map restr (defined_idents str) (more_idents str) in
1368 let f = function
1369 | [ { str_desc = Tstr_eval (expr, _attrs) } ] when topl ->
1370 assert (size = 0);
1371 Lambda.subst (fun _ _ env -> env) !transl_store_subst
1372 (transl_exp ~scopes expr)
1373 | str -> transl_store_structure ~scopes module_id map prims aliases str
1374 in
1375 transl_store_label_init module_id size f str
1376 (*size, transl_label_init (transl_store_structure module_id map prims str)*)
1377
1378let transl_store_phrases module_name str =
1379 let scopes =
1380 enter_module_definition ~scopes:empty_scopes
1381 (Ident.create_persistent module_name)
1382 in
1383 transl_store_gen ~scopes module_name (str,Tcoerce_none) true
1384
1385let transl_store_implementation module_name (str, restr) =
1386 let s = !transl_store_subst in
1387 transl_store_subst := Ident.Map.empty;
1388 let module_ident = Ident.create_persistent module_name in
1389 let scopes = enter_module_definition ~scopes:empty_scopes module_ident in
1390 let (i, code) = transl_store_gen ~scopes module_name (str, restr) false in
1391 transl_store_subst := s;
1392 { Lambda.main_module_block_size = i;
1393 code;
1394 (* module_ident is not used by closure, but this allow to share
1395 the type with the flambda version *)
1396 module_ident;
1397 required_globals = required_globals ~flambda:true code }
1398
1399(* Compile a toplevel phrase *)
1400
1401let toploop_ident = Ident.create_persistent "Toploop"
1402let toploop_getvalue_pos = 0 (* position of getvalue in module Toploop *)
1403let toploop_setvalue_pos = 1 (* position of setvalue in module Toploop *)
1404
1405let aliased_idents = ref Ident.empty
1406
1407let set_toplevel_unique_name id =
1408 aliased_idents :=
1409 Ident.add id (Ident.unique_toplevel_name id) !aliased_idents
1410
1411let toplevel_name id =
1412 try Ident.find_same id !aliased_idents
1413 with Not_found -> Ident.name id
1414
1415let toploop_getvalue id =
1416 Lapply{
1417 ap_loc=Loc_unknown;
1418 ap_func=Lprim(Pfield (toploop_getvalue_pos, Pointer, Mutable),
1419 [Lprim(Pgetglobal toploop_ident, [], Loc_unknown)],
1420 Loc_unknown);
1421 ap_args=[Lconst(Const_immstring (toplevel_name id))];
1422 ap_tailcall=Default_tailcall;
1423 ap_inlined=Default_inline;
1424 ap_specialised=Default_specialise;
1425 }
1426
1427let toploop_setvalue id lam =
1428 Lapply{
1429 ap_loc=Loc_unknown;
1430 ap_func=Lprim(Pfield (toploop_setvalue_pos, Pointer, Mutable),
1431 [Lprim(Pgetglobal toploop_ident, [], Loc_unknown)],
1432 Loc_unknown);
1433 ap_args=
1434 [Lconst(Const_immstring (toplevel_name id));
1435 lam];
1436 ap_tailcall=Default_tailcall;
1437 ap_inlined=Default_inline;
1438 ap_specialised=Default_specialise;
1439 }
1440
1441let toploop_setvalue_id id = toploop_setvalue id (Lvar id)
1442
1443let close_toplevel_term lam =
1444 Ident.Set.fold (fun id l -> Llet(Strict, Pgenval, id,
1445 toploop_getvalue id, l))
1446 (free_variables lam) lam
1447
1448let transl_toplevel_item ~scopes item =
1449 match item.str_desc with
1450 Tstr_eval (expr, _)
1451 | Tstr_value(Nonrecursive,
1452 [{vb_pat = {pat_desc=Tpat_any};vb_expr = expr}]) ->
1453 (* special compilation for toplevel "let _ = expr", so
1454 that Toploop can display the result of the expression.
1455 Otherwise, the normal compilation would result
1456 in a Lsequence returning unit. *)
1457 transl_exp ~scopes expr
1458 | Tstr_value(rec_flag, pat_expr_list) ->
1459 let idents = let_bound_idents pat_expr_list in
1460 transl_let ~scopes ~in_structure:true rec_flag pat_expr_list
1461 (make_sequence toploop_setvalue_id idents)
1462 | Tstr_typext(tyext) ->
1463 let idents =
1464 List.map (fun ext -> ext.ext_id) tyext.tyext_constructors
1465 in
1466 (* we need to use unique name in case of multiple
1467 definitions of the same extension constructor in the toplevel *)
1468 List.iter set_toplevel_unique_name idents;
1469 transl_type_extension ~scopes item.str_env None tyext
1470 (make_sequence toploop_setvalue_id idents)
1471 | Tstr_exception ext ->
1472 set_toplevel_unique_name ext.tyexn_constructor.ext_id;
1473 toploop_setvalue ext.tyexn_constructor.ext_id
1474 (transl_extension_constructor ~scopes
1475 item.str_env None ext.tyexn_constructor)
1476 | Tstr_module {mb_id=None; mb_presence=Mp_present; mb_expr=modl} ->
1477 transl_module ~scopes Tcoerce_none None modl
1478 | Tstr_module {mb_id=Some id; mb_presence=Mp_present; mb_expr=modl} ->
1479 (* we need to use the unique name for the module because of issues
1480 with "open" (PR#8133) *)
1481 set_toplevel_unique_name id;
1482 let lam = transl_module
1483 ~scopes:(enter_module_definition ~scopes id)
1484 Tcoerce_none (Some(Pident id)) modl in
1485 toploop_setvalue id lam
1486 | Tstr_recmodule bindings ->
1487 let idents = List.filter_map (fun mb -> mb.mb_id) bindings in
1488 compile_recmodule ~scopes
1489 (fun id modl ->
1490 match id with
1491 | None ->
1492 transl_module ~scopes Tcoerce_none None modl
1493 | Some id ->
1494 transl_module
1495 ~scopes:(enter_module_definition ~scopes id)
1496 Tcoerce_none (Some (Pident id)) modl)
1497 bindings
1498 (make_sequence toploop_setvalue_id idents)
1499 | Tstr_class cl_list ->
1500 (* we need to use unique names for the classes because there might
1501 be a value named identically *)
1502 let (ids, class_bindings) = transl_class_bindings ~scopes cl_list in
1503 List.iter set_toplevel_unique_name ids;
1504 Value_rec_compiler.compile_letrec class_bindings
1505 (make_sequence toploop_setvalue_id ids)
1506 | Tstr_include incl ->
1507 let ids = bound_value_identifiers incl.incl_type in
1508 let modl = incl.incl_mod in
1509 let mid = Ident.create_local "include" in
1510 let rec set_idents pos = function
1511 [] ->
1512 lambda_unit
1513 | id :: ids ->
1514 Lsequence(toploop_setvalue id
1515 (Lprim(Pfield (pos, Pointer, Mutable),
1516 [Lvar mid], Loc_unknown)),
1517 set_idents (pos + 1) ids) in
1518 Llet(Strict, Pgenval, mid,
1519 transl_module ~scopes Tcoerce_none None modl, set_idents 0 ids)
1520 | Tstr_primitive descr ->
1521 record_primitive descr.val_val;
1522 lambda_unit
1523 | Tstr_open od ->
1524 let pure = pure_module od.open_expr in
1525 (* this optimization shouldn't be needed because Simplif would
1526 actually remove the [Llet] when it's not used.
1527 But since [scan_used_globals] runs before Simplif, we need to do
1528 it. *)
1529 begin match od.open_bound_items with
1530 | [] when pure = Alias -> lambda_unit
1531 | _ ->
1532 let ids = bound_value_identifiers od.open_bound_items in
1533 let mid = Ident.create_local "open" in
1534 let rec set_idents pos = function
1535 [] ->
1536 lambda_unit
1537 | id :: ids ->
1538 Lsequence(toploop_setvalue id
1539 (Lprim(Pfield (pos, Pointer, Mutable),
1540 [Lvar mid], Loc_unknown)),
1541 set_idents (pos + 1) ids)
1542 in
1543 Llet(pure, Pgenval, mid,
1544 transl_module ~scopes Tcoerce_none None od.open_expr,
1545 set_idents 0 ids)
1546 end
1547 | Tstr_module ({mb_presence=Mp_absent}) ->
1548 lambda_unit
1549 | Tstr_modtype _
1550 | Tstr_type _
1551 | Tstr_class_type _
1552 | Tstr_attribute _ ->
1553 lambda_unit
1554
1555let transl_toplevel_item_and_close ~scopes itm =
1556 close_toplevel_term
1557 (transl_label_init (fun () -> transl_toplevel_item ~scopes itm))
1558
1559let transl_toplevel_definition str =
1560 reset_labels ();
1561 Translprim.clear_used_primitives ();
1562 make_sequence
1563 (transl_toplevel_item_and_close ~scopes:empty_scopes)
1564 str.str_items
1565
1566(* Compile the initialization code for a packed library *)
1567
1568let get_component = function
1569 None -> Lconst const_unit
1570 | Some id -> Lprim(Pgetglobal id, [], Loc_unknown)
1571
1572let transl_package_flambda component_names coercion =
1573 module_block_size component_names coercion,
1574 apply_coercion Loc_unknown Strict coercion
1575 (Lprim(Pmakeblock(0, Immutable, None),
1576 List.map get_component component_names,
1577 Loc_unknown))
1578
1579let transl_package component_names target_name coercion =
1580 let components =
1581 Lprim(Pmakeblock(0, Immutable, None),
1582 List.map get_component component_names, Loc_unknown) in
1583 Lprim(Psetglobal target_name,
1584 [apply_coercion Loc_unknown Strict coercion components],
1585 Loc_unknown)
1586 (*
1587 let components =
1588 match coercion with
1589 Tcoerce_none ->
1590 List.map get_component component_names
1591 | Tcoerce_structure (pos_cc_list, id_pos_list) ->
1592 (* ignore id_pos_list as the ids are already bound *)
1593 let g = Array.of_list component_names in
1594 List.map
1595 (fun (pos, cc) -> apply_coercion Strict cc (get_component g.(pos)))
1596 pos_cc_list
1597 | _ ->
1598 assert false in
1599 Lprim(Psetglobal target_name, [Lprim(Pmakeblock(0, Immutable), components)])
1600 *)
1601
1602let transl_store_package component_names target_name coercion =
1603 let rec make_sequence fn pos arg =
1604 match arg with
1605 [] -> lambda_unit
1606 | hd :: tl -> Lsequence(fn pos hd, make_sequence fn (pos + 1) tl) in
1607 match coercion with
1608 Tcoerce_none ->
1609 (List.length component_names,
1610 make_sequence
1611 (fun pos id ->
1612 Lprim(Psetfield(pos, Pointer, Root_initialization),
1613 [Lprim(Pgetglobal target_name, [], Loc_unknown);
1614 get_component id],
1615 Loc_unknown))
1616 0 component_names)
1617 | Tcoerce_structure (pos_cc_list, _id_pos_list) ->
1618 let components =
1619 Lprim(Pmakeblock(0, Immutable, None),
1620 List.map get_component component_names,
1621 Loc_unknown)
1622 in
1623 let blk = Ident.create_local "block" in
1624 (List.length pos_cc_list,
1625 Llet (Strict, Pgenval, blk,
1626 apply_coercion Loc_unknown Strict coercion components,
1627 make_sequence
1628 (fun pos _id ->
1629 Lprim(Psetfield(pos, Pointer, Root_initialization),
1630 [Lprim(Pgetglobal target_name, [], Loc_unknown);
1631 Lprim(Pfield (pos, Pointer, Mutable),
1632 [Lvar blk], Loc_unknown)],
1633 Loc_unknown))
1634 0 pos_cc_list))
1635 (*
1636 (* ignore id_pos_list as the ids are already bound *)
1637 let id = Array.of_list component_names in
1638 (List.length pos_cc_list,
1639 make_sequence
1640 (fun dst (src, cc) ->
1641 Lprim(Psetfield(dst, false),
1642 [Lprim(Pgetglobal target_name, []);
1643 apply_coercion Strict cc (get_component id.(src))]))
1644 0 pos_cc_list)
1645 *)
1646 | _ -> assert false
1647
1648(* Error report *)
1649
1650open Format_doc
1651module Style = Misc.Style
1652
1653let print_cycle ppf cycle =
1654 let print_ident ppf (x,_) = pp_print_string ppf (Ident.name x) in
1655 let pp_sep ppf () = fprintf ppf "@ -> " in
1656 fprintf ppf "%a%a%s"
1657 (pp_print_list ~pp_sep print_ident) cycle
1658 pp_sep ()
1659 (Ident.name @@ fst @@ List.hd cycle)
1660
1661let rec collect_components = function
1662 | Pident id -> [Ident.name id]
1663 | Pdot (p, s) -> collect_components p @ [s]
1664 | Papply (p, _) -> collect_components p
1665 | Pextra_ty (p, _) -> collect_components p
1666
1667let get_relative_path top_module path =
1668 let comps = collect_components path in
1669 let comps =
1670 match comps with
1671 | h :: (_ :: _ as t) when h = top_module -> t
1672 | _ -> comps
1673 in
1674 String.concat "." comps
1675
1676
1677let explanation_submsg (id, unsafe_info) =
1678 match unsafe_info with
1679 | Unnamed -> assert false (* can't be part of a cycle. *)
1680 | Unsafe {reason; loc; path} ->
1681 let print fmt =
1682 let printer =
1683 let top_module = Ident.name id in
1684 let guilty = get_relative_path top_module path in
1685 doc_printf fmt
1686 Style.inline_code top_module
1687 Style.inline_code guilty in
1688 Location.mkloc printer loc in
1689 match reason with
1690 | Unsafe_module_binding ->
1691 print "Module %a defines an unsafe module, %a ."
1692 | Unsafe_functor ->
1693 print "Module %a defines an unsafe functor, %a ."
1694 | Unsafe_typext ->
1695 print "Module %a defines an unsafe extension constructor, %a ."
1696 | Unsafe_non_function ->
1697 print "Module %a defines an unsafe value, %a ."
1698
1699let report_error loc = function
1700 | Circular_dependency cycle ->
1701 let[@manual.ref "s:recursive-modules"] manual_ref = [ 12; 2 ] in
1702 Location.errorf ~loc ~sub:(List.map explanation_submsg cycle)
1703 "Cannot safely evaluate the definition of the following cycle@ \
1704 of recursively-defined modules:@ %a.@ \
1705 There are no safe modules in this cycle@ %a."
1706 print_cycle cycle Misc.print_see_manual manual_ref
1707 | Conflicting_inline_attributes ->
1708 Location.errorf "@[Conflicting %a attributes@]"
1709 Style.inline_code "inline"
1710
1711let () =
1712 Location.register_error_of_exn
1713 (function
1714 | Error (loc, err) -> Some (report_error loc err)
1715 | _ ->
1716 None
1717 )
1718
1719let reset () =
1720 primitive_declarations := [];
1721 transl_store_subst := Ident.Map.empty;
1722 aliased_idents := Ident.empty;
1723 Env.reset_required_globals ();
1724 Translprim.clear_used_primitives ()