@@ -277,7 +277,9 @@ let clear_cache (loader : t) : unit =
277277 this function retains the same last-import policy as a defensive fallback
278278 for callers that flatten an already-loaded program directly. Local decls
279279 in [prog.prog_decls] always win over imported ones. *)
280- let rec flatten_imports_from (visiting : string list list ) (loader : t )
280+ let rec flatten_imports_from
281+ (cache : (string list, program) Hashtbl.t )
282+ (visiting : string list list ) (loader : t )
281283 (prog : program ) : program =
282284 (* Local-decl names suppress same-named imports of any kind. *)
283285 let local_name_list =
@@ -318,8 +320,18 @@ let rec flatten_imports_from (visiting : string list list) (loader : t)
318320 undefined `subs`. [visiting] stops an import cycle. *)
319321 let lm =
320322 if List. mem mod_path visiting then lm0
321- else { lm0 with mod_program =
322- flatten_imports_from (mod_path :: visiting) loader lm0.mod_program }
323+ else
324+ (* Memoised per flatten call: a module shared by several import
325+ paths (a diamond) is flattened once, not once per path. *)
326+ let flat = match Hashtbl. find_opt cache mod_path with
327+ | Some p -> p
328+ | None ->
329+ let p = flatten_imports_from cache (mod_path :: visiting) loader
330+ lm0.mod_program in
331+ Hashtbl. replace cache mod_path p;
332+ p
333+ in
334+ { lm0 with mod_program = flat }
323335 in
324336 let public_decls = List. filter_map (fun decl ->
325337 match decl with
@@ -512,4 +524,4 @@ let rec flatten_imports_from (visiting : string list list) (loader : t)
512524(* * Inline the declarations [prog]'s imports need (transitively) into [prog],
513525 for the backends that compile one flattened program. *)
514526let flatten_imports (loader : t ) (prog : program ) : program =
515- flatten_imports_from [] loader prog
527+ flatten_imports_from ( Hashtbl. create 8 ) [] loader prog
0 commit comments