Skip to content

Lazy strengthening #119

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7,874 changes: 3,971 additions & 3,903 deletions boot/menhir/parser.ml

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion lambda/translmod.ml
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,8 @@ let init_shape id modl =
let rec init_shape_mod subid loc env mty =
match Mtype.scrape env mty with
Mty_ident _
| Mty_alias _ ->
| Mty_alias _
| Mty_strengthen _ ->
raise (Initialization_failure
(Unsafe {reason=Unsafe_module_binding;loc;subid}))
| Mty_signature sg ->
Expand Down
2 changes: 2 additions & 0 deletions ocamldoc/odoc_env.ml
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,8 @@ let subst_module_type env t =
Odoc_name.to_path (full_module_type_name env (Odoc_name.from_path p))
in
Mty_ident new_p
| Mty_strengthen (mt,p,a) ->
Mty_strengthen (iter mt,p,a)
| Mty_alias _
| Mty_signature _ ->
t
Expand Down
3 changes: 2 additions & 1 deletion ocamldoc/odoc_print.ml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ let simpl_module_type ?code t =
let rec iter t =
match t with
Mty_ident _
| Mty_alias _ -> t
| Mty_alias _
| Mty_strengthen _ -> t
| Mty_signature _ ->
(
match code with
Expand Down
26 changes: 26 additions & 0 deletions parsing/ast_helper.ml
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,32 @@ module Mty = struct
let with_ ?loc ?attrs a b = mk ?loc ?attrs (Pmty_with (a, b))
let typeof_ ?loc ?attrs a = mk ?loc ?attrs (Pmty_typeof a)
let extension ?loc ?attrs a = mk ?loc ?attrs (Pmty_extension a)

let strengthen mty lid =
let loc = mty.pmty_loc in
let mt =
{ pmty_desc = Pmty_with (mty, [Pwith_module (lid,lid)]);
pmty_attributes = [];
pmty_loc = loc
}
in
let md =
{ pmd_name = Location.mknoloc None;
pmd_type = mt;
pmd_attributes = [];
pmd_loc = loc
}
in
let payload = PSig [{psig_desc = Psig_module md; psig_loc = loc}] in
Pmty_extension (Location.mknoloc "strengthen", payload)

let unstrengthen = function
| {pmty_desc =
Pmty_extension (str, PSig [{psig_desc =
Psig_module {pmd_type = {pmty_desc =
Pmty_with (mty, [Pwith_module (_,lid)])}}}])}
when str.txt = "strengthen" -> Some (mty, lid)
| _ -> None
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these should probably be done via the new stuff in parsing/extensions.ml and parsing/extensions_parsing.ml. Maybe talk to @antalsz or @goldfirere about getting a PR to add support for module types to those first and then rebase and use it?

Looking at how those files work, it looks like we'd use an encoding like:

functor (_ : [%extension.strengthen]) -> S with module L = L

which is maybe a little easier than the encoding here and probably works better with ppxen.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes -- I'd be happy to throw that together.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

functor (_ : [%extension.strengthen]) -> S with module L = L

I'm not doubting you're right but I'd like to understand why this is better. I'm a bit worried that this would replace a signature with a functor type.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a valid worry.

The problem with your original encoding is that ppxen don't generally look inside of extension nodes -- meaning your strengthened module types will be invisible to all ppx transformations, which could be bad.

Instead, the general-purpose extensions architecture is designed to be consulted essentially at every inspection of a parsetree type. The actual encoding I chose (in #141/#142) is functor (_ : [%extensions.strengthen]) (_ : S) -> L. (I decided not to use with module L = L to avoid duplication.) I know the L is a module, not a module type, but the parsetree just wants a Longident.t there, so it doesn't care about the distinction. (Though maybe this is bad, in case a ppx is looking for module identifiers? Feedback welcome, at https://github.com/ocaml-flambda/ocaml-jst/pull/142/files#r1131011021)

As for your worry: we must be careful that every time we look at a module type, we check to see if it's the special form of a syntax extension. I've done this in #141. But the check that I've done it right is just "look to see if it looks right". Because we can't edit the parsetree, we can't really be sure of not making a mistake here. (Indeed, in #140, I found a few places where we might be getting this wrong for some expression extensions.)

end

module Mod = struct
Expand Down
4 changes: 4 additions & 0 deletions parsing/ast_helper.mli
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,10 @@ module Mty:
with_constraint list -> module_type
val typeof_: ?loc:loc -> ?attrs:attrs -> module_expr -> module_type
val extension: ?loc:loc -> ?attrs:attrs -> extension -> module_type

(* Hack to deal with the strengthening syntax (`S with M`) *)
val strengthen: module_type -> lid -> module_type_desc
val unstrengthen: module_type -> (module_type * lid) option
end

(** Module expressions *)
Expand Down
2 changes: 2 additions & 0 deletions parsing/parser.mly
Original file line number Diff line number Diff line change
Expand Up @@ -1754,6 +1754,8 @@ module_type:
{ Pmty_functor(Named (mknoloc None, $1), $3) }
| module_type WITH separated_nonempty_llist(AND, with_constraint)
{ Pmty_with($1, $3) }
| module_type WITH mkrhs(mod_longident)
{ Mty.strengthen $1 $3 }
/* | LPAREN MODULE mkrhs(mod_longident) RPAREN
{ Pmty_alias $3 } */
| extension
Expand Down
8 changes: 4 additions & 4 deletions testsuite/tests/shapes/functors.ml
Original file line number Diff line number Diff line change
Expand Up @@ -223,8 +223,8 @@ module Big_to_small1 : B2S = functor (X : Big) -> X
[%%expect{|
{
"Big_to_small1"[module] ->
Abs<.40>(X/388, {<.39>
"t"[type] -> X/388<.39> . "t"[type];
Abs<.40>(X/386, {<.39>
"t"[type] -> X/386<.39> . "t"[type];
});
}
module Big_to_small1 : B2S
Expand All @@ -234,8 +234,8 @@ module Big_to_small2 : B2S = functor (X : Big) -> struct include X end
[%%expect{|
{
"Big_to_small2"[module] ->
Abs<.42>(X/391, {
"t"[type] -> X/391<.41> . "t"[type];
Abs<.42>(X/389, {
"t"[type] -> X/389<.41> . "t"[type];
});
}
module Big_to_small2 : B2S
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ File "pr7112_bad.ml", line 13, characters 30-31:
13 | module G (X : F(N).S) : A.S = X
^
Error: Signature mismatch:
Modules do not match: F(N).S is not included in A.S
Modules do not match: (F(N).S with X) is not included in A.S
44 changes: 37 additions & 7 deletions testsuite/tests/typing-modules/functors.ml
Original file line number Diff line number Diff line change
Expand Up @@ -543,7 +543,7 @@ module F : functor (X : a) -> sig type t end
Line 6, characters 13-19:
6 | type t = F(X).t
^^^^^^
Error: Modules do not match: a/1 is not included in a/2
Error: Modules do not match: (a/1 with P.X) is not included in a/2
Line 3, characters 2-15:
Definition of module type a/1
Line 1, characters 0-13:
Expand Down Expand Up @@ -1237,15 +1237,19 @@ end
module U = F(PF)(PF)(PF)
[%%expect {|
module F :
functor (X : sig type witness module type t module M : t end) -> X.t
functor (X : sig type witness module type t module M : t end) ->
(X.t with X.M)
module PF :
sig
type witness
module type t =
functor (X : sig type witness module type t module M : t end) -> X.t
functor (X : sig type witness module type t module M : t end) ->
(X.t with X.M)
module M = F
end
module U : PF.t
module U :
functor (X : sig type witness module type t module M : t end) ->
(X.t with X.M)
|}]

module W = F(PF)(PF)(PF)(PF)(PF)(F)
Expand All @@ -1267,7 +1271,7 @@ Error: The functor application is ill-typed.
6. Modules do not match:
F :
functor (X : sig type witness module type t module M : t end) ->
X.t
(X.t with X.M)
is not included in
$T6 = sig type witness module type t module M : t end
Modules do not match:
Expand Down Expand Up @@ -1527,7 +1531,7 @@ Error: Signature mismatch:
sig type wrong end ->
functor (X : sig module type T end) (Res : X.T) (Res :
X.T) (Res : X.T)
-> X.T
-> (X.T with Res)
end
is not included in
sig
Expand Down Expand Up @@ -1657,7 +1661,7 @@ module type Ext = sig module type T module X : T end
module AExt : sig module type T = A module X = A end
module FiveArgsExt :
sig module type T = ty -> ty -> ty -> ty -> ty -> sig end module X : T end
module Bar : functor (W : A) (X : Ext) (Y : B) (Z : Ext) -> Z.T
module Bar : functor (W : A) (X : Ext) (Y : B) (Z : Ext) -> (Z.T with Z.X)
type fine = Bar(A)(FiveArgsExt)(B)(AExt).a
|}]

Expand Down Expand Up @@ -1745,3 +1749,29 @@ module Shape_arg :
module M4 : functor (Arg5 : sig end) -> M3(Arg5).S3
end
|}]


module F (X : sig module type S module M : S end) = struct
module N = X.M
end

module G (X : sig module type S module M : S end) = struct
module O = F(X)
end

module A = struct
module type S = sig type t end
module M = struct type t end
end

module B = G(A)
[%%expect{|
module F :
functor (X : sig module type S module M : S end) ->
sig module N : (X.S with X.M) end
module G :
functor (X : sig module type S module M : S end) ->
sig module O : sig module N : (X.S with X.M) end end
module A : sig module type S = sig type t end module M : sig type t end end
module B : sig module O : sig module N : sig type t = A.M.t end end end
|}]
55 changes: 55 additions & 0 deletions testsuite/tests/typing-modules/strengthening.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
(* TEST
* expect
*)

module type S1 = sig
module type T
module M : T
module N : T with M
end
[%%expect{|
module type S1 = sig module type T module M : T module N : (T with M) end
|}]

module type S2 = sig
module type T
module M : sig end
module N : T with M
end
[%%expect{|
Line 1:
Error: Modules do not match: T is not included in sig end
|}]

module type S3 = sig
module type T = sig type t end
module M : T
module N : T with M
end
[%%expect{|
module type S3 =
sig
module type T = sig type t end
module M : T
module N : sig type t = M.t end
end
|}]

module type S4 = sig
module type T = sig type t end
module M : T
module N : T with M with type t = int
end
[%%expect{|
Line 4, characters 13-39:
4 | module N : T with M with type t = int
^^^^^^^^^^^^^^^^^^^^^^^^^^
Error: In this `with' constraint, the new definition of t
does not match its original definition in the constrained signature:
Type declarations do not match:
type t = int
is not included in
type t = M.t
The type int is not equal to the type M.t
|}]

4 changes: 4 additions & 0 deletions testsuite/tests/warnings/w32.compilers.reference
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,7 @@ File "w32.ml", line 65, characters 18-29:
65 | module G (X : sig val x : int end) = X
^^^^^^^^^^^
Warning 32 [unused-value-declaration]: unused value x.
File "w32.ml", line 76, characters 29-40:
76 | module type S = sig type t val x : int end
^^^^^^^^^^^
Warning 32 [unused-value-declaration]: unused value x.
12 changes: 12 additions & 0 deletions testsuite/tests/warnings/w32.ml
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,15 @@ module H (X : sig val x : int end) = X
module type S = sig
module F: sig val x : int end -> sig end
end

(* Nominal type comparison *)

module Nominal = struct
module type S = sig type t val x : int end

module F(X:S) = struct type t = X.t end
module M : S = struct type t = int let x = 1 end

module N = F(M)
end

3 changes: 3 additions & 0 deletions testsuite/tests/warnings/w32.mli
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,6 @@ module H (X : sig val x : int end) : sig val x : int end
module type S = sig
module F: sig val x : int end -> sig end
end

module Nominal : sig module N : sig type t end end

4 changes: 4 additions & 0 deletions testsuite/tests/warnings/w60.compilers.reference
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,7 @@ File "w60.ml", line 40, characters 13-14:
40 | let module M = struct end in
^
Warning 60 [unused-module]: unused module M.
File "w60.ml", line 48, characters 4-22:
48 | module M : sig end
^^^^^^^^^^^^^^^^^^
Warning 60 [unused-module]: unused module M.
19 changes: 19 additions & 0 deletions testsuite/tests/warnings/w60.ml
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,22 @@ let () =
(* M is unused, but no warning was emitted before 4.10. *)
let module M = struct end in
()


(* Nominal type comparisons *)

module Nominal = struct
module type S = sig
module M : sig end
type t
end

module M : S = struct
module M = struct end
type t = int
end

module F(X:S) = struct type t = X.t end

module N = F(M)
end
3 changes: 3 additions & 0 deletions testsuite/tests/warnings/w60.mli
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@ end

module M : sig end
module O : sig end

module Nominal : sig module N : sig type t end end

4 changes: 2 additions & 2 deletions toplevel/topdirs.ml
Original file line number Diff line number Diff line change
Expand Up @@ -569,7 +569,7 @@ let () =
(if secretly_the_same_path env path new_path
then acc
else def Trec_not :: acc)
| Mty_ident _ | Mty_signature _ | Mty_functor _ ->
| Mty_ident _ | Mty_signature _ | Mty_functor _ | Mty_strengthen _ ->
List.rev (def (is_rec_module id md) :: acc)
in
accum_aliases path md []
Expand All @@ -593,7 +593,7 @@ let () =
(if secretly_the_same_path env path new_path
then acc
else def :: acc)
| None | Some (Mty_alias _ | Mty_signature _ | Mty_functor _) ->
| None | Some (Mty_alias _ | Mty_signature _ | Mty_functor _ | Mty_strengthen _) ->
List.rev (def :: acc)
in
accum_defs path mtd []
Expand Down
3 changes: 3 additions & 0 deletions typing/btype.ml
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,9 @@ let type_iterators =
| Mty_functor (p, mt) ->
it.it_functor_param it p;
it.it_module_type it mt
| Mty_strengthen (mty,p,_) ->
it.it_module_type it mty;
it.it_path p
and it_class_type it = function
Cty_constr (p, tyl, cty) ->
it.it_path p;
Expand Down
2 changes: 1 addition & 1 deletion typing/ctype.ml
Original file line number Diff line number Diff line change
Expand Up @@ -739,7 +739,7 @@ let rec normalize_package_path env p =
in
match t with
| Some (Mty_ident p) -> normalize_package_path env p
| Some (Mty_signature _ | Mty_functor _ | Mty_alias _) | None ->
| Some (Mty_signature _ | Mty_functor _ | Mty_alias _ | Mty_strengthen _ ) | None ->
match p with
Path.Pdot (p1, s) ->
(* For module aliases *)
Expand Down
4 changes: 2 additions & 2 deletions typing/env.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1921,8 +1921,8 @@ let rec components_of_module_maker
fcomp_shape = cm_shape;
fcomp_cache = Hashtbl.create 17;
fcomp_subst_cache = Hashtbl.create 17 })
| MtyL_ident _ -> Error No_components_abstract
| MtyL_alias p -> Error (No_components_alias p)
| MtyL_ident _ | MtyL_strengthen _ -> Error No_components_abstract

(* Insertion of bindings by identifier + path *)

Expand Down Expand Up @@ -2574,7 +2574,7 @@ let read_signature modname filename =
let md = Subst.Lazy.force_module_decl mda.mda_declaration in
match md.md_type with
| Mty_signature sg -> sg
| Mty_ident _ | Mty_functor _ | Mty_alias _ -> assert false
| Mty_ident _ | Mty_functor _ | Mty_alias _ | Mty_strengthen _ -> assert false

let is_identchar_latin1 = function
| 'A'..'Z' | 'a'..'z' | '_' | '\192'..'\214' | '\216'..'\246'
Expand Down
Loading