(* TEST
   flags = "-strict-sequence"
   * expect
*)
external a : (int [@untagged]) -> unit = "a" "a_nat"
external b : (int32 [@unboxed]) -> unit = "b" "b_nat"
external c : (int64 [@unboxed]) -> unit = "c" "c_nat"
external d : (nativeint [@unboxed]) -> unit = "d" "d_nat"
external e : (float [@unboxed]) -> unit = "e" "e_nat"

type t = private int

external f : (t [@untagged]) -> unit = "f" "f_nat"

module M : sig
  external a : int -> (int [@untagged]) = "a" "a_nat"
  external b : (int [@untagged]) -> int = "b" "b_nat"
end = struct
  external a : int -> (int [@untagged]) = "a" "a_nat"
  external b : (int [@untagged]) -> int = "b" "b_nat"
end;;

[%%expect{|
external a : (int [@untagged]) -> unit = "a" "a_nat"
external b : (int32 [@unboxed]) -> unit = "b" "b_nat"
external c : (int64 [@unboxed]) -> unit = "c" "c_nat"
external d : (nativeint [@unboxed]) -> unit = "d" "d_nat"
external e : (float [@unboxed]) -> unit = "e" "e_nat"
type t = private int
external f : (t [@untagged]) -> unit = "f" "f_nat"
module M :
  sig
    external a : int -> (int [@untagged]) = "a" "a_nat"
    external b : (int [@untagged]) -> int = "b" "b_nat"
  end
|}]

module Global_attributes = struct
  [@@@ocaml.alert "-deprecated"]

  external a : float -> float = "a" "noalloc" "a_nat" "float"
  external b : float -> float = "b" "noalloc" "b_nat"
  external c : float -> float = "c" "c_nat" "float"
  external d : float -> float = "d" "noalloc"
  external e : float -> float = "e"

  (* Should output a warning: no native implementation provided *)
  external f : (int32 [@unboxed]) -> (int32 [@unboxed]) = "f" "noalloc"
  external g : int32 -> int32 = "g" "g_nat" [@@unboxed] [@@noalloc]

  external h : (int [@untagged]) -> (int [@untagged]) = "h" "h_nat" "noalloc"
  external i : int -> int = "i" "i_nat" [@@untagged] [@@noalloc]
end;;

[%%expect{|
Line 11, characters 2-71:
11 |   external f : (int32 [@unboxed]) -> (int32 [@unboxed]) = "f" "noalloc"
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error: [@The native code version of the primitive is mandatory
       when attributes [@untagged] or [@unboxed] are present.
|}]

module Old_style_warning = struct
  [@@@ocaml.warning "+3"]
  external a : float -> float = "a" "noalloc" "a_nat" "float"
  external b : float -> float = "b" "noalloc" "b_nat"
  external c : float -> float = "c" "c_nat" "float"
  external d : float -> float = "d" "noalloc"
  external e : float -> float = "c" "float"
end;;
[%%expect{|
Line 3, characters 2-61:
3 |   external a : float -> float = "a" "noalloc" "a_nat" "float"
      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Alert deprecated: [@@unboxed] + [@@noalloc] should be used
instead of "float"
Line 4, characters 2-53:
4 |   external b : float -> float = "b" "noalloc" "b_nat"
      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Alert deprecated: [@@noalloc] should be used instead of "noalloc"
Line 5, characters 2-51:
5 |   external c : float -> float = "c" "c_nat" "float"
      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Alert deprecated: [@@unboxed] + [@@noalloc] should be used
instead of "float"
Line 6, characters 2-45:
6 |   external d : float -> float = "d" "noalloc"
      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Alert deprecated: [@@noalloc] should be used instead of "noalloc"
module Old_style_warning :
  sig
    external a : float -> float = "a" "a_nat" [@@unboxed] [@@noalloc]
    external b : float -> float = "b" "b_nat" [@@noalloc]
    external c : float -> float = "c" "c_nat" [@@unboxed] [@@noalloc]
    external d : float -> float = "d" [@@noalloc]
    external e : float -> float = "c" "float"
  end
|}]

(* Bad: attributes not reported in the interface *)

module Bad1 : sig
  external f : int -> int = "f" "f_nat"
end = struct
  external f : int -> (int [@untagged]) = "f" "f_nat"
end;;

[%%expect{|
Lines 3-5, characters 6-3:
3 | ......struct
4 |   external f : int -> (int [@untagged]) = "f" "f_nat"
5 | end..
Error: Signature mismatch:
       Modules do not match:
         sig external f : int -> (int [@untagged]) = "f" "f_nat" end
       is not included in
         sig external f : int -> int = "f" "f_nat" end
       Values do not match:
         external f : int -> (int [@untagged]) = "f" "f_nat"
       is not included in
         external f : int -> int = "f" "f_nat"
|}]

module Bad2 : sig
  external f : int -> int = "a" "a_nat"
end = struct
  external f : (int [@untagged]) -> int = "f" "f_nat"
end;;

[%%expect{|
Lines 3-5, characters 6-3:
3 | ......struct
4 |   external f : (int [@untagged]) -> int = "f" "f_nat"
5 | end..
Error: Signature mismatch:
       Modules do not match:
         sig external f : (int [@untagged]) -> int = "f" "f_nat" end
       is not included in
         sig external f : int -> int = "a" "a_nat" end
       Values do not match:
         external f : (int [@untagged]) -> int = "f" "f_nat"
       is not included in
         external f : int -> int = "a" "a_nat"
|}]

module Bad3 : sig
  external f : float -> float = "f" "f_nat"
end = struct
  external f : float -> (float [@unboxed]) = "f" "f_nat"
end;;

[%%expect{|
Lines 3-5, characters 6-3:
3 | ......struct
4 |   external f : float -> (float [@unboxed]) = "f" "f_nat"
5 | end..
Error: Signature mismatch:
       Modules do not match:
         sig external f : float -> (float [@unboxed]) = "f" "f_nat" end
       is not included in
         sig external f : float -> float = "f" "f_nat" end
       Values do not match:
         external f : float -> (float [@unboxed]) = "f" "f_nat"
       is not included in
         external f : float -> float = "f" "f_nat"
|}]

module Bad4 : sig
  external f : float -> float = "a" "a_nat"
end = struct
  external f : (float [@unboxed]) -> float = "f" "f_nat"
end;;

[%%expect{|
Lines 3-5, characters 6-3:
3 | ......struct
4 |   external f : (float [@unboxed]) -> float = "f" "f_nat"
5 | end..
Error: Signature mismatch:
       Modules do not match:
         sig external f : (float [@unboxed]) -> float = "f" "f_nat" end
       is not included in
         sig external f : float -> float = "a" "a_nat" end
       Values do not match:
         external f : (float [@unboxed]) -> float = "f" "f_nat"
       is not included in
         external f : float -> float = "a" "a_nat"
|}]

(* Bad: attributes in the interface but not in the implementation *)

module Bad5 : sig
  external f : int -> (int [@untagged]) = "f" "f_nat"
end = struct
  external f : int -> int = "f" "f_nat"
end;;

[%%expect{|
Lines 3-5, characters 6-3:
3 | ......struct
4 |   external f : int -> int = "f" "f_nat"
5 | end..
Error: Signature mismatch:
       Modules do not match:
         sig external f : int -> int = "f" "f_nat" end
       is not included in
         sig external f : int -> (int [@untagged]) = "f" "f_nat" end
       Values do not match:
         external f : int -> int = "f" "f_nat"
       is not included in
         external f : int -> (int [@untagged]) = "f" "f_nat"
|}]

module Bad6 : sig
  external f : (int [@untagged]) -> int = "f" "f_nat"
end = struct
  external f : int -> int = "a" "a_nat"
end;;

[%%expect{|
Lines 3-5, characters 6-3:
3 | ......struct
4 |   external f : int -> int = "a" "a_nat"
5 | end..
Error: Signature mismatch:
       Modules do not match:
         sig external f : int -> int = "a" "a_nat" end
       is not included in
         sig external f : (int [@untagged]) -> int = "f" "f_nat" end
       Values do not match:
         external f : int -> int = "a" "a_nat"
       is not included in
         external f : (int [@untagged]) -> int = "f" "f_nat"
|}]

module Bad7 : sig
  external f : float -> (float [@unboxed]) = "f" "f_nat"
end = struct
  external f : float -> float = "f" "f_nat"
end;;

[%%expect{|
Lines 3-5, characters 6-3:
3 | ......struct
4 |   external f : float -> float = "f" "f_nat"
5 | end..
Error: Signature mismatch:
       Modules do not match:
         sig external f : float -> float = "f" "f_nat" end
       is not included in
         sig external f : float -> (float [@unboxed]) = "f" "f_nat" end
       Values do not match:
         external f : float -> float = "f" "f_nat"
       is not included in
         external f : float -> (float [@unboxed]) = "f" "f_nat"
|}]

module Bad8 : sig
  external f : (float [@unboxed]) -> float = "f" "f_nat"
end = struct
  external f : float -> float = "a" "a_nat"
end;;

[%%expect{|
Lines 3-5, characters 6-3:
3 | ......struct
4 |   external f : float -> float = "a" "a_nat"
5 | end..
Error: Signature mismatch:
       Modules do not match:
         sig external f : float -> float = "a" "a_nat" end
       is not included in
         sig external f : (float [@unboxed]) -> float = "f" "f_nat" end
       Values do not match:
         external f : float -> float = "a" "a_nat"
       is not included in
         external f : (float [@unboxed]) -> float = "f" "f_nat"
|}]

(* Bad: unboxed or untagged with the wrong type *)

external g : (float [@untagged]) -> float = "g" "g_nat";;
[%%expect{|
Line 1, characters 14-19:
1 | external g : (float [@untagged]) -> float = "g" "g_nat";;
                  ^^^^^
Error: Don't know how to untag this type. Only int can be untagged.
|}]
external h : (int [@unboxed]) -> float = "h" "h_nat";;
[%%expect{|
Line 1, characters 14-17:
1 | external h : (int [@unboxed]) -> float = "h" "h_nat";;
                  ^^^
Error: Don't know how to unbox this type.
       Only float, int32, int64 and nativeint can be unboxed.
|}]

(* Bad: unboxing the function type *)
external i : int -> float [@unboxed] = "i" "i_nat";;
[%%expect{|
Line 1, characters 13-25:
1 | external i : int -> float [@unboxed] = "i" "i_nat";;
                 ^^^^^^^^^^^^
Error: Don't know how to unbox this type.
       Only float, int32, int64 and nativeint can be unboxed.
|}]

(* Bad: unboxing a "deep" sub-type. *)
external j : int -> (float [@unboxed]) * float = "j" "j_nat";;
[%%expect{|
Line 1, characters 21-26:
1 | external j : int -> (float [@unboxed]) * float = "j" "j_nat";;
                         ^^^^^
Error: The attribute '@unboxed' should be attached to
       a direct argument or result of the primitive,
       it should not occur deeply into its type.
|}]

(* This should be rejected, but it is quite complicated to do
   in the current state of things *)

external k : int -> (float [@unboxd]) = "k" "k_nat";;
[%%expect{|
external k : int -> float = "k" "k_nat"
|}]

(* Bad: old style annotations + new style attributes *)

external l : float -> float = "l" "l_nat" "float" [@@unboxed];;
[%%expect{|
Line 1, characters 0-61:
1 | external l : float -> float = "l" "l_nat" "float" [@@unboxed];;
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error: Cannot use "float" in conjunction with [@unboxed]/[@untagged].
|}]
external m : (float [@unboxed]) -> float = "m" "m_nat" "float";;
[%%expect{|
Line 1, characters 0-62:
1 | external m : (float [@unboxed]) -> float = "m" "m_nat" "float";;
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error: Cannot use "float" in conjunction with [@unboxed]/[@untagged].
|}]
external n : float -> float = "n" "noalloc" [@@noalloc];;
[%%expect{|
Line 1, characters 0-55:
1 | external n : float -> float = "n" "noalloc" [@@noalloc];;
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error: Cannot use "noalloc" in conjunction with [@@noalloc].
|}]

(* Warnings: unboxed / untagged without any native implementation *)
external o : (float[@unboxed]) -> float = "o";;
[%%expect{|
Line 1, characters 0-45:
1 | external o : (float[@unboxed]) -> float = "o";;
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error: [@The native code version of the primitive is mandatory
       when attributes [@untagged] or [@unboxed] are present.
|}]
external p : float -> (float[@unboxed]) = "p";;
[%%expect{|
Line 1, characters 0-45:
1 | external p : float -> (float[@unboxed]) = "p";;
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error: [@The native code version of the primitive is mandatory
       when attributes [@untagged] or [@unboxed] are present.
|}]
external q : (int[@untagged]) -> float = "q";;
[%%expect{|
Line 1, characters 0-44:
1 | external q : (int[@untagged]) -> float = "q";;
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error: [@The native code version of the primitive is mandatory
       when attributes [@untagged] or [@unboxed] are present.
|}]
external r : int -> (int[@untagged]) = "r";;
[%%expect{|
Line 1, characters 0-42:
1 | external r : int -> (int[@untagged]) = "r";;
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error: [@The native code version of the primitive is mandatory
       when attributes [@untagged] or [@unboxed] are present.
|}]
external s : int -> int = "s" [@@untagged];;
[%%expect{|
Line 1, characters 0-42:
1 | external s : int -> int = "s" [@@untagged];;
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error: [@The native code version of the primitive is mandatory
       when attributes [@untagged] or [@unboxed] are present.
|}]
external t : float -> float = "t" [@@unboxed];;
[%%expect{|
Line 1, characters 0-45:
1 | external t : float -> float = "t" [@@unboxed];;
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error: [@The native code version of the primitive is mandatory
       when attributes [@untagged] or [@unboxed] are present.
|}]

(* PR#7424 *)
type 'a b = B of 'a b b [@@unboxed];;
[%%expect{|
type 'a b = B of 'a b b [@@unboxed]
|}]


(* MPR#7828 *)
type i = I of int
external id : i -> i = "%identity";;
[%%expect{|
type i = I of int
Line 2, characters 0-34:
2 | external id : i -> i = "%identity";;
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Warning 61: This primitive declaration uses type i, whose representation
may be either boxed or unboxed. Without an annotation to indicate
which representation is intended, the boxed representation has been
selected by default. This default choice may change in future
versions of the compiler, breaking the primitive implementation.
You should explicitly annotate the declaration of i
with [@@boxed] or [@@unboxed], so that its external interface
remains stable in the future.
external id : i -> i = "%identity"
|}];;

type i = I of int
type j = J of int
external id : i -> j = "%identity";;
[%%expect{|
type i = I of int
type j = J of int
Line 3, characters 0-34:
3 | external id : i -> j = "%identity";;
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Warning 61: This primitive declaration uses type i, whose representation
may be either boxed or unboxed. Without an annotation to indicate
which representation is intended, the boxed representation has been
selected by default. This default choice may change in future
versions of the compiler, breaking the primitive implementation.
You should explicitly annotate the declaration of i
with [@@boxed] or [@@unboxed], so that its external interface
remains stable in the future.
Line 3, characters 0-34:
3 | external id : i -> j = "%identity";;
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Warning 61: This primitive declaration uses type j, whose representation
may be either boxed or unboxed. Without an annotation to indicate
which representation is intended, the boxed representation has been
selected by default. This default choice may change in future
versions of the compiler, breaking the primitive implementation.
You should explicitly annotate the declaration of j
with [@@boxed] or [@@unboxed], so that its external interface
remains stable in the future.
external id : i -> j = "%identity"
|}];;

type ib = I of int [@@boxed]
external idb : ib -> ib = "%identity";;
[%%expect{|
type ib = I of int
external idb : ib -> ib = "%identity"
|}];;

type iub = I of int [@@unboxed]
external idub : iub -> iub = "%identity";;
[%%expect{|
type iub = I of int [@@unboxed]
external idub : iub -> iub = "%identity"
|}];;

(* #9607: separability was not computed on with-constraints *)
module type T  = sig type 'k t end
module M : T with type 'k t = string = struct
  type 'k t = string
end
type t = T : 'k M.t -> t [@@unboxed]

[%%expect{|
module type T = sig type 'k t end
module M : sig type 'k t = string end
type t = T : 'k M.t -> t [@@unboxed]
|}];;
