this repo has no description
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix(odoc): strip inline include decls to reduce memory usage 5.7x

Add an `expanded` flag to includes that marks them as "already derived
by odoc". After compilation, all includes are marked expanded=true,
which prevents redundant re-derivation from the decl in dependent
modules. Inline Signature include decls are stripped to empty signatures
as a size optimization.

For `base.odoc` link, this reduces peak RSS from 25.1GB to 4.4GB (5.7x)
with no change to HTML output.

Imported from /cache/jons-agent/oxmono commits f7711a7e6..47185ae05.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

+197 -6
+1 -1
src/loader/cmt.cppo.ml
··· 611 611 match decl_modty with 612 612 | Some m -> 613 613 let decl = ModuleType m in 614 - [Include {parent; doc; decl; expansion; status; strengthened=None; loc }] 614 + [Include {parent; doc; decl; expansion; expanded = false; status; strengthened=None; loc }] 615 615 | _ -> 616 616 content.items 617 617
+1 -1
src/loader/cmti.cppo.ml
··· 914 914 | Some uexpr -> 915 915 #endif 916 916 let decl = Include.ModuleType uexpr in 917 - [Include {parent; doc; decl; expansion; status; strengthened=None; loc }] 917 + [Include {parent; doc; decl; expansion; expanded = false; status; strengthened=None; loc }] 918 918 | _ -> 919 919 (* TODO: Handle [include functor] *) 920 920 content.items
+1
src/model/lang.ml
··· 217 217 status : [ `Inline | `Closed | `Open | `Default ]; 218 218 decl : decl; 219 219 expansion : expansion; 220 + expanded : bool; 220 221 } 221 222 end = 222 223 Include
+16 -2
src/xref2/compile.ml
··· 446 446 { i.expansion with content = expansion_sg } 447 447 in 448 448 let expansion = 449 - if i.expansion.content.compiled then i.expansion else get_expansion () 449 + if i.expanded then i.expansion 450 + else get_expansion () 450 451 in 451 452 let items, env' = signature_items env i.parent expansion.content.items in 452 453 let expansion = ··· 455 456 content = { expansion.content with items; compiled = true }; 456 457 } 457 458 in 458 - ({ i with decl = include_decl env i.parent i.decl; expansion }, env') 459 + let decl = include_decl env i.parent i.decl in 460 + (* After compilation, expanded=true marks includes as "already 461 + derived by odoc" — the expansion is authoritative without 462 + re-derivation from the decl. Inline Signature decls are stripped 463 + as a size optimization since the expansion is shown inline. *) 464 + let stripped, decl = 465 + match decl with 466 + | Include.ModuleType (Signature _) -> 467 + true, 468 + Include.ModuleType (Signature 469 + { items = []; compiled = true; removed = []; doc = i.doc }) 470 + | _ -> false, decl 471 + in 472 + ({ i with decl; expansion; expanded = true }, env') 459 473 460 474 and simple_expansion : 461 475 Env.t ->
+2
src/xref2/component.ml
··· 368 368 status : [ `Default | `Inline | `Closed | `Open ]; 369 369 shadowed : Odoc_model.Lang.Include.shadowed; 370 370 expansion_ : Signature.t; 371 + expanded : bool; 371 372 decl : decl; 372 373 loc : Odoc_model.Location_.span; 373 374 } ··· 2607 2608 doc = docs ident_map i.doc; 2608 2609 shadowed = i.expansion.shadowed; 2609 2610 expansion_ = apply_sig_map ident_map i.expansion.content; 2611 + expanded = i.expanded; 2610 2612 status = i.status; 2611 2613 strengthened = option module_path ident_map i.strengthened; 2612 2614 decl;
+1
src/xref2/component.mli
··· 340 340 status : [ `Default | `Inline | `Closed | `Open ]; 341 341 shadowed : Odoc_model.Lang.Include.shadowed; 342 342 expansion_ : Signature.t; 343 + expanded : bool; 343 344 decl : decl; 344 345 loc : Odoc_model.Location_.span; 345 346 }
+1
src/xref2/lang_of.ml
··· 667 667 shadowed; 668 668 content = signature parent { map with shadowed } i.expansion_; 669 669 }; 670 + expanded = i.expanded; 670 671 status = i.status; 671 672 strengthened = Opt.map (Path.module_ map) i.strengthened; 672 673 loc = i.loc;
+14 -2
src/xref2/tools.ml
··· 1862 1862 >>= fun (items', handled', subbed_modules', removed') -> 1863 1863 let component = 1864 1864 if handled' then 1865 - map_include_decl i.decl sub >>= fun decl -> 1865 + let is_stripped = 1866 + match i.decl with 1867 + | Component.Include.ModuleType 1868 + (Signature { items = []; _ }) -> true 1869 + | _ -> false 1870 + in 1871 + let decl_result = 1872 + if is_stripped then Ok i.decl 1873 + else map_include_decl i.decl sub 1874 + in 1875 + decl_result >>= fun decl -> 1866 1876 let expansion_ = 1867 1877 Component.Signature. 1868 1878 { ··· 1874 1884 in 1875 1885 Ok 1876 1886 (Component.Signature.Include 1877 - { i with decl; expansion_; strengthened = None }) 1887 + { i with decl; expansion_; 1888 + expanded = i.expanded; 1889 + strengthened = None }) 1878 1890 else Ok item 1879 1891 in 1880 1892 component >>= fun c ->
+10
test/xref2/stripped_include_decl_reconstruct.t/a.mli
··· 1 + (** Module A defines a signature with an inline include. *) 2 + 3 + module type S = sig 4 + type t 5 + 6 + include sig 7 + val x : t 8 + val y : t -> t 9 + end 10 + end
+9
test/xref2/stripped_include_decl_reconstruct.t/b.mli
··· 1 + (** Module B includes A.S with a type substitution. 2 + 3 + The substitution flows through [fragmap] in tools.ml. When the inline 4 + include's decl has been stripped, [map_include_decl] needs the 5 + reconstructed decl to correctly wrap the substitution. Without 6 + reconstruction, the empty decl produces [With(subst, empty_sig)] 7 + which loses the vals from the include. *) 8 + 9 + module M : A.S with type t := int
+51
test/xref2/stripped_include_decl_reconstruct.t/run.t
··· 1 + When an inline include's decl is stripped (replaced with an empty 2 + signature to save space), fragmap in tools.ml must reconstruct the 3 + decl from expansion_ before wrapping it with type substitutions. 4 + 5 + Without reconstruction, the substitution wraps an empty signature, 6 + losing all vals from the include. 7 + 8 + Set up: 9 + 10 + $ cat a.mli 11 + (** Module A defines a signature with an inline include. *) 12 + 13 + module type S = sig 14 + type t 15 + 16 + include sig 17 + val x : t 18 + val y : t -> t 19 + end 20 + end 21 + 22 + $ cat b.mli 23 + (** Module B includes A.S with a type substitution. 24 + 25 + The substitution flows through [fragmap] in tools.ml. When the inline 26 + include's decl has been stripped, [map_include_decl] needs the 27 + reconstructed decl to correctly wrap the substitution. Without 28 + reconstruction, the empty decl produces [With(subst, empty_sig)] 29 + which loses the vals from the include. *) 30 + 31 + module M : A.S with type t := int 32 + 33 + Compile and link: 34 + 35 + $ compile a.mli b.mli 36 + 37 + The module M in B should have both vals from the include, with 38 + type t substituted for int: 39 + 40 + $ odoc_print b.odocl -r M.x | jq -c '.type_' 41 + {"Constr":[{"`Resolved":{"`CoreType":"int"}},[]]} 42 + 43 + $ odoc_print b.odocl -r M.y | jq -c '.type_' 44 + {"Arrow":[["None",{"Constr":[{"`Resolved":{"`CoreType":"int"}},[]]}],[{"Constr":[{"`Resolved":{"`CoreType":"int"}},[]]},[[],[]]]]} 45 + 46 + Generate HTML and verify both vals appear: 47 + 48 + $ odoc html-generate b.odocl -o html --indent 49 + $ grep 'id="val-[xy]"' html/test/B/M/index.html 50 + <div class="spec value anchored" id="val-x"> 51 + <div class="spec value anchored" id="val-y">
+18
test/xref2/stripped_include_reexpansion.t/lib.mli
··· 1 + module type Pretty = sig 2 + type output 3 + val pp : int -> output 4 + val pp_hum : int -> output 5 + end 6 + 7 + module type Pretty_helpers = sig 8 + include Pretty with type output := string 9 + val default_indent : int 10 + end 11 + 12 + include Pretty_helpers 13 + 14 + module Make (X : Pretty) : Pretty_helpers 15 + 16 + module Private : sig 17 + val internal : int 18 + end
+3
test/xref2/stripped_include_reexpansion.t/mid.mli
··· 1 + include 2 + module type of Lib 3 + with module Private := Lib.Private
+65
test/xref2/stripped_include_reexpansion.t/run.t
··· 1 + After compilation, odoc sets expanded=true on all includes to mark 2 + them as "already derived". This prevents re-derivation from the decl 3 + in dependent modules. The fragmap in tools.ml preserves this flag, 4 + ensuring expansions remain authoritative through strengthening. 5 + 6 + The pattern that exercises this: lib.mli defines module types and a 7 + destructible module (Private). mid.mli includes lib with a destructive 8 + module substitution (module Private :=). top.mli includes mid with 9 + @inline, which strengthens mid's signature. 10 + 11 + $ cat lib.mli 12 + module type Pretty = sig 13 + type output 14 + val pp : int -> output 15 + val pp_hum : int -> output 16 + end 17 + 18 + module type Pretty_helpers = sig 19 + include Pretty with type output := string 20 + val default_indent : int 21 + end 22 + 23 + include Pretty_helpers 24 + 25 + module Make (X : Pretty) : Pretty_helpers 26 + 27 + module Private : sig 28 + val internal : int 29 + end 30 + 31 + 32 + 33 + 34 + 35 + 36 + 37 + 38 + 39 + $ cat mid.mli 40 + include 41 + module type of Lib 42 + with module Private := Lib.Private 43 + 44 + $ cat top.mli 45 + (** @inline *) 46 + include module type of struct 47 + include Mid 48 + end 49 + 50 + Compile and link: 51 + 52 + $ compile lib.mli mid.mli top.mli 53 + 54 + Generate HTML for Top: 55 + 56 + $ odoc html-generate top.odocl -o html --indent 57 + 58 + Top's module type declarations should reference Mid (the source module 59 + via strengthening). Nested includes within the expansion use Top's own 60 + module types (Top.Pretty, Top.Pretty_helpers) since expanded=true 61 + skips re-derivation and paths resolve in Top's context. 62 + 63 + $ grep 'Mid\.Pretty' html/test/Top/index.html 64 + <a href="../Mid/module-type-Pretty/index.html">Mid.Pretty</a> 65 + Mid.Pretty_helpers
+4
test/xref2/stripped_include_reexpansion.t/top.mli
··· 1 + (** @inline *) 2 + include module type of struct 3 + include Mid 4 + end