Skip to content

Commit c670199

Browse files
committed
Fix resolution involving deeply nested substitutions
Fixes #690 There has been logic to fix this sort of issue for a long time, but it broke at some point. Mostly the fix is that when calculating the signatures of `With` module expressions, we always recalculate rather than using any previously calculated signature - this is because the previously calculated signature makes a round-trip from Component.* to Lang.* and Lang.ModuleType.simple_signature doesn't preserve removed items as the Components layer does. By itself this would not have fixed #690 though - The way the rest of the fix works is by ensuring that at each level of the expansion we have enough to figure out that a replacement is necessary and what it should be. For example, in issue #690, @Octachron provides the following example: ```ocaml module type S = sig module M: sig type t end type t = M.t end module type T = S with type M.t := int ``` Before this fix, the expansion of `T` would look like this: ```ocaml module M : sig end type t = M.t ``` Clearly the `M.t` can't be resolved from this. Instead, this fix works by ensuring that the expansion of `T` looks like this: ```ocaml module M: sig type t end with type t := int type t = M.t ``` From this we have enough information to replace the `M.t` with an `int` at the point we do the resolution of the RHS of type `t`. We don't really want the output to show `sig type t end with type t := int` however, so in the document layer these expressions are replaced with `sig ... end` as we had before.
1 parent 91f6310 commit c670199

File tree

7 files changed

+83
-34
lines changed

7 files changed

+83
-34
lines changed

src/document/generator.ml

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1430,14 +1430,22 @@ module Make (Syntax : SYNTAX) = struct
14301430
++ Link.from_path (m :> Paths.Path.t)
14311431
++ O.txt " " ++ O.keyword "end"
14321432

1433+
and is_elidable_with_u : Odoc_model.Lang.ModuleType.U.expr -> bool = function
1434+
| Path _ -> false
1435+
| Signature _ -> true
1436+
| With (_, expr) -> is_elidable_with_u expr
1437+
| TypeOf _ -> false
1438+
14331439
and umty : Odoc_model.Lang.ModuleType.U.expr -> text =
14341440
fun m ->
14351441
match m with
14361442
| Path p -> Link.from_path (p :> Paths.Path.t)
1443+
| Signature _ ->
1444+
Syntax.Mod.open_tag ++ O.txt " ... " ++ Syntax.Mod.close_tag
1445+
| With (_, expr) when is_elidable_with_u expr ->
1446+
Syntax.Mod.open_tag ++ O.txt " ... " ++ Syntax.Mod.close_tag
14371447
| With (subs, expr) -> mty_with subs expr
14381448
| TypeOf { t_desc; _ } -> mty_typeof t_desc
1439-
| Signature _ ->
1440-
Syntax.Mod.open_tag ++ O.txt " ... " ++ Syntax.Mod.close_tag
14411449

14421450
and mty : Odoc_model.Lang.ModuleType.expr -> text =
14431451
fun m ->
@@ -1470,6 +1478,8 @@ module Make (Syntax : SYNTAX) = struct
14701478
++ mty arg_expr ++ O.txt ")" ++ O.txt " " ++ Syntax.Type.arrow
14711479
)
14721480
++ O.txt " " ++ mty expr
1481+
| With { w_expr ; _ } when is_elidable_with_u w_expr ->
1482+
Syntax.Mod.open_tag ++ O.txt " ... " ++ Syntax.Mod.close_tag
14731483
| With { w_substitutions; w_expr; _ } -> mty_with w_substitutions w_expr
14741484
| TypeOf { t_desc; _ } -> mty_typeof t_desc
14751485
| Signature _ ->

src/xref2/compile.ml

Lines changed: 6 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -580,20 +580,7 @@ and u_module_type_expr :
580580
| None -> subs
581581
in
582582
let result : ModuleType.U.expr = With (subs', expr') in
583-
match expr' with
584-
| Signature _ -> (
585-
(* Explicitly handle 'sig ... end with ...' - replace with a plain signature.
586-
See equivalent in [module_type_expr] *)
587-
let cu =
588-
Component.Of_Lang.(u_module_type_expr empty (With (subs, expr')))
589-
in
590-
let expansion =
591-
Expand_tools.aux_expansion_of_u_module_type_expr env cu
592-
in
593-
match expansion with
594-
| Ok sg -> Signature Lang_of.(signature id empty sg)
595-
| _ -> result)
596-
| _ -> result)
583+
result)
597584
| TypeOf { t_desc; t_expansion } ->
598585
let t_desc =
599586
match t_desc with
@@ -634,17 +621,11 @@ and module_type_expr :
634621
| With { w_substitutions; w_expansion; w_expr } as e -> (
635622
let w_expansion = get_expansion w_expansion e in
636623
let w_expr = u_module_type_expr env id w_expr in
637-
match (w_expr, w_expansion) with
638-
| Signature _, Some (Signature sg) ->
639-
(* Explicitly handle 'sig ... end with ...' - replace with a plain signature.
640-
See equivalent in [u_module_type_expr] *)
641-
Signature sg
642-
| _, _ -> (
643-
let cexpr = Component.Of_Lang.(u_module_type_expr empty w_expr) in
644-
let subs' = module_type_map_subs env id cexpr w_substitutions in
645-
match subs' with
646-
| None -> With { w_substitutions; w_expansion; w_expr }
647-
| Some s -> With { w_substitutions = s; w_expansion; w_expr }))
624+
let cexpr = Component.Of_Lang.(u_module_type_expr empty w_expr) in
625+
let subs' = module_type_map_subs env id cexpr w_substitutions in
626+
match subs' with
627+
| None -> With { w_substitutions; w_expansion; w_expr }
628+
| Some s -> With { w_substitutions = s; w_expansion; w_expr })
648629
| Functor (param, res) ->
649630
let param' = functor_parameter env param in
650631
let env' = Env.add_functor_parameter param env in

src/xref2/test.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -739,7 +739,7 @@ now we can ask for the signature of this module:
739739
# let sg = get_ok @@ Tools.signature_of_module env (Component.Delayed.get m);;
740740
val sg : Component.Signature.t =
741741
{Odoc_xref2.Component.Signature.items =
742-
[Odoc_xref2.Component.Signature.Module (`LModule (M, 39),
742+
[Odoc_xref2.Component.Signature.Module (`LModule (M, 45),
743743
Odoc_model.Lang.Signature.Ordinary,
744744
{Odoc_xref2.Component.Delayed.v =
745745
Some
@@ -751,7 +751,7 @@ val sg : Component.Signature.t =
751751
None);
752752
canonical = None; hidden = false};
753753
get = None});
754-
Odoc_xref2.Component.Signature.Module (`LModule (N, 40),
754+
Odoc_xref2.Component.Signature.Module (`LModule (N, 46),
755755
Odoc_model.Lang.Signature.Ordinary,
756756
{Odoc_xref2.Component.Delayed.v =
757757
Some
@@ -760,7 +760,7 @@ val sg : Component.Signature.t =
760760
Odoc_xref2.Component.Module.ModuleType
761761
(Odoc_xref2.Component.ModuleType.Path
762762
{Odoc_xref2.Component.ModuleType.p_expansion = None;
763-
p_path = `Dot (`Local (`LModule (M, 39), false), "S")});
763+
p_path = `Dot (`Local (`LModule (M, 45), false), "S")});
764764
canonical = None; hidden = false};
765765
get = None})];
766766
compiled = false; removed = []; doc = []}
@@ -793,7 +793,7 @@ val m : Component.Module.t Component.Delayed.t =
793793
# get_ok @@ Tools.signature_of_module env (Component.Delayed.get m);;
794794
- : Component.Signature.t =
795795
{Odoc_xref2.Component.Signature.items =
796-
[Odoc_xref2.Component.Signature.Type (`LType (t, 47),
796+
[Odoc_xref2.Component.Signature.Type (`LType (t, 53),
797797
Odoc_model.Lang.Signature.Ordinary,
798798
{Odoc_xref2.Component.Delayed.v =
799799
Some

src/xref2/tools.ml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1233,8 +1233,12 @@ and signature_of_module_type_expr :
12331233
| Ok (_, mt) -> signature_of_module_type env mt
12341234
| Error e -> Error (`UnresolvedPath (`ModuleType (p_path, e))))
12351235
| Component.ModuleType.Signature s -> Ok s
1236-
| Component.ModuleType.With { w_expansion = Some e; _ } ->
1236+
(* | Component.ModuleType.With { w_expansion = Some e; _ } ->
12371237
Ok (signature_of_simple_expansion e)
1238+
1239+
Recalculate 'With' expressions always, as we need to know which
1240+
items have been removed
1241+
*)
12381242
| Component.ModuleType.With { w_substitutions; w_expr; _ } ->
12391243
signature_of_u_module_type_expr ~mark_substituted env w_expr >>= fun sg ->
12401244
handle_signature_with_subs ~mark_substituted env sg w_substitutions

test/xref2/deep_substitution.t/m.mli

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module type S = sig
2+
module M: sig
3+
type t
4+
end
5+
type t = M.t
6+
end
7+
module type T = S with type M.t := int
8+

test/xref2/deep_substitution.t/run.t

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
Test to check that when doing a deep substitution inside a signature,
2+
that items that reference that element are still correctly resolved
3+
even if they're in a different path.
4+
5+
$ cat m.mli
6+
module type S = sig
7+
module M: sig
8+
type t
9+
end
10+
type t = M.t
11+
end
12+
module type T = S with type M.t := int
13+
14+
15+
In this, we want to check that the type `t` in module type `T` has
16+
its RHS correctly replaced with an `int`
17+
18+
$ ocamlc -c -bin-annot m.mli
19+
$ odoc compile m.cmti
20+
$ odoc link m.odoc
21+
$ odoc html-generate m.odocl --indent -o .
22+
$ odoc_print m.odocl -r T.t
23+
{
24+
"id": {
25+
"`Type": [
26+
{ "`ModuleType": [ { "`Root": [ "None", "M" ] }, "T" ] },
27+
"t"
28+
]
29+
},
30+
"doc": [],
31+
"equation": {
32+
"params": [],
33+
"private_": "false",
34+
"manifest": {
35+
"Some": {
36+
"Constr": [
37+
{ "`Resolved": { "`Identifier": { "`CoreType": "int" } } },
38+
[]
39+
]
40+
}
41+
},
42+
"constraints": []
43+
},
44+
"representation": "None"
45+
}
46+

test/xref2/with.t/run.t

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,10 @@ Let's check which module type `.content.Module.items[0].ModuleType` refers to:
4444
]
4545
}
4646

47-
And it ought to be a simple Signature after compiling and linking.
47+
And it ought to still be a With after compiling and linking.
4848

4949
$ odoc_print test.odocl | jq '.content.Module.items[0].ModuleType.expr.Some | keys'
5050
[
51-
"Signature"
51+
"With"
5252
]
5353

0 commit comments

Comments
 (0)