···11+# Unreleased
22+33+### Added
44+- Allow persistent latex macros in HTML/KaTeX backend (@dlesbre, #1391)
55+- `markdown-generate` command now accepts multiple `.odocl` files in a single
66+ invocation, eliminating the need for shell scripting (@davesnx, #1387)
77+88+### Fixed
99+- Fix compile-time crashing bugs #930 and #1385 (@jonludlam, #1400)
1010+111# 3.1.0
212313### Added
···616- New arguments to LaTeX generator, --shorten-beyond-depth and
717 --remove-functor-arg-link (@Octachron, #1337)
818- New experimental markdown generator (@davesnx, #1341)
99-- `markdown-generate` command now accepts multiple `.odocl` files in a single
1010- invocation, eliminating the need for shell scripting (@davesnx)
1111-- Allow persistent latex macros in HTML/KaTeX backend (@dlesbre, #1391)
12191320### Changed
1421- Remove cmdliner compatibility layer, no longer needed (@dbuenzli, #1328)
+7-1
src/loader/cmti.ml
···897897 let container = (parent : Identifier.Signature.t :> Identifier.LabelParent.t) in
898898 let doc, status = Doc_attr.attached ~warnings_tag:env.warnings_tag Odoc_model.Semantics.Expect_status container incl.incl_attributes in
899899 let content, shadowed = Cmi.read_signature_noenv env parent (Odoc_model.Compat.signature incl.incl_type) in
900900- let expr = read_module_type env parent container incl.incl_mod in
900900+ (* Use a synthetic parent for the include's module type expression to avoid
901901+ identifier conflicts with items in the enclosing signature. Items inside
902902+ the include expression (like TypeSubstitutions) will get identifiers under
903903+ this synthetic parent, which won't clash with the real parent's items. *)
904904+ let include_parent = Identifier.fresh_include_parent parent in
905905+ let include_container = (include_parent :> Identifier.LabelParent.t) in
906906+ let expr = read_module_type env include_parent include_container incl.incl_mod in
901907 let umty = Odoc_model.Lang.umty_of_mty expr in
902908 let expansion = { content; shadowed; } in
903909#if defined OXCAML
+13
src/model/paths.ml
···644644 `SourceLocationInternal (p, n))
645645 end
646646647647+ (* Counter for generating unique synthetic parents for include expressions.
648648+ Items inside an include's module type expression need a different parent
649649+ to avoid identifier conflicts with items in the enclosing signature. *)
650650+ let include_parent_counter = ref 0
651651+652652+ (* Create a synthetic parent identifier for items inside an include's module
653653+ type expression. Uses a lowercase module name (illegal in normal OCaml)
654654+ to ensure no clashes with real identifiers. *)
655655+ let fresh_include_parent (parent : Signature.t) : Signature.t =
656656+ incr include_parent_counter;
657657+ let name = Printf.sprintf "include%d_" !include_parent_counter in
658658+ (Mk.module_ (parent, ModuleName.make_std name) :> Signature.t)
659659+647660 module Hashtbl = struct
648661 module Any = Hashtbl.Make (Any)
649662 module ContainerPage = Hashtbl.Make (ContainerPage)
+6
src/model/paths.mli
···358358 SourcePage.t * LocalName.t ->
359359 [> `SourceLocationInternal of SourcePage.t * LocalName.t ] id
360360 end
361361+362362+ (** Create a synthetic parent identifier for items inside an include's
363363+ module type expression. Uses a lowercase module name (illegal in normal
364364+ OCaml) to ensure no clashes with real identifiers. Each call returns a
365365+ fresh identifier. *)
366366+ val fresh_include_parent : Signature.t -> Signature.t
361367end
362368363369(** Normal OCaml paths (i.e. the ones present in types) *)
+17-5
src/xref2/compile.ml
···322322 let m' = module_type env mt in
323323 let ty = Component.Of_Lang.(module_type (empty ()) m') in
324324 let env' = Env.add_module_type mt.id ty env in
325325- loop (ModuleType (module_type env mt) :: items) env' rest
325325+ let items' = ModuleType m' :: items in
326326+ loop items' env' rest
326327 | ModuleTypeSubstitution mt ->
327328 let env' = Env.open_module_type_substitution mt env in
328329 loop
···385386 fun env id decl ->
386387 let open Include in
387388 match decl with
388388- | ModuleType expr -> ModuleType (u_module_type_expr env id expr)
389389+ | ModuleType expr ->
390390+ let rec is_elidable_with_u : Odoc_model.Lang.ModuleType.U.expr -> bool =
391391+ function
392392+ | Path _ -> false
393393+ | Signature _ -> true
394394+ | With (_, expr) -> is_elidable_with_u expr
395395+ | TypeOf _ -> false
396396+ in
397397+ if is_elidable_with_u expr then ModuleType expr
398398+ else ModuleType (u_module_type_expr env id expr)
389399 | Alias p -> Alias (module_path env p)
390400391401and module_type : Env.t -> ModuleType.t -> ModuleType.t =
···424434 Strengthen.signature cp sg
425435 | None -> sg
426436 in
427427- let e = Lang_of.(simple_expansion map i.parent (Signature sg')) in
428428-437437+ let sg'' = Tools.apply_inner_substs env sg' in
438438+ let e = Lang_of.(simple_expansion map i.parent (Signature sg'')) in
429439 let expansion_sg =
430440 match e with
431441 | ModuleType.Signature sg -> sg
···434444 in
435445 { i.expansion with content = expansion_sg }
436446 in
437437- let expansion = get_expansion () in
447447+ let expansion =
448448+ if i.expansion.content.compiled then i.expansion else get_expansion ()
449449+ in
438450 let items, env' = signature_items env i.parent expansion.content.items in
439451 let expansion =
440452 {
+10-1
src/xref2/component.ml
···10931093 let pp_sep ppf () = Format.fprintf ppf ", " in
10941094 Format.fprintf ppf "(%a)" (Format.pp_print_list ~pp_sep type_param) ts
1095109510961096- and type_equation c ppf t =
10961096+ and type_equation_manifest c ppf t =
10971097 match t.TypeDecl.Equation.manifest with
10981098 | None -> ()
10991099 | Some m -> Format.fprintf ppf " = %a" (type_expr c) m
11001100+11011101+ and type_equation_params _c ppf t =
11021102+ match t.TypeDecl.Equation.params with
11031103+ | [] -> ()
11041104+ | ps -> Format.fprintf ppf "%a" type_params ps
11051105+11061106+ and type_equation c ppf t =
11071107+ Format.fprintf ppf "(params %a)%a" (type_equation_params c) t
11081108+ (type_equation_manifest c) t
1100110911011110 and exception_ _c _ppf _e = ()
11021111
+4-3
src/xref2/expand_tools.ml
···5050 match t with
5151 | Var (v, _) -> (
5252 try List.assoc v map
5353- with _ ->
5454- Format.eprintf "Failed to list assoc %s\n%!" v;
5555- failwith "bah")
5353+ with Not_found ->
5454+ Format.eprintf "Type variable '%s' not found in map [%s]@." v
5555+ (String.concat ", " (List.map fst map));
5656+ assert false)
5657 | Any -> Any
5758 | Alias (t, s) ->
5859 if List.mem_assoc s map then raise Clash else Alias (type_expr map t, s)
+3-1
src/xref2/lang_of.ml
···651651 (* Don't start shadowing within any signatures *)
652652 match d with
653653 | Alias p -> Alias (Path.module_ map p)
654654- | ModuleType mty -> ModuleType (u_module_type_expr map identifier mty)
654654+ | ModuleType mty ->
655655+ let include_parent = Identifier.fresh_include_parent identifier in
656656+ ModuleType (u_module_type_expr map include_parent mty)
655657656658and include_ parent map i =
657659 let open Component.Include in
+9-2
src/xref2/link.ml
···712712and include_decl : Env.t -> Id.Signature.t -> Include.decl -> Include.decl =
713713 fun env id decl ->
714714 let open Include in
715715+ let rec is_elidable_with_u : Odoc_model.Lang.ModuleType.U.expr -> bool =
716716+ function
717717+ | Path _ -> false
718718+ | Signature _ -> true
719719+ | With (_, expr) -> is_elidable_with_u expr
720720+ | TypeOf _ -> false
721721+ in
715722 match decl with
723723+ | ModuleType expr when is_elidable_with_u expr -> ModuleType expr
716724 | ModuleType expr -> ModuleType (u_module_type_expr env id expr)
717725 | Alias p -> Alias (module_path env p)
718726···11871195 | e ->
11881196 Format.eprintf
11891197 "Caught unexpected exception when expanding type \
11901190- declaration (%s)\n\
11911191- %!"
11981198+ declaration (%s)@."
11921199 (Printexc.to_string e);
11931200 Constr (`Resolved p, ts))
11941201 | _ -> Constr (`Resolved p, ts)
+48
src/xref2/subst.ml
···2929 unresolve_opaque_paths = false;
3030 }
31313232+let pp fmt s =
3333+ let pp_map pp_binding b fmt map =
3434+ let pp_b fmt (id, v) =
3535+ Format.fprintf fmt "%a -> %a" Ident.fmt id pp_binding v
3636+ in
3737+ Format.fprintf fmt "@[<hov 1>{%a}@]" (Format.pp_print_list pp_b) (b map)
3838+ in
3939+ let pp_subst ppp fmt v =
4040+ Format.fprintf fmt "%s"
4141+ (match v with
4242+ | `Prefixed (p, _) -> Format.asprintf "%a" ppp p
4343+ | `Renamed id' -> Format.asprintf "%a" Ident.fmt id'
4444+ | `Substituted -> "<substituted>")
4545+ in
4646+ let pp_type_replacement fmt (te, eq) =
4747+ Format.fprintf fmt "(%a,%a)"
4848+ Component.Fmt.(type_expr default)
4949+ te
5050+ Component.Fmt.(type_equation default)
5151+ eq
5252+ in
5353+5454+ Format.fprintf fmt
5555+ "{ module_ = %a;@ module_type = %a;@ type_ = %a;@ class_type = %a;@ \
5656+ type_replacement = %a;@ module_type_replacement = %a;@ \
5757+ path_invalidating_modules = [%a];@ unresolve_opaque_paths = %b }"
5858+ (pp_map (pp_subst Component.Fmt.(module_path default)) ModuleMap.bindings)
5959+ s.module_
6060+ (pp_map
6161+ (pp_subst Component.Fmt.(module_type_path default))
6262+ ModuleTypeMap.bindings)
6363+ s.module_type
6464+ (pp_map (pp_subst Component.Fmt.(type_path default)) TypeMap.bindings)
6565+ s.type_
6666+ (pp_map (pp_subst Component.Fmt.(class_type_path default)) TypeMap.bindings)
6767+ s.class_type
6868+ (pp_map pp_type_replacement TypeMap.bindings)
6969+ s.type_replacement
7070+ (pp_map Component.Fmt.(module_type_expr default) ModuleTypeMap.bindings)
7171+ s.module_type_replacement
7272+ (Format.pp_print_list Ident.fmt)
7373+ s.path_invalidating_modules s.unresolve_opaque_paths
7474+3275let unresolve_opaque_paths s = { s with unresolve_opaque_paths = true }
33763477let path_invalidate_module id t =
···563606 | Any -> acc
564607 | Var (n, _) -> (n, type_expr s pexpr) :: acc
565608 in
609609+ if List.length ts <> List.length eq.params then (
610610+ Format.eprintf
611611+ "Type substitution error: eq.params length=%d ts length=%d@."
612612+ (List.length eq.params) (List.length ts);
613613+ assert false);
566614 let vars = List.fold_left2 mk_var [] ts eq.params in
567615 substitute_vars vars t
568616 | Not_replaced p -> Constr (p, List.map (type_expr s) ts))
+2
src/xref2/subst.mli
···3344type t = Component.Substitution.t
5566+val pp : Format.formatter -> t -> unit
77+68val identity : t
79810val unresolve_opaque_paths : t -> t
···2350235023512351let resolve_class_type_path env p =
23522352 resolve_class_type env p >>= fun (p, _) -> Ok p
23532353+23542354+let apply_inner_substs env (sg : Component.Signature.t) : Component.Signature.t
23552355+ =
23562356+ let rec inner (items : Component.Signature.item list) :
23572357+ Component.Signature.item list =
23582358+ match items with
23592359+ | Component.Signature.TypeSubstitution (id, typedecl) :: rest -> (
23602360+ let subst =
23612361+ Component.ModuleType.TypeSubst
23622362+ (`Dot (`Root, Ident.Name.type_ id), typedecl.equation)
23632363+ in
23642364+ let rest =
23652365+ Component.Signature.Type
23662366+ (id, Ordinary, Component.Delayed.put (fun () -> typedecl))
23672367+ :: inner rest
23682368+ in
23692369+ match fragmap env subst { sg with items = rest } with
23702370+ | Ok sg' -> sg'.items
23712371+ | Error _ -> failwith "error")
23722372+ | Component.Signature.ModuleSubstitution (id, modsubst) :: rest -> (
23732373+ let subst =
23742374+ Component.ModuleType.ModuleSubst
23752375+ (`Dot (`Root, Ident.Name.module_ id), modsubst.manifest)
23762376+ in
23772377+ let rest =
23782378+ Component.Signature.Module
23792379+ ( id,
23802380+ Ordinary,
23812381+ Component.Delayed.put (fun () ->
23822382+ {
23832383+ Component.Module.source_loc = None;
23842384+ doc = modsubst.doc;
23852385+ type_ = Alias (modsubst.manifest, None);
23862386+ canonical = None;
23872387+ hidden = false;
23882388+ }) )
23892389+ :: inner rest
23902390+ in
23912391+ match fragmap env subst { sg with items = rest } with
23922392+ | Ok sg' -> sg'.items
23932393+ | Error _ -> failwith "error")
23942394+ | Component.Signature.ModuleTypeSubstitution (id, modtypesubst) :: rest -> (
23952395+ let subst =
23962396+ Component.ModuleType.ModuleTypeSubst
23972397+ (`Dot (`Root, Ident.Name.module_type id), modtypesubst.manifest)
23982398+ in
23992399+ let rest =
24002400+ Component.Signature.ModuleType
24012401+ ( id,
24022402+ Component.Delayed.put (fun () ->
24032403+ {
24042404+ Component.ModuleType.source_loc = None;
24052405+ doc = modtypesubst.doc;
24062406+ expr = Some modtypesubst.manifest;
24072407+ canonical = None;
24082408+ }) )
24092409+ :: inner rest
24102410+ in
24112411+ match fragmap env subst { sg with items = rest } with
24122412+ | Ok sg' -> sg'.items
24132413+ | Error _ -> failwith "error")
24142414+ | x :: rest -> x :: inner rest
24152415+ | [] -> []
24162416+ in
24172417+ { sg with items = inner sg.items }
+2
src/xref2/tools.mli
···337337338338val disable_all_caches : unit -> unit
339339(** Disable the caches completely *)
340340+341341+val apply_inner_substs : Env.t -> Component.Signature.t -> Component.Signature.t
+2-3
test/sources/functor.t/run.t
···6060 <a href="../../src/a.ml.html#module-F.val-y" class="source_link">Source
6161 </a>
62626363- $ cat html/root/source/a.ml.html | grep L3
6464- cat: html/root/source/a.ml.html: No such file or directory
6565- [1]
6363+ $ cat html/src/a.ml.html | grep L3
6464+ <a id="L3" class="source_line" href="#L3">3</a>
66656766However, on functor results, there is a link to source in the file:
6867
+6-3
test/xref2/expansion.t/run.t
···2020 module S :
2121 sig
2222 module type X = sig module M : sig type t end end
2323- module X : X with M.t = int (sig : module M : sig type t = int end end)
2323+ module X : X with M.t(params ) = int
2424+ (sig : module M : sig type t = int end end)
2425 module Y : sig type t end
2526 module Z : module type of Y (sig : type t end)
2627 module A : X (sig : module M : sig type t end end)
···3334 module S :
3435 sig
3536 module type X = sig module M : sig type t end end
3636- module X : X with M.t = int (sig : module M : sig type t = int end end)
3737+ module X : X with M.t(params ) = int
3838+ (sig : module M : sig type t = int end end)
3739 module Y : sig type t end
3840 module Z : module type of Y (sig : type t end)
3941 module A : X (sig : module M : sig type t end end)
···4244 module Test = S
4345 (sig :
4446 module type X = sig module M : sig type t end end
4545- module X : X with M.t = int (sig : module M : sig type t = int end end)
4747+ module X : X with M.t(params ) = int
4848+ (sig : module M : sig type t = int end end)
4649 module Y : sig type t end
4750 module Z : module type of Y (sig : type t end)
4851 module A : X (sig : module M : sig type t end end)
+61
test/xref2/github_issue_930.t/edge_cases.mli
···11+(** Edge case tests for GitHub issue #930 *)
22+33+(** Multiple TypeSubstitutions in same signature *)
44+module type Multi_subst = sig
55+ type 'a t := unit
66+ type 'b u := int
77+88+ val f : bool t
99+ val g : string u
1010+end
1111+1212+(** Deeply nested includes - 5 levels deep *)
1313+module type Level1 = sig
1414+ type 'a t := unit
1515+ val x : int t
1616+end
1717+1818+module type Level2 = sig
1919+ type t
2020+ include Level1
2121+end
2222+2323+module type Level3 = sig
2424+ include Level2
2525+end
2626+2727+module type Level4 = sig
2828+ include Level3
2929+end
3030+3131+module type Level5 = sig
3232+ include Level4
3333+end
3434+3535+(** Multiple paths to same signature *)
3636+module type Multipath_base = sig
3737+ type 'a t := unit
3838+ val x : int t
3939+end
4040+4141+module type Multipath_via_a = sig
4242+ include Multipath_base
4343+end
4444+4545+module type Multipath_via_b = sig
4646+ include Multipath_base
4747+end
4848+4949+module type Multipath_use1 = sig
5050+ include Multipath_via_a
5151+end
5252+5353+module type Multipath_use2 = sig
5454+ include Multipath_via_b
5555+end
5656+5757+(** TypeSubstitution with record field types *)
5858+module type With_record = sig
5959+ type 'a t := unit
6060+ type r = { field : int t }
6161+end
+96
test/xref2/github_issue_930.t/run.t
···11+Test for GitHub issue #930: Crash when substituting for the same name at different arities.
22+33+This tests that inline TypeSubstitution items (type 'a t := unit) are correctly
44+applied during include expansion, preventing crashes and producing correct output.
55+66+ $ ocamlc -c -bin-annot test.mli
77+ $ ocamlc -c -bin-annot edge_cases.mli
88+99+Compile and link both test files:
1010+1111+ $ odoc compile test.cmti
1212+ $ odoc compile edge_cases.cmti
1313+ $ odoc link test.odoc
1414+ $ odoc link edge_cases.odoc
1515+1616+=== Test 1: Original MWE from issue #930 ===
1717+1818+The key test is that odoc doesn't crash with "Invalid_argument(List.fold_left2)".
1919+Check that includes work correctly - the TypeSubstitution is applied when
2020+S1 is included in S2 and S2 is included in S3:
2121+2222+ $ odoc_print test.odocl -r S2.x | jq -c '.type_.Constr[0]'
2323+ {"`Resolved":{"`CoreType":"unit"}}
2424+2525+ $ odoc_print test.odocl -r S3.x | jq -c '.type_.Constr[0]'
2626+ {"`Resolved":{"`CoreType":"unit"}}
2727+2828+=== Test 2: Issue #1385 - Creators_base with nested types ===
2929+3030+Check that S0_with_creators_base compiles without crashing and has the
3131+concat function with simplified types (t -> t):
3232+3333+ $ odoc_print test.odocl -r S0_with_creators_base.concat | jq -c '.type_.Arrow[1].Constr[0]'
3434+ {"`Resolved":{"`Identifier":{"`Type":[{"`ModuleType":[{"`Root":["None","Test"]},"S0_with_creators_base"]},"t"]}}}
3535+3636+ $ odoc_print test.odocl -r S0_with_creators_base.concat | jq -c '.type_.Arrow[2].Constr[0]'
3737+ {"`Resolved":{"`Identifier":{"`Type":[{"`ModuleType":[{"`Root":["None","Test"]},"S0_with_creators_base"]},"t"]}}}
3838+3939+=== Test 3: Deeply nested includes ===
4040+4141+Level5 goes through 5 levels of includes with TypeSubstitution at Level1.
4242+The substitution should be applied correctly through all levels:
4343+4444+ $ odoc_print edge_cases.odocl -r Level5.x | jq -c '.type_.Constr[0]'
4545+ {"`Resolved":{"`CoreType":"unit"}}
4646+4747+=== Test 4: Multiple paths to same signature ===
4848+4949+Multipath_use1 and Multipath_use2 both include variations of Multipath_base:
5050+5151+ $ odoc_print edge_cases.odocl -r Multipath_use1.x | jq -c '.type_.Constr[0]'
5252+ {"`Resolved":{"`CoreType":"unit"}}
5353+5454+ $ odoc_print edge_cases.odocl -r Multipath_use2.x | jq -c '.type_.Constr[0]'
5555+ {"`Resolved":{"`CoreType":"unit"}}
5656+5757+=== Test 5: Verify HTML generation succeeds and shows correct types ===
5858+5959+ $ odoc html-generate test.odocl -o html --indent
6060+ $ odoc html-generate edge_cases.odocl -o html --indent
6161+6262+S2.x should show "unit" (TypeSubstitution applied through include of S1):
6363+6464+ $ grep "val.*x" html/Test/module-type-S2/index.html | sed 's/<[^>]*>//g' | grep -o "val x.*" | head -1
6565+ val x : unit
6666+6767+S3.x should show "unit" (TypeSubstitution applied through include of S2):
6868+6969+ $ grep "val.*x" html/Test/module-type-S3/index.html | sed 's/<[^>]*>//g' | grep -o "val x.*" | head -1
7070+ val x : unit
7171+7272+Level5.x should show "unit" after 5 levels of nested includes:
7373+7474+ $ grep "val.*x" html/Edge_cases/module-type-Level5/index.html | sed 's/<[^>]*>//g' | grep -o "val x.*" | head -1
7575+ val x : unit
7676+7777+=== Test 6: Verify TypeSubstitutions in includes are correctly applied ===
7878+7979+In S2 and S3, the TypeSubstitution from S1 should be applied (not shown as "type subst"):
8080+8181+ $ grep -c "type subst" html/Test/module-type-S2/index.html 2>/dev/null || true
8282+ 0
8383+8484+ $ grep -c "type subst" html/Test/module-type-S3/index.html 2>/dev/null || true
8585+ 0
8686+8787+Level5 should have no TypeSubstitution visible (applied through nested includes):
8888+8989+ $ grep -c "type subst" html/Edge_cases/module-type-Level5/index.html 2>/dev/null || true
9090+ 0
9191+9292+Note: S1 still shows its TypeSubstitution declaration because that's where it's defined.
9393+This is expected - the substitution is only applied when the signature is included:
9494+9595+ $ grep -c "type subst" html/Test/module-type-S1/index.html
9696+ 1
+59
test/xref2/github_issue_930.t/test.mli
···11+(** Test for GitHub issue #930: Crash when substituting for the same name at
22+ different arities.
33+44+ The original issue was that odoc crashed with [Invalid_argument("List.fold_left2")]
55+ when a signature had a destructive type substitution (type 'a t := unit) followed
66+ by an include that re-introduces a type with the same name but different arity.
77+*)
88+99+(** Original MWE from issue #930 *)
1010+module type S1 = sig
1111+ type t0
1212+1313+ type 'a t := unit
1414+ (** Destructive substitution - 'a t becomes unit *)
1515+1616+ val x : t0 t
1717+end
1818+1919+module type S2 = sig
2020+ type t
2121+ (** This [t] has arity 0, different from S1's ['a t] *)
2222+2323+ include S1 with type t0 := t
2424+end
2525+2626+module type S3 = sig
2727+ type t1
2828+2929+ include S2 with type t := t1
3030+end
3131+3232+(** Updated MWE from issue #930 - simpler reproduction *)
3333+module type Simple = sig
3434+ type 'a t := unit
3535+3636+ include sig
3737+ type 'a t
3838+3939+ val f : int t
4040+ end
4141+ with type 'a t := 'a t
4242+end
4343+4444+(** Issue #1385 - related case with nested includes *)
4545+module type Creators_base = sig
4646+ type ('a, 'b, 'c) t
4747+ type ('a, 'b, 'c) concat
4848+4949+ include sig
5050+ type ('a, 'b, 'c) t
5151+ val concat : (('a, 'p1, 'p2) t, 'p1, 'p2) concat -> ('a, 'p1, 'p2) t
5252+ end
5353+ with type ('a, 'b, 'c) t := ('a, 'b, 'c) t
5454+end
5555+5656+module type S0_with_creators_base = sig
5757+ type t
5858+ include Creators_base with type ('a, _, _) t := t and type ('a, _, _) concat := t
5959+end
+52
test/xref2/include_self_reference.t/run.t
···11+Test for include with type substitution referencing outer type.
22+33+When we have:
44+ $ cat test.mli
55+ (** Test case for include with type substitution referencing outer type.
66+77+ When the include's signature has a TypeSubstitution like [type reader := reader],
88+ the RHS [reader] refers to the outer type, not the substitution itself.
99+ This should not create a self-referential TypeSubstitution. *)
1010+1111+ module type Read = sig
1212+ type reader = unit
1313+1414+ include sig
1515+ type reader := reader
1616+1717+ val bin_read_unit : reader
1818+ end
1919+ end
2020+2121+The TypeSubstitution [type reader := reader] should substitute the inner
2222+[reader] with the outer [reader] type. The RHS [reader] refers to the outer
2323+type (type reader = unit), not the substitution itself.
2424+2525+Previously, items inside the include's signature expression were getting
2626+identifiers with the same parent as siblings of the include statement.
2727+This caused the TypeSubstitution's identifier to equal the outer type's
2828+identifier, making the manifest appear as a self-reference.
2929+3030+ $ ocamlc -c -bin-annot test.mli
3131+3232+Compile and link should succeed without errors:
3333+3434+ $ odoc compile test.cmti
3535+ $ odoc link test.odoc
3636+3737+The resulting signature should have:
3838+- type reader = unit
3939+- val bin_read_unit : reader (where reader refers to the type above)
4040+4141+Check that bin_read_unit has type reader (resolved to the outer type):
4242+4343+ $ odoc_print test.odocl -r Read.bin_read_unit | jq -c '.type_.Constr[0]'
4444+ {"`Resolved":{"`Identifier":{"`Type":[{"`ModuleType":[{"`Root":["None","Test"]},"Read"]},"reader"]}}}
4545+4646+Generate HTML and verify the output is correct:
4747+4848+ $ odoc html-generate test.odocl -o html --indent
4949+5050+The module type Read should show both the type and the value:
5151+5252+ $ grep -E "type reader|val bin_read_unit" html/Test/module-type-Read/index.html | sed 's/<[^>]*>//g' | sed 's/^[[:space:]]*//'
+15
test/xref2/include_self_reference.t/test.mli
···11+(** Test case for include with type substitution referencing outer type.
22+33+ When the include's signature has a TypeSubstitution like [type reader := reader],
44+ the RHS [reader] refers to the outer type, not the substitution itself.
55+ This should not create a self-referential TypeSubstitution. *)
66+77+module type Read = sig
88+ type reader = unit
99+1010+ include sig
1111+ type reader := reader
1212+1313+ val bin_read_unit : reader
1414+ end
1515+end
+15-29
test/xref2/shadow3.t/run.t
···1818 module type {B}1/shadowed/(CCCC) = A.B
1919 include {B}1/shadowed/(CCCC)
2020 (sig : module {A}1/shadowed/(AAAA) = A.A end)
2121- module type B1 :=
2222- sig
2323- module A :
2424- sig
2525- include module type of struct include {A}1/shadowed/(AAAA) end
2626- (sig :
2727- include module type of struct include A.{A}1/shadowed/(AAAA) end
2828- (sig : type t = {A}1/shadowed/(AAAA).t end)
2929- type a = A.A.a
3030- end)
3131- type a
3232- end
3333- end
3434- include B1 (sig : module {A}2/shadowed/(CCCC) = A.A end)
2121+ include sig
2222+ module A :
2323+ sig
2424+ include module type of struct include {A}1/shadowed/(AAAA) end
2525+ (sig : type t end)
2626+ type a
2727+ endend (sig : module {A}2/shadowed/(CCCC) = A.A end)
3528 end)
3629 include module type of struct include B end
3730 (sig :
3831 module type B = B.B
3932 include B (sig : module {A}1/shadowed/(BBBB) = B.A end)
4040- module type B1 :=
4141- sig
4242- module A :
4343- sig
4444- include module type of struct include {A}1/shadowed/(BBBB) end
4545- (sig :
4646- include module type of struct include B.{A}1/shadowed/(BBBB) end
4747- (sig : type t = {A}1/shadowed/(BBBB).t end)
4848- type b = B.A.b
4949- end)
5050- type b
5151- end
5252- end
5353- include B1 (sig : module {A}3/shadowed/(CCCC) = B.A end)
3333+ include sig
3434+ module A :
3535+ sig
3636+ include module type of struct include {A}1/shadowed/(BBBB) end
3737+ (sig : type t end)
3838+ type b
3939+ endend (sig : module {A}3/shadowed/(CCCC) = B.A end)
5440 end)
5541 module A :
5642 sig
5743 include module type of struct include {A}3/shadowed/(CCCC) end
5844 (sig :
5945 include module type of struct include B.{A}1/shadowed/(BBBB) end
6060- (sig : type t = {A}3/shadowed/(CCCC).t end)
4646+ (sig : type t = B.A.t end)
6147 type b = B.A.b
6248 end)
6349 end
+11-5
test/xref2/shadow5.t/run.t
···3333 type t = int
3434 val y : t
3535 include sigtype t = t
3636- val z : tend with [t = t] (sig : val z : t end)
3636+ val z : tend with [t(params ) = t] (sig : val z : t end)
3737 end
3838 module type Z =
3939 sig
···4141 (sig :
4242 type {t}1/shadowed/(AAAA) = int
4343 val y : int
4444- include sigtype t = t
4545- val z : tend with [t = int] (sig : val z : int end)
4444+ include sig
4545+ type t = {t}1/shadowed/(AAAA)
4646+ val z : tend with [t(params ) = {t}1/shadowed/(AAAA)]
4747+ (sig : val z : int end)
4648 end)
4749 type t = int
4850 end
···7678 $ odoc_print b.odocl --short --show-include-expansions
7779 module type X = sig type t val z : t end
7880 module type Y =
7979- sig type t = int val y : t include X with [t = t] (sig : val z : t end) end
8181+ sig
8282+ type t = int
8383+ val y : t
8484+ include X with [t(params ) = t] (sig : val z : t end)
8585+ end
8086 module type Z =
8187 sig
8288 include Y
8389 (sig :
8490 type {t}1/shadowed/(BBBB) = int
8591 val y : int
8686- include X with [t = int] (sig : val z : int end)
9292+ include X with [t(params ) = int] (sig : val z : int end)
8793 end)
8894 type t = int
8995 end
+5-5
test/xref2/subst/test.md
···131131module SomeMonad/20 :
132132 sig
133133 type t/25
134134- include r(Monad/21) with [resolved(root(Monad/21).t) = [a] resolved(t/25)]
134134+ include r(Monad/21) with [resolved(root(Monad/21).t)(params (a)) = [a] resolved(t/25)]
135135 (sig :
136136 val map/26 : ([a] resolved(t/25)) -> ((a) -> b) -> [b] resolved(t/25)
137137 val join/27 : ([[a] resolved(t/25)] resolved(t/25)) -> [a] resolved(t/25)
···141141module ComplexTypeExpr/19 :
142142 sig
143143 type t/28
144144- include r(Monad/21) with [resolved(root(Monad/21).t) = ([resolved(int) * a] resolved(t/28) * [a * resolved(int)] resolved(t/28))]
144144+ include r(Monad/21) with [resolved(root(Monad/21).t)(params (a)) = ([resolved(int) * a] resolved(t/28) * [a * resolved(int)] resolved(t/28))]
145145 (sig :
146146 val map/29 : (([resolved(int) * a] resolved(t/28) * [a * resolved(int)] resolved(t/28))) -> ((a) -> b) -> ([resolved(int) * b] resolved(t/28) * [b * resolved(int)] resolved(t/28))
147147 val join/30 : (([resolved(int) * ([resolved(int) * a] resolved(t/28) * [a * resolved(int)] resolved(t/28))] resolved(t/28) * [([resolved(int) * a] resolved(t/28) * [a * resolved(int)] resolved(t/28)) * resolved(int)] resolved(t/28))) -> ([resolved(int) * a] resolved(t/28) * [a * resolved(int)] resolved(t/28))
···150150 end (canonical=None)
151151module Erase/18 :
152152 sig
153153- include r(Monad/21) with [resolved(root(Monad/21).t) = a]
153153+ include r(Monad/21) with [resolved(root(Monad/21).t)(params (a)) = a]
154154 (sig :
155155 val map/31 : (a) -> ((a) -> b) -> b
156156 val join/32 : (a) -> a
···186186module SwappedVars/44 :
187187 sig
188188 type t/50
189189- include r(Monad_2/45) with [resolved(root(Monad_2/45).t) = [b * a] resolved(t/50)]
189189+ include r(Monad_2/45) with [resolved(root(Monad_2/45).t)(params (a, b)) = [b * a] resolved(t/50)]
190190 (sig :
191191 val map/51 : ([err * a] resolved(t/50)) -> f:((a) -> b) -> [err * b] resolved(t/50)
192192 val join/52 : ([e * [e * a] resolved(t/50)] resolved(t/50)) -> [e * a] resolved(t/50)
···219219module M/59 :
220220 sig
221221 type t/63
222222- include r(S/60) with [resolved(root(S/60).t) = [(alias (poly_var [ `A of (a * b) ]) b)] resolved(t/63)]
222222+ include r(S/60) with [resolved(root(S/60).t)(params (a)) = [(alias (poly_var [ `A of (a * b) ]) b)] resolved(t/63)]
223223 (sig :
224224 val map/64 : ([(alias (poly_var [ `A of (a * b) ]) b)] resolved(t/63)) -> ((a) -> b) -> [(alias (poly_var [ `A of (b * b) ]) b)] resolved(t/63)
225225 (removed=type (a) t = ([(alias (poly_var [ `A of (a * b) ]) b)] local(t/63,false)))