(* TEST
 expect;
*)

module type Printable = sig
  type t
  val print : t -> unit
end
[%%expect {|
module type Printable = sig type t val print : t -> unit end
|}]
module type Comparable = sig
  type t
  val compare : t -> t -> int
end
[%%expect {|
module type Comparable = sig type t val compare : t -> t -> int end
|}]
module type PrintableComparable = sig
  include Printable
  include Comparable with type t = t
end
[%%expect {|
Line 3, characters 2-36:
3 |   include Comparable with type t = t
      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error: Illegal shadowing of included type "t/2" by "t".
Line 2, characters 2-19:
2 |   include Printable
      ^^^^^^^^^^^^^^^^^
  Type "t/2" came from this include.
Line 3, characters 2-23:
3 |   val print : t -> unit
      ^^^^^^^^^^^^^^^^^^^^^
  The value "print" has no valid type if "t/2" is shadowed.
|}]

module type Sunderscore = sig
  type (_, _) t
end with type (_, 'a) t = int * 'a
[%%expect {|
module type Sunderscore = sig type (_, 'a) t = int * 'a end
|}]


(* Valid substitutions in a recursive module used to fail
   due to the ordering of the modules. This is fixed since #9623. *)
module type S0 = sig
  module rec M : sig type t = M2.t end
  and M2 : sig type t = int end
end with type M.t = int
[%%expect {|
module type S0 =
  sig module rec M : sig type t = int end and M2 : sig type t = int end end
|}]


module type PrintableComparable = sig
  type t
  include Printable with type t := t
  include Comparable with type t := t
end
[%%expect {|
module type PrintableComparable =
  sig type t val print : t -> unit val compare : t -> t -> int end
|}]
module type PrintableComparable = sig
  include Printable
  include Comparable with type t := t
end
[%%expect {|
module type PrintableComparable =
  sig type t val print : t -> unit val compare : t -> t -> int end
|}]
module type ComparableInt = Comparable with type t := int
[%%expect {|
module type ComparableInt = sig val compare : int -> int -> int end
|}]
module type S = sig type t val f : t -> t end
[%%expect {|
module type S = sig type t val f : t -> t end
|}]
module type S' = S with type t := int
[%%expect {|
module type S' = sig val f : int -> int end
|}]

module type S = sig type 'a t val map : ('a -> 'b) -> 'a t -> 'b t end
module type S1 = S with type 'a t := 'a list
[%%expect {|
module type S = sig type 'a t val map : ('a -> 'b) -> 'a t -> 'b t end
module type S1 = sig val map : ('a -> 'b) -> 'a list -> 'b list end
|}]
module type S2 = S with type 'a t := (string * 'a) list
[%%expect {|
module type S2 =
  sig val map : ('a -> 'b) -> (string * 'a) list -> (string * 'b) list end
|}]
module type S3 = S with type _ t := int
[%%expect {|
module type S3 = sig val map : ('a -> 'b) -> int -> int end
|}]


module type S =
  sig module T : sig type exp type arg end val f : T.exp -> T.arg end
module M = struct type exp = string type arg = int end
module type S' = S with module T := M
[%%expect {|
module type S =
  sig module T : sig type exp type arg end val f : T.exp -> T.arg end
module M : sig type exp = string type arg = int end
module type S' = sig val f : M.exp -> M.arg end
|}]


module type S = sig type 'a t end with type 'a t := unit
[%%expect {|
module type S = sig end
|}]

module type S = sig
  type t = [ `Foo ]
  type s = private [< t ]
end with type t := [ `Foo ]
[%%expect {|
module type S = sig type s = private [< `Foo ] end
|}]

module type S = sig
  type t = ..
  type t += A
end with type t := exn
[%%expect {|
module type S = sig type exn += A end
|}]

(* We allow type constraints when replacing a path by a path. *)
type 'a t constraint 'a = 'b list
module type S = sig
  type 'a t2 constraint 'a = 'b list
  type 'a mylist = 'a list
  val x : int mylist t2
end with type 'a t2 := 'a t
[%%expect {|
type 'a t constraint 'a = 'b list
module type S = sig type 'a mylist = 'a list val x : int mylist t end
|}]

(* but not when replacing a path by a type expression *)
type 'a t constraint 'a = 'b list
module type S = sig
  type 'a t2 constraint 'a = 'b list
  type 'a mylist = 'a list
  val x : int mylist t2
end with type 'a t2 := 'a t * bool
[%%expect {|
type 'a t constraint 'a = 'b list
Lines 2-6, characters 16-34:
2 | ................sig
3 |   type 'a t2 constraint 'a = 'b list
4 |   type 'a mylist = 'a list
5 |   val x : int mylist t2
6 | end with type 'a t2 := 'a t * bool
Error: Destructive substitutions are not supported for constrained
       types (other than when replacing a type constructor with
       a type constructor with the same arguments).
|}]

(* Issue where the typer weakens an alias, which breaks the typing of the rest
   of the signature. (MPR#7723)*)
module type S = sig
  module M1 : sig type t = int end
  module M2 = M1
  module M3 : sig module M = M2 end
  module F(X : sig module M = M1 end) : sig type t end
  type t = F(M3).t
end with type M2.t = int
[%%expect {|
module type S =
  sig
    module M1 : sig type t = int end
    module M2 = M1
    module M3 : sig module M = M2 end
    module F : (X : sig module M = M1 end) -> sig type t end
    type t = F(M3).t
  end
|}]

type (_, _) eq = Refl : ('a, 'a) eq

module Equal (M : Set.OrderedType) (N : Set.OrderedType with type t = M.t) : sig
  val eq : (Set.Make(M).t, Set.Make(N).t) eq
end = struct
  type meq = Eq of (Set.Make(M).t, Set.Make(M).t) eq
  module type S = sig
    module N = M
    type neq = meq = Eq of (Set.Make(M).t, Set.Make(N).t) eq
  end
  module type T = S with type N.t = M.t with module N := N;;
  module rec T : T = T
  let eq =
    let T.Eq eq = Eq Refl in
    eq
end;;
[%%expect {|
type (_, _) eq = Refl : ('a, 'a) eq
Line 8, characters 4-16:
8 |     module N = M
        ^^^^^^^^^^^^
Error: Functor arguments and recursive modules (within the
       recursive definition), such as "M", cannot be aliased
|}]

(* Checking that the uses of M.t are rewritten regardless of how they
   are named, but we don't rewrite other types by the same name. *)
module type S = sig
  module M : sig type t val x : t end
  val y : M.t
  module A : sig module M : sig type t val z : t -> M.t end end
end with type M.t := float
[%%expect {|
module type S =
  sig
    module M : sig val x : float end
    val y : float
    module A : sig module M : sig type t val z : t -> float end end
  end
|}]

(* Regression test: at some point, expanding S1 twice in the same
   "with type" would result in a signature with duplicate ids, which
   would confuse the rewriting (we would end with (M2.x : int)) and
   only then get refreshened. *)
module type S = sig
  module type S1 = sig type t type a val x : t end
  module M1 : S1
  type a = M1.t
  module M2 : S1
  type b = M2.t
end with type M1.a = int and type M2.a = int and type M1.t := int;;
[%%expect {|
module type S =
  sig
    module type S1 = sig type t type a val x : t end
    module M1 : sig type a = int val x : int end
    type a = int
    module M2 : sig type t type a = int val x : t end
    type b = M2.t
  end
|}]

(* And now some corner cases with aliases: *)

module type S = sig
  module M : sig type t end
  module A = M
end with type M.t := float
[%%expect {|
Lines 1-4, characters 16-26:
1 | ................sig
2 |   module M : sig type t end
3 |   module A = M
4 | end with type M.t := float
Error: This "with" constraint on "M.t" changes "M", which is aliased
       in the constrained signature (as "A").
|}]

(* And more corner cases with applicative functors: *)

module type S = sig
  module M : sig type t type u end
  module F(X : sig type t end) : sig type t end
  type t = F(M).t
end
[%%expect {|
module type S =
  sig
    module M : sig type t type u end
    module F : (X : sig type t end) -> sig type t end
    type t = F(M).t
  end
|}]

(* This particular substitution cannot be made to work *)
module type S2 = S with type M.t := float
[%%expect {|
Line 1, characters 17-41:
1 | module type S2 = S with type M.t := float
                     ^^^^^^^^^^^^^^^^^^^^^^^^
Error: This "with" constraint on "M.t" makes the applicative functor
       type "F(M).t" ill-typed in the constrained signature:
       Modules do not match:
         sig type u = M.u end
       is not included in
         sig type t end
       The type "t" is required but not provided
|}]

(* However if the applicative functor doesn't care about the type
   we're removing, the typer accepts the removal. *)
module type S2 = S with type M.u := float
[%%expect {|
module type S2 =
  sig
    module M : sig type t end
    module F : (X : sig type t end) -> sig type t end
    type t = F(M).t
  end
|}]

(* In the presence of recursive modules, the use of a module can come before its
   definition (in the typed tree). *)

module Id(X : sig type t end) = struct type t = X.t end
module type S3 = sig
  module rec M : sig type t = A of Id(M2).t end
  and M2 : sig type t end
end with type M2.t := int
[%%expect {|
module Id : (X : sig type t end) -> sig type t = X.t end
Lines 2-5, characters 17-25:
2 | .................sig
3 |   module rec M : sig type t = A of Id(M2).t end
4 |   and M2 : sig type t end
5 | end with type M2.t := int
Error: This "with" constraint on "M2.t" makes the applicative functor
       type "Id(M2).t" ill-typed in the constrained signature:
       Modules do not match: sig end is not included in sig type t end
       The type "t" is required but not provided
|}]


(* Deep destructive module substitution: *)

module A = struct module P = struct type t let x = 1 end end
module type S = sig
  module M : sig
    module N : sig
      module P : sig
        type t
      end
    end
  end
  type t = M.N.P.t
end with module M.N := A
[%%expect {|
module A : sig module P : sig type t val x : int end end
module type S = sig module M : sig end type t = A.P.t end
|}]

(* Same as for types, not all substitutions are accepted *)

module type S = sig
  module M : sig
    module N : sig
      module P : sig
        type t
      end
    end
  end
  module Alias = M
end with module M.N := A
[%%expect {|
Lines 1-10, characters 16-24:
 1 | ................sig
 2 |   module M : sig
 3 |     module N : sig
 4 |       module P : sig
 5 |         type t
 6 |       end
 7 |     end
 8 |   end
 9 |   module Alias = M
10 | end with module M.N := A
Error: This "with" constraint on "M.N" changes "M", which is aliased
       in the constrained signature (as "Alias").
|}]
