diff --git a/ocaml/otherlibs/stdlib_alpha/or_null.ml b/ocaml/otherlibs/stdlib_alpha/or_null.ml index 646f5f80113..b20b47fba3a 100644 --- a/ocaml/otherlibs/stdlib_alpha/or_null.ml +++ b/ocaml/otherlibs/stdlib_alpha/or_null.ml @@ -12,4 +12,6 @@ (* *) (**************************************************************************) -type ('a : non_null_value) t = 'a option +type ('a : non_null_value) t = 'a or_null = + | Null + | This of 'a diff --git a/ocaml/otherlibs/stdlib_alpha/or_null.mli b/ocaml/otherlibs/stdlib_alpha/or_null.mli index e9acae99f42..71900daf874 100644 --- a/ocaml/otherlibs/stdlib_alpha/or_null.mli +++ b/ocaml/otherlibs/stdlib_alpha/or_null.mli @@ -13,4 +13,6 @@ (**************************************************************************) (** Unboxed option type. Unimplemented. *) -type ('a : non_null_value) t +type ('a : non_null_value) t = 'a or_null = + | Null + | This of 'a diff --git a/ocaml/testsuite/tests/ppx-empty-cases/test.compilers.reference b/ocaml/testsuite/tests/ppx-empty-cases/test.compilers.reference index fcfebfb74c5..25a0daeb744 100644 --- a/ocaml/testsuite/tests/ppx-empty-cases/test.compilers.reference +++ b/ocaml/testsuite/tests/ppx-empty-cases/test.compilers.reference @@ -3,35 +3,35 @@ (empty_cases_returning_string/269 = (function {nlocal = 0} param/271 (raise - (makeblock 0 (getpredef Match_failure/32!!) [0: "test.ml" 28 50]))) + (makeblock 0 (getpredef Match_failure/33!!) [0: "test.ml" 28 50]))) empty_cases_returning_float64/272 = (function {nlocal = 0} param/274 : unboxed_float (raise - (makeblock 0 (getpredef Match_failure/32!!) [0: "test.ml" 29 50]))) + (makeblock 0 (getpredef Match_failure/33!!) [0: "test.ml" 29 50]))) empty_cases_accepting_string/275 = (function {nlocal = 0} param/277 (raise - (makeblock 0 (getpredef Match_failure/32!!) [0: "test.ml" 30 50]))) + (makeblock 0 (getpredef Match_failure/33!!) [0: "test.ml" 30 50]))) empty_cases_accepting_float64/278 = (function {nlocal = 0} param/280[unboxed_float] (raise - (makeblock 0 (getpredef Match_failure/32!!) [0: "test.ml" 31 50]))) + (makeblock 0 (getpredef Match_failure/33!!) [0: "test.ml" 31 50]))) non_empty_cases_returning_string/281 = (function {nlocal = 0} param/283 (raise - (makeblock 0 (getpredef Assert_failure/42!!) [0: "test.ml" 32 68]))) + (makeblock 0 (getpredef Assert_failure/43!!) [0: "test.ml" 32 68]))) non_empty_cases_returning_float64/284 = (function {nlocal = 0} param/286 : unboxed_float (raise - (makeblock 0 (getpredef Assert_failure/42!!) [0: "test.ml" 33 68]))) + (makeblock 0 (getpredef Assert_failure/43!!) [0: "test.ml" 33 68]))) non_empty_cases_accepting_string/287 = (function {nlocal = 0} param/289 (raise - (makeblock 0 (getpredef Assert_failure/42!!) [0: "test.ml" 34 68]))) + (makeblock 0 (getpredef Assert_failure/43!!) [0: "test.ml" 34 68]))) non_empty_cases_accepting_float64/290 = (function {nlocal = 0} param/292[unboxed_float] (raise - (makeblock 0 (getpredef Assert_failure/42!!) [0: "test.ml" 35 68])))) + (makeblock 0 (getpredef Assert_failure/43!!) [0: "test.ml" 35 68])))) (makeblock 0 empty_cases_returning_string/269 empty_cases_returning_float64/272 empty_cases_accepting_string/275 empty_cases_accepting_float64/278 non_empty_cases_returning_string/281 diff --git a/ocaml/testsuite/tests/typing-layouts-non-null-value/arguments.ml b/ocaml/testsuite/tests/typing-layouts-non-null-value/arguments.ml index 7e28219d3ec..f610abe23d7 100644 --- a/ocaml/testsuite/tests/typing-layouts-non-null-value/arguments.ml +++ b/ocaml/testsuite/tests/typing-layouts-non-null-value/arguments.ml @@ -49,7 +49,7 @@ let _ = [| Fake_or_null.some 3 |] let _ = [: Fake_or_null.some "test " :] -let _ = Some (Fake_or_null.some 4.2) +let _ = Some (Fake_or_null.some 42) let _ = lazy (Fake_or_null.none) ;; @@ -58,7 +58,7 @@ let _ = lazy (Fake_or_null.none) - : 'a Fake_or_null.t list = [] - : int Fake_or_null.t array = [||] - : string Fake_or_null.t iarray = [::] -- : float Fake_or_null.t option = Some +- : int Fake_or_null.t option = Some - : 'a Fake_or_null.t lazy_t = lazy |}] diff --git a/ocaml/testsuite/tests/typing-layouts-non-null-value/basics.ml b/ocaml/testsuite/tests/typing-layouts-non-null-value/basics.ml index 550f4fbd8b6..e2fd3a8d4f0 100644 --- a/ocaml/testsuite/tests/typing-layouts-non-null-value/basics.ml +++ b/ocaml/testsuite/tests/typing-layouts-non-null-value/basics.ml @@ -166,8 +166,6 @@ let _ = id_non_null_value None let _ = id_non_null_value (Some 0) -let _ = id_non_null_value 3.14 - let _ = id_non_null_value [| 3.; 8. |] let _ = id_non_null_value 4L @@ -189,7 +187,6 @@ let _ = id_non_null_value (Bytes.empty) - : string * string = ("a", "b") - : 'a option = None - : int option = Some 0 -- : float = 3.14 - : float array = [|3.; 8.|] - : int64 = 4L - : nativeint = 15n @@ -199,6 +196,23 @@ let _ = id_non_null_value (Bytes.empty) - : bytes = Bytes.of_string "" |}] +(* CR layouts v3: [float] should be non-null: *) + +let _ = id_non_null_value 3.14 +;; + +[%%expect{| +Line 1, characters 26-30: +1 | let _ = id_non_null_value 3.14 + ^^^^ +Error: This expression has type float but an expression was expected of type + ('a : non_null_value) + The layout of float is value, because + it is the primitive value type float. + But the layout of float must be a sublayout of non_null_value, because + of the definition of id_non_null_value at line 3, characters 4-21. +|}] + (* Boxed records and variants are non-null: *) type t1 = { x : int; y : string } diff --git a/ocaml/testsuite/tests/typing-layouts-non-null-value/or_null.ml b/ocaml/testsuite/tests/typing-layouts-non-null-value/or_null.ml deleted file mode 100644 index a33be00ac66..00000000000 --- a/ocaml/testsuite/tests/typing-layouts-non-null-value/or_null.ml +++ /dev/null @@ -1,332 +0,0 @@ -(* TEST - reason = "Unboxed types aren't implemented yet"; - skip; - expect; -*) -(* CR layouts (v3): enable this test *) - -module type Or_null = sig - (* CR layouts (v3): Not sure how to express that None and Some should - be part of this module. They're not quite constructors. So the syntax - here might plausibly change. *) - type 'a t = 'a or_null = - | None - | Some of 'a - - val none : 'a or_null - val some : 'a -> 'a or_null - val value : 'a or_null -> default:'a -> 'a - val get : 'a or_null -> 'a - val bind : 'a or_null -> ('a -> 'b or_null) -> 'b or_null - (* unlike [option] we cannot have [join] *) - val map : ('a -> 'b) -> 'a or_null -> 'b or_null - val fold : none:'a -> some:('b -> 'a) -> 'b or_null -> 'a - val iter : ('a -> unit) -> 'a or_null -> unit - - val is_none : 'a or_null -> bool - val is_some : 'a or_null -> bool - val equal : ('a -> 'a -> bool) -> 'a or_null -> 'a or_null -> bool - val compare : ('a -> 'a -> int) -> 'a or_null -> 'a or_null -> int - - val to_result : none:'e -> 'a or_null -> ('a, 'e) result - val to_list : 'a or_null -> 'a list - val to_seq : 'a or_null -> 'a Seq.t - - val to_option : 'a or_null -> 'a option - val of_option : 'a option -> 'a or_null -end - -module Or_null : Or_null = Or_null - -(* CR layouts (v3): check output to see how bad the pretty-printing is. - In particular, it would be nice to suppress layout annotations that - are implied by the rest of the signature, but this may be hard. *) -[%%expect {| -success -|}] - -(* ensure that immediacy "looks through" or_null *) -type t1 : immediate = int or_null -type t2 : immediate = bool or_null - -[%%expect {| -success -|}] - -type t : immediate = string or_null - -[%%expect {| -error -|}] - -type t : value = string or_null - -[%%expect {| -success -|}] - -(* ensure that or_null can't be repeated *) -type 'a t = 'a or_null or_null - -[%%expect {| -error -|}] - -(* check inference around or_null *) -type 'a t = 'a or_null -type ('a : immediate) t = 'a or_null - -[%%expect {| -success -success (inferring an immediate jkind for [t] and non_null_immediate for ['a]) -|}] - -(* more jkind checking *) -type t : non_null_value = string or_null - -[%%expect {| -error -|}] - -type t1 : non_null_value = string -type t2 : non_null_value = int -type t3 : non_null_immediate = int -type t4 : value = int or_null - -[%%expect {| -success -|}] - -(* magic looking-through of [or_null] can't be abstracted over *) -type 'a t = 'a or_null -type q1 : value = string t -type q2 : immediate = int t (* but t isn't abstract, so this is OK *) - -[%%expect {| -success -|}] - -type q = string t t - -[%%expect {| -error -|}] - -type q = int t t - -[%%expect {| -error -|}] - -type 'a q1 = 'a t -type ('a : immediate) q2 : immediate = 'a t - -[%%expect {| -success -|}] - -module type T = sig - type t -end - -[%%expect {| -success -|}] - -(* this should be rejected, because the default for [t] is [non_null_value] *) -module M : T = struct - type t = string or_null -end - -[%%expect {| -error -|}] - -module M : T = struct - type t = int or_null -end - -[%%expect {| -error -|}] - -module M : sig - type 'a t -end = struct - type 'a t = 'a or_null -end - -(* CR layouts (v3): This error message had better be excellent, because the - solution -- to add a [: value] annotation -- will be unusual. Normally, - people think of [value] as the default! *) -[%%expect {| -error -|}] - -module M : sig - type 'a t : value -end = struct - type 'a t = 'a or_null -end - -[%%expect {| -success -|}] - -type t = string M.t - -[%%expect {| -success -|}] - -type t = int M.t - -[%%expect {| -success -|}] - -type t = (int M.t : immediate) (* this is the one that requires "looking through" *) - -[%%expect {| -error -|}] - -(* tests that or_null actually works at runtime *) - -let x = match Or_null.some 5 with - | None -> 6 - | Some n -> n - -let x = match Or_null.Some 5 with - | None -> 6 - | Some n -> n - -let x = match Or_null.some "hello" with - | None -> "bad" - | Some s -> s - -let x = match Or_null.Some "hello" with - | None -> "bad" - | Some s -> s - -let x = match Or_null.none with - | None -> 6 - | Some s -> s - -let x = match Or_null.None with - | None -> 6 - | Some s -> s - -let x = match Or_null.none with - | None -> "good" - | Some s -> s - -let x = match Or_null.None with - | None -> "good" - | Some s -> s - -[%%expect {| -5 -5 -"hello" -"hello" -6 -6 -"good" -"good" -|}] - -let b = Or_null.some 0 = Obj.magic 0 - -(* this should work because they're immediate, though it's technically unspecified *) -let b = Or_null.some 0 == Obj.magic 0 - -let b = (Or_null.none : int or_null) = Obj.magic 0 - -let b = (Or_null.none : string or_null) = Obj.magic 0 - -let b = (Or_null.none : int or_null) = Obj.magic (Or_null.none : string or_null) - -[%%expect {| -true -true -false -false -true -|}] - -(* CR layouts (v3): make other reference-implementation tests for the - [Or_null] interface once we have the quickcheck-like architecture - (TANDC-1809). *) - -(* check allocation behavior *) - -let measure_alloc f = - (* NB: right-to-left evaluation order gets this right *) - let baseline_allocation = Gc.allocated_bytes() -. Gc.allocated_bytes() in - let before = Gc.allocated_bytes () in - let result = (f[@inlined never]) () in - let after = Gc.allocated_bytes () in - (after -. before) -. baseline_allocation, result - -[%%expect {| -success -|}] - -let alloc = measure_alloc (fun () -> let x = Or_null.some 5 in ()) -let alloc = measure_alloc (fun () -> let x = Or_null.Some 5 in ()) -let alloc = - measure_alloc (fun () -> - (* this should infer f to be local, and thus the closures at usage - sites won't allocate *) - let bind opt f = Or_null.(match opt with - None -> None - Some x -> f x - ) in - let x = Or_null.some 5 in - let y = Or_null.some 6 in - let f a b = bind x (fun x -> bind y Or_null.(fun y -> some (x + y))) in - f x y) - -[%%expect {| -0 -0 -0 -|}] - -(* sub-typing *) - -let f x = (x : int :> int or_null) -let f x = (x : string :> string or_null) -let f x = (x : int list :> int or_null list) -let f x = (x : string list :> string or_null list) -let f x = (x : int list :> int list or_null) -let f x = (x : string list :> string list or_null) - -[%%expect {| -success -|}] - -let f x = (x : int or_null :> int) - -[%%expect {| -error -|}] - -let f x = (x : string or_null :> string) - -[%%expect {| -error -|}] - -let f x = (x : int :> int or_null or_null) - -[%%expect {| -error -|}] - -let f x = (x : int :> string or_null) - -[%%expect {| -error -|}] diff --git a/ocaml/testsuite/tests/typing-layouts-non-null-value/test_or_null.ml b/ocaml/testsuite/tests/typing-layouts-non-null-value/test_or_null.ml new file mode 100644 index 00000000000..660a510210f --- /dev/null +++ b/ocaml/testsuite/tests/typing-layouts-non-null-value/test_or_null.ml @@ -0,0 +1,486 @@ +(* TEST + flags = "-extension-universe alpha"; + include stdlib_alpha; + expect; +*) + +module type Or_null = sig + type ('a : non_null_value) t = 'a or_null = + | Null + | This of 'a + + (* CR layouts v3.0: implement those functions. *) + + (* val none : 'a or_null + val some : 'a -> 'a or_null + val value : 'a or_null -> default:'a -> 'a + val get : 'a or_null -> 'a + val bind : 'a or_null -> ('a -> 'b or_null) -> 'b or_null + (* unlike [option] we cannot have [join] *) + val map : ('a -> 'b) -> 'a or_null -> 'b or_null + val fold : none:'a -> some:('b -> 'a) -> 'b or_null -> 'a + val iter : ('a -> unit) -> 'a or_null -> unit + + val is_none : 'a or_null -> bool + val is_some : 'a or_null -> bool + val equal : ('a -> 'a -> bool) -> 'a or_null -> 'a or_null -> bool + val compare : ('a -> 'a -> int) -> 'a or_null -> 'a or_null -> int + + val to_result : none:'e -> 'a or_null -> ('a, 'e) result + val to_list : 'a or_null -> 'a list + val to_seq : 'a or_null -> 'a Seq.t + + val to_option : 'a or_null -> 'a option + val of_option : 'a option -> 'a or_null *) +end + +module Or_null : Or_null = Stdlib_alpha.Or_null + +(* CR layouts (v3): check output to see how bad the pretty-printing is. + In particular, it would be nice to suppress layout annotations that + are implied by the rest of the signature, but this may be hard. *) +[%%expect {| +module type Or_null = + sig type ('a : non_null_value) t = 'a or_null = Null | This of 'a end +module Or_null : Or_null +|}] + +(* CR layouts v3.0: ensure that immediacy "looks through" or_null. + Currently, [immediate] is always non-null, so we can't test this. *) +type t1 : immediate = int or_null +type t2 : immediate = bool or_null + +[%%expect {| +Line 1, characters 0-33: +1 | type t1 : immediate = int or_null + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Error: The layout of type int or_null is value, because + it is the primitive value type or_null. + But the layout of type int or_null must be a sublayout of immediate, because + of the definition of t1 at line 1, characters 0-33. +|}] + +type t : immediate = string or_null + +[%%expect {| +Line 1, characters 0-35: +1 | type t : immediate = string or_null + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Error: The layout of type string or_null is value, because + it is the primitive value type or_null. + But the layout of type string or_null must be a sublayout of immediate, because + of the definition of t at line 1, characters 0-35. +|}] + +type t : value = string or_null + +[%%expect {| +type t = string or_null +|}] + +(* ensure that or_null can't be repeated *) +type 'a t = 'a or_null or_null + +[%%expect {| +Line 1, characters 12-22: +1 | type 'a t = 'a or_null or_null + ^^^^^^^^^^ +Error: This type 'a or_null should be an instance of type + ('b : non_null_value) + The layout of 'a or_null is value, because + it is the primitive value type or_null. + But the layout of 'a or_null must be a sublayout of non_null_value, because + the type argument of option has layout non_null_value. +|}] + +(* check inference around or_null *) +type 'a t = 'a or_null +type ('a : immediate) t = 'a or_null + +[%%expect {| +type ('a : non_null_value) t = 'a or_null +type ('a : immediate) t = 'a or_null +|}] + +(* more jkind checking *) +type t : non_null_value = string or_null + +[%%expect {| +Line 1, characters 0-40: +1 | type t : non_null_value = string or_null + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Error: The layout of type string or_null is value, because + it is the primitive value type or_null. + But the layout of type string or_null must be a sublayout of non_null_value, because + of the definition of t at line 1, characters 0-40. +|}] + +(* CR layouts v3.0: implement [immediate_or_null] *) + +type t1 : non_null_value = string +type t2 : non_null_value = int +type t3 : immediate = int +type t4 : immediate_or_null = int or_null + +[%%expect {| +type t1 = string +type t2 = int +type t3 = int +Line 4, characters 10-27: +4 | type t4 : immediate_or_null = int or_null + ^^^^^^^^^^^^^^^^^ +Error: Unknown layout immediate_or_null +|}] + +(* magic looking-through of [or_null] can't be abstracted over *) +type 'a t = 'a or_null +type q1 : value = string t +type q2 : immediate_or_null = int t (* but t isn't abstract, so this is OK *) + +[%%expect {| +type ('a : non_null_value) t = 'a or_null +type q1 = string t +Line 3, characters 10-27: +3 | type q2 : immediate_or_null = int t (* but t isn't abstract, so this is OK *) + ^^^^^^^^^^^^^^^^^ +Error: Unknown layout immediate_or_null +|}] + +type q = string t t + +[%%expect {| +Line 1, characters 9-17: +1 | type q = string t t + ^^^^^^^^ +Error: This type string t = string or_null should be an instance of type + ('a : non_null_value) + The layout of string t is value, because + it is the primitive value type or_null. + But the layout of string t must be a sublayout of non_null_value, because + of the definition of t at line 1, characters 0-22. +|}] + +type q = int t t + +[%%expect {| +Line 1, characters 9-14: +1 | type q = int t t + ^^^^^ +Error: This type int t = int or_null should be an instance of type + ('a : non_null_value) + The layout of int t is value, because + it is the primitive value type or_null. + But the layout of int t must be a sublayout of non_null_value, because + of the definition of t at line 1, characters 0-22. +|}] + +(* CR layouts v2.8: Make [or_null] kind polymorphic, so this is accepted. *) + +type 'a q1 = 'a t +type ('a : immediate) q2 : immediate_or_null = 'a t + +[%%expect {| +type ('a : non_null_value) q1 = 'a t +Line 2, characters 27-44: +2 | type ('a : immediate) q2 : immediate_or_null = 'a t + ^^^^^^^^^^^^^^^^^ +Error: Unknown layout immediate_or_null +|}] + +(* CR layouts v3.0: default to [non_null_value] for abstract types *) +module type T = sig + type t +end + +[%%expect {| +module type T = sig type t end +|}] + +(* this should be rejected, because the default for [t] is [non_null_value] *) +module M : T = struct + type t = string or_null +end + +[%%expect {| +module M : T +|}] + +module M : T = struct + type t = int or_null +end + +[%%expect {| +module M : T +|}] + +module M : sig + type 'a t +end = struct + type 'a t = 'a or_null +end + +(* CR layouts (v3): This error message had better be excellent, because the + solution -- to add a [: value] annotation -- will be unusual. Normally, + people think of [value] as the default! *) +[%%expect {| +Lines 3-5, characters 6-3: +3 | ......struct +4 | type 'a t = 'a or_null +5 | end +Error: Signature mismatch: + Modules do not match: + sig type ('a : non_null_value) t = 'a or_null end + is not included in + sig type 'a t end + Type declarations do not match: + type ('a : non_null_value) t = 'a or_null + is not included in + type 'a t + Their parameters differ: + The type ('a : non_null_value) is not equal to the type ('a0 : value) + because their layouts are different. +|}] + +(* CR layouts v3.0: ['a] in signature should default to [non_null_value] *) + +module M : sig + type 'a t : value +end = struct + type 'a t = 'a or_null +end + +[%%expect {| +Lines 3-5, characters 6-3: +3 | ......struct +4 | type 'a t = 'a or_null +5 | end +Error: Signature mismatch: + Modules do not match: + sig type ('a : non_null_value) t = 'a or_null end + is not included in + sig type 'a t : value end + Type declarations do not match: + type ('a : non_null_value) t = 'a or_null + is not included in + type 'a t : value + Their parameters differ: + The type ('a : non_null_value) is not equal to the type ('a0 : value) + because their layouts are different. +|}] + +module M : sig + type ('a : non_null_value) t : value +end = struct + type 'a t = 'a or_null +end + +[%%expect {| +module M : sig type ('a : non_null_value) t : value end +|}] + +type t = string M.t + +[%%expect {| +type t = string M.t +|}] + +type t = int M.t + +[%%expect {| +type t = int M.t +|}] + +type ('a : immediate) id_imm = 'a + +type t = (int M.t) id_imm (* this is the one that requires "looking through" *) + +[%%expect {| +type ('a : immediate) id_imm = 'a +Line 3, characters 10-17: +3 | type t = (int M.t) id_imm (* this is the one that requires "looking through" *) + ^^^^^^^ +Error: This type int M.t should be an instance of type ('a : immediate) + The layout of int M.t is value, because + of the definition of t at line 2, characters 2-38. + But the layout of int M.t must be a sublayout of immediate, because + of the definition of id_imm at line 1, characters 0-33. +|}] + +(* CR layouts v3: [float or_null] should compile: *) + +type t = float or_null +;; + +[%%expect {| +Line 1, characters 9-14: +1 | type t = float or_null + ^^^^^ +Error: This type float should be an instance of type ('a : non_null_value) + The layout of float is value, because + it is the primitive value type float. + But the layout of float must be a sublayout of non_null_value, because + the type argument of option has layout non_null_value. +|}] + +(* CR layouts v3: [float or_null array] should not compile, + but for a different reason: *) + +type t = float or_null array +;; + +[%%expect {| +Line 1, characters 9-14: +1 | type t = float or_null array + ^^^^^ +Error: This type float should be an instance of type ('a : non_null_value) + The layout of float is value, because + it is the primitive value type float. + But the layout of float must be a sublayout of non_null_value, because + the type argument of option has layout non_null_value. +|}] + +(* CR layouts v3.0: implement features below. *) + +(* + +(* tests that or_null actually works at runtime *) + +let x = match Or_null.some 5 with + | None -> 6 + | Some n -> n + +let x = match Or_null.Some 5 with + | None -> 6 + | Some n -> n + +let x = match Or_null.some "hello" with + | None -> "bad" + | Some s -> s + +let x = match Or_null.Some "hello" with + | None -> "bad" + | Some s -> s + +let x = match Or_null.none with + | None -> 6 + | Some s -> s + +let x = match Or_null.None with + | None -> 6 + | Some s -> s + +let x = match Or_null.none with + | None -> "good" + | Some s -> s + +let x = match Or_null.None with + | None -> "good" + | Some s -> s + +[%%expect {| +5 +5 +"hello" +"hello" +6 +6 +"good" +"good" +|}] + +let b = Or_null.some 0 = Obj.magic 0 + +(* this should work because they're immediate, though it's technically unspecified *) +let b = Or_null.some 0 == Obj.magic 0 + +let b = (Or_null.none : int or_null) = Obj.magic 0 + +let b = (Or_null.none : string or_null) = Obj.magic 0 + +let b = (Or_null.none : int or_null) = Obj.magic (Or_null.none : string or_null) + +[%%expect {| +true +true +false +false +true +|}] + +(* CR layouts (v3): make other reference-implementation tests for the + [Or_null] interface once we have the quickcheck-like architecture + (TANDC-1809). *) + +(* check allocation behavior *) + +let measure_alloc f = + (* NB: right-to-left evaluation order gets this right *) + let baseline_allocation = Gc.allocated_bytes() -. Gc.allocated_bytes() in + let before = Gc.allocated_bytes () in + let result = (f[@inlined never]) () in + let after = Gc.allocated_bytes () in + (after -. before) -. baseline_allocation, result + +[%%expect {| +success +|}] + +let alloc = measure_alloc (fun () -> let x = Or_null.some 5 in ()) +let alloc = measure_alloc (fun () -> let x = Or_null.Some 5 in ()) +let alloc = + measure_alloc (fun () -> + (* this should infer f to be local, and thus the closures at usage + sites won't allocate *) + let bind opt f = Or_null.(match opt with + None -> None + Some x -> f x + ) in + let x = Or_null.some 5 in + let y = Or_null.some 6 in + let f a b = bind x (fun x -> bind y Or_null.(fun y -> some (x + y))) in + f x y) + +[%%expect {| +0 +0 +0 +|}] + +(* sub-typing *) + +let f x = (x : int :> int or_null) +let f x = (x : string :> string or_null) +let f x = (x : int list :> int or_null list) +let f x = (x : string list :> string or_null list) +let f x = (x : int list :> int list or_null) +let f x = (x : string list :> string list or_null) + +[%%expect {| +success +|}] + +let f x = (x : int or_null :> int) + +[%%expect {| +error +|}] + +let f x = (x : string or_null :> string) + +[%%expect {| +error +|}] + +let f x = (x : int :> int or_null or_null) + +[%%expect {| +error +|}] + +let f x = (x : int :> string or_null) + +[%%expect {| +error +|}] + +*) diff --git a/ocaml/testsuite/tests/typing-layouts/datatypes_alpha.ml b/ocaml/testsuite/tests/typing-layouts/datatypes_alpha.ml index d7c0d9bc3fe..c96c13be1c8 100644 --- a/ocaml/testsuite/tests/typing-layouts/datatypes_alpha.ml +++ b/ocaml/testsuite/tests/typing-layouts/datatypes_alpha.ml @@ -325,8 +325,8 @@ Line 8, characters 32-36: ^^^^ Error: This expression has type float but an expression was expected of type ('a : immediate) - The layout of float is non_null_value, because - it is the primitive non-null value type float. + The layout of float is value, because + it is the primitive value type float. But the layout of float must be a sublayout of immediate, because of the definition of s6 at line 2, characters 0-35. |}];; diff --git a/ocaml/typing/jkind.ml b/ocaml/typing/jkind.ml index 7880618ec34..45b65f838af 100644 --- a/ocaml/typing/jkind.ml +++ b/ocaml/typing/jkind.ml @@ -1014,6 +1014,11 @@ end = struct | Polymorphic_variant -> fprintf ppf "it's a polymorphic variant type" | Arrow -> fprintf ppf "it's a function type" | First_class_module -> fprintf ppf "it's a first-class module type" + | Type_argument { parent_path; position; arity } -> + fprintf ppf "the %stype argument of %a has layout %s" + (format_position ~arity position) + !printtyp_path parent_path + (Legacy.string_of_const Non_null_value) let format_float64_creation_reason ppf : float64_creation_reason -> _ = function @@ -1416,6 +1421,9 @@ module Debug_printers = struct | Polymorphic_variant -> fprintf ppf "Polymorphic_variant" | Arrow -> fprintf ppf "Arrow" | First_class_module -> fprintf ppf "First_class_module" + | Type_argument { parent_path; position; arity } -> + fprintf ppf "Type_argument (pos %d, arity %d) of %a" position arity + !printtyp_path parent_path let float64_creation_reason ppf : float64_creation_reason -> _ = function | Primitive id -> fprintf ppf "Primitive %s" (Ident.unique_name id) diff --git a/ocaml/typing/jkind_intf.ml b/ocaml/typing/jkind_intf.ml index c6b08d91d1c..07d7154dbfb 100644 --- a/ocaml/typing/jkind_intf.ml +++ b/ocaml/typing/jkind_intf.ml @@ -213,6 +213,12 @@ module History = struct | Polymorphic_variant | Arrow | First_class_module + | Type_argument of + { parent_path : Path.t; + position : int; + arity : int + } + (* [position] is 1-indexed *) type immediate_creation_reason = | Empty_record diff --git a/ocaml/typing/predef.ml b/ocaml/typing/predef.ml index 273d038e662..39849c84385 100644 --- a/ocaml/typing/predef.ml +++ b/ocaml/typing/predef.ml @@ -55,6 +55,7 @@ and ident_unboxed_float32 = ident_create "float32#" and ident_unboxed_nativeint = ident_create "nativeint#" and ident_unboxed_int32 = ident_create "int32#" and ident_unboxed_int64 = ident_create "int64#" +and ident_or_null = ident_create "or_null" and ident_int8x16 = ident_create "int8x16" and ident_int16x8 = ident_create "int16x8" @@ -184,6 +185,8 @@ and ident_nil = ident_create "[]" and ident_cons = ident_create "::" and ident_none = ident_create "None" and ident_some = ident_create "Some" +and ident_null = ident_create "Null" +and ident_this = ident_create "This" let predef_jkind_annotation const = Option.map @@ -209,6 +212,9 @@ let option_argument_jkind = Jkind.value ~why:( let list_argument_jkind = Jkind.value ~why:( Type_argument {parent_path = path_list; position = 1; arity = 1}) +let or_null_argument_jkind = Jkind.non_null_value ~why:( + Type_argument {parent_path = path_option; position = 1; arity = 1}) + let mk_add_type add_type ?manifest type_ident ?(kind=Type_abstract Abstract_def) @@ -323,7 +329,8 @@ let build_initial_env add_type add_extension empty_env = ~kind:Type_open ~jkind:(Jkind.non_null_value ~why:Extensible_variant) |> add_type ident_extension_constructor - |> add_type ident_float + (* CR layouts v3: [float] should be non-null. *) + |> add_type ident_float ~jkind:(Jkind.value ~why:(Primitive ident_float)) |> add_type ident_floatarray |> add_type ident_int ~jkind:(Jkind.immediate ~why:(Primitive ident_int)) ~jkind_annotation:Immediate @@ -398,6 +405,18 @@ let build_initial_env add_type add_extension empty_env = |> add_type ident_unboxed_int64 ~jkind:(Jkind.bits64 ~why:(Primitive ident_unboxed_int64)) ~jkind_annotation:Bits64 + |> add_type1 ident_or_null + ~variance:Variance.covariant + (* CR layouts v3: Right now, since [float] can't be used in [or_null], + it is always separable. Revisit this once we finish the design. *) + ~separability:Separability.Ind + ~kind:(fun tvar -> + variant [cstr ident_null []; cstr ident_this [tvar, Unrestricted]] + [| Constructor_uniform_value, [| |]; + Constructor_uniform_value, [| or_null_argument_jkind |]; + |]) + ~jkind:(Jkind.value ~why:(Primitive ident_or_null)) + ~param_jkind:or_null_argument_jkind |> add_type ident_bytes |> add_type ident_unit ~kind:(variant diff --git a/testsuite/tests/lib-extensions/alpha_exports.ml b/testsuite/tests/lib-extensions/alpha_exports.ml index c283c905c35..b82ce88331d 100644 --- a/testsuite/tests/lib-extensions/alpha_exports.ml +++ b/testsuite/tests/lib-extensions/alpha_exports.ml @@ -9,3 +9,6 @@ *) open Stdlib_alpha + +(* Test that [Or_null] is exported. *) +type ('a : non_null_value) t = 'a Or_null.t