(* TEST
 expect;
*)

module Constr = struct
  type t = A | B | C

  let get _ _ = A

  let put f = ignore (f () : t)
end

module Record = struct
  type t = { a : int; b : int; c : int }

  let get _ _ = { a = 0; b = 0; c = 0 }

  let put f = ignore (f () : t)
end

module Bool = struct
  type t = true | false

  let get _ _ = true

  let put f = ignore (f () : t)
end

module List = struct
  type 'a t = [] | (::) of 'a * 'a t

  let get _ _ = []

  let put f = ignore (f () : int t)
end

module Unit = struct
  [@@@warning "-redefining-unit"]
  type t = ()

  let get _ _ = ()

  let put f = ignore (f (() : unit) : t)
end;;
[%%expect{|
module Constr :
  sig
    type t = A | B | C
    val get : 'a -> 'b -> t
    val put : (unit -> t) -> unit
  end
module Record :
  sig
    type t = { a : int; b : int; c : int; }
    val get : 'a -> 'b -> t
    val put : (unit -> t) -> unit
  end
module Bool :
  sig
    type t = true | false
    val get : 'a -> 'b -> t
    val put : (unit -> t) -> unit
  end
module List :
  sig
    type 'a t = [] | (::) of 'a * 'a t
    val get : 'a -> 'b -> 'c t
    val put : (unit -> int t) -> unit
  end
module Unit :
  sig type t = () val get : 'a -> 'b -> t val put : (unit -> t) -> unit end
|}]

let () =
  match Constr.get () with
  | A | B | C -> ();;
[%%expect{|
Line 3, characters 4-5:
3 |   | A | B | C -> ();;
        ^
Error: This pattern should not be a constructor, the expected type is
       "'a -> Constr.t"
|}]

let () =
  match Record.get () with
  | { a; _ } -> ();;
[%%expect{|
Line 3, characters 4-12:
3 |   | { a; _ } -> ();;
        ^^^^^^^^
Error: This pattern should not be a record, the expected type is
       "'a -> Record.t"
|}]

let () =
  match Bool.get () with
  | true -> ();;
[%%expect{|
Line 3, characters 4-8:
3 |   | true -> ();;
        ^^^^
Error: This pattern should not be a boolean literal, the expected type is
       "'a -> Bool.t"
|}]

let () =
  match Bool.get () with
  | false -> ();;
[%%expect{|
Line 3, characters 4-9:
3 |   | false -> ();;
        ^^^^^
Error: This pattern should not be a boolean literal, the expected type is
       "'a -> Bool.t"
|}]

let () =
  match List.get () with
  | [] -> ();;
[%%expect{|
Line 3, characters 4-6:
3 |   | [] -> ();;
        ^^
Error: This pattern should not be a list literal, the expected type is
       "'a -> 'b List.t"
|}]

let () =
  match List.get () with
  | _ :: _ -> ();;
[%%expect{|
Line 3, characters 4-10:
3 |   | _ :: _ -> ();;
        ^^^^^^
Error: This pattern should not be a list literal, the expected type is
       "'a -> 'b List.t"
|}]

let () =
  match Unit.get () with
  | () -> ();;
[%%expect{|
Line 3, characters 4-6:
3 |   | () -> ();;
        ^^
Error: This pattern should not be a unit literal, the expected type is
       "'a -> Unit.t"
|}]

let () = Constr.put A;;
[%%expect{|
Line 1, characters 20-21:
1 | let () = Constr.put A;;
                        ^
Error: This expression should not be a constructor, the expected type is
       "unit -> Constr.t"
|}]

let () = Record.put { a = 0; b = 0; c = 0 };;
[%%expect{|
Line 1, characters 20-43:
1 | let () = Record.put { a = 0; b = 0; c = 0 };;
                        ^^^^^^^^^^^^^^^^^^^^^^^
Error: This expression should not be a record, the expected type is
       "unit -> Record.t"
|}]

let () = Bool.put true;;
[%%expect{|
Line 1, characters 18-22:
1 | let () = Bool.put true;;
                      ^^^^
Error: This expression should not be a boolean literal, the expected type is
       "unit -> Bool.t"
|}]

let () = Bool.put false;;
[%%expect{|
Line 1, characters 18-23:
1 | let () = Bool.put false;;
                      ^^^^^
Error: This expression should not be a boolean literal, the expected type is
       "unit -> Bool.t"
|}]

let () = List.put [];;
[%%expect{|
Line 1, characters 18-20:
1 | let () = List.put [];;
                      ^^
Error: This expression should not be a list literal, the expected type is
       "unit -> int List.t"
|}]

let () = List.put (1 :: 2);;
[%%expect{|
Line 1, characters 18-26:
1 | let () = List.put (1 :: 2);;
                      ^^^^^^^^
Error: This expression should not be a list literal, the expected type is
       "unit -> int List.t"
|}]

let () = Unit.put ();;
[%%expect{|
Line 1, characters 18-20:
1 | let () = Unit.put ();;
                      ^^
Error: This expression should not be a unit literal, the expected type is
       "unit -> Unit.t"
|}]

let () =
  ignore ((Record.get ()).a);;
[%%expect{|
Line 2, characters 10-25:
2 |   ignore ((Record.get ()).a);;
              ^^^^^^^^^^^^^^^
Error: This expression has type "'a -> Record.t" which is not a record type.
|}]

let () =
  (Record.get ()).a <- 5;;
[%%expect{|
Line 2, characters 2-17:
2 |   (Record.get ()).a <- 5;;
      ^^^^^^^^^^^^^^^
Error: This expression has type "'a -> Record.t" which is not a record type.
|}]

let () =
  ignore { (Record.get ()) with a = 5 };;
[%%expect{|
Line 2, characters 11-26:
2 |   ignore { (Record.get ()) with a = 5 };;
               ^^^^^^^^^^^^^^^
Error: This expression has type "'a -> Record.t" which is not a record type.
|}]

let foo x =
  Record.put { x with a = 5 };;
[%%expect{|
Line 2, characters 13-29:
2 |   Record.put { x with a = 5 };;
                 ^^^^^^^^^^^^^^^^
Error: This expression should not be a record, the expected type is
       "unit -> Record.t"
|}]
