From b5836debf55b777f0b7b0928ae4607ef2af51285 Mon Sep 17 00:00:00 2001 From: Christoph Knittel Date: Sat, 24 Oct 2026 06:10:33 +0000 Subject: [PATCH] Box Some of a constant case represented by undefined Why: `Some(U)` with `@as(undefined) U` compiled to plain `undefined` when `U` was a constant, so it was `None`, while `Some(v)` with `v = U` passed at run time stayed distinct (#8954). A constant constructor whose literal is `undefined` is now a none for `const_some_none_depth`, so `Some` of it is boxed like `Some(None)`. This holds for untagged and tagged variants alike. The spec's known bug is gone, and rule 12 states the case. JS modified: Example: @unboxed type u = | @as(undefined) U | V(string) let x: option = Some(U) Before (js): let x = undefined; After (js): let x = Primitive_option.some(undefined); Fixes: #8954 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01CMkrYaktctdKc4M41WF36t Signed-off-by: Christoph Knittel --- changes/some-undefined-constant.fix.md | 1 + compiler/ml/UntaggedVariants.md | 20 +-- compiler/ml/lambda.ml | 5 +- .../tests/src/untagged_variants_spec_test.mjs | 166 +++++++++--------- .../tests/src/untagged_variants_spec_test.res | 2 + 5 files changed, 95 insertions(+), 99 deletions(-) create mode 100644 changes/some-undefined-constant.fix.md diff --git a/changes/some-undefined-constant.fix.md b/changes/some-undefined-constant.fix.md new file mode 100644 index 0000000000..36c0d2d6fb --- /dev/null +++ b/changes/some-undefined-constant.fix.md @@ -0,0 +1 @@ +- Keep `Some(U)` distinct from `None` when `U` is a constant case represented by `undefined` (`@as(undefined)`). diff --git a/compiler/ml/UntaggedVariants.md b/compiler/ml/UntaggedVariants.md index a3e21a8352..30dc9eabd5 100644 --- a/compiler/ml/UntaggedVariants.md +++ b/compiler/ml/UntaggedVariants.md @@ -291,7 +291,8 @@ as `Primitive_option.some(v)` otherwise. An untagged variant cannot hold | an abstract type without it, a type variable, `option<_>` | yes | | a type whose declaration is not on the load path | yes | -An `@as(null)` case does not prevent unboxing; `Some(N)` is `null`. +An `@as(null)` case does not prevent unboxing; `Some(N)` is `null`. `Some(U)` of an `@as(undefined)` case is boxed, as a constant +too, so it stays distinct from `None`. | Declaration | `Some(v)` | | --- | --- | @@ -313,7 +314,7 @@ abstract types `@notUndefined`; `Blob.t` and `File.t` are not marked. - Tests: `spec_test` rule 12; `UntaggedVariants.res` (`OptionUnboxingHeuristic`); `tests/tests/src/option_wrapping_test.res`; `tests/build_tests/option_transitive_dependency`. -- Shaped by #6222, #6233, #8719. +- Shaped by #6222, #6233, #8719, #8954. ### 13. `@notUndefined` @@ -356,21 +357,6 @@ cases with a payload (see Known bugs). Each was observed with the compiler built from this tree; the programs are compiled with `bsc` and run in Node 25. -**`Some` of a constant `undefined` case is `None`.** Rule 12 boxes `Some(v)` -for a variant with an `@as(undefined)` case, but a constant argument skips -the boxing. - -```rescript -@unboxed type u = | @as(undefined) U | V(string) -let x: option = Some(U) // let x = undefined; -@inline(never) let mk = (v: u): option => Some(v) -Console.log2(x == None, mk(U) == None) // true false -``` - -`lambda.ml:const_some_none_depth` returns "not a none" for every -`Const_constructor`, including one whose literal is `undefined`; that this is -the cause is conjectured, not established. - **Overlapping instance classes follow declaration order** (#8938). ```rescript diff --git a/compiler/ml/lambda.ml b/compiler/ml/lambda.ml index b27e16e241..329348f00d 100644 --- a/compiler/ml/lambda.ml +++ b/compiler/ml/lambda.ml @@ -585,7 +585,10 @@ let rec const_some_none_depth (c : structured_constant) : int = | Const_some v -> let depth = const_some_none_depth v in if depth < 0 then depth else depth + 1 - | Const_module_alias | Const_js_undefined _ -> 0 + | Const_module_alias | Const_js_undefined _ + (* A constructor represented by [undefined], [@as(undefined)] *) + | Const_constructor {literal = Some Undefined} -> + 0 | _ -> -1 (* A constant that has to be built at run time rather than shared. A [Some] diff --git a/tests/tests/src/untagged_variants_spec_test.mjs b/tests/tests/src/untagged_variants_spec_test.mjs index fe9506bae4..e71c04de94 100644 --- a/tests/tests/src/untagged_variants_spec_test.mjs +++ b/tests/tests/src/untagged_variants_spec_test.mjs @@ -190,6 +190,8 @@ function someWithUndefined(v) { return Primitive_option.some(v); } +let someConstantUndefined = Primitive_option.some(undefined); + function someWithOpaque(v) { return Primitive_option.some(v); } @@ -228,163 +230,164 @@ function someWithDefined(v) { Mocha.describe("Untagged_variants_spec_test", () => { Mocha.test("rule 1: @unboxed on one constructor of a tagged variant", () => { - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 224, characters 7-14", id(3), 3); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 225, characters 7-14", id({ + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 225, characters 7-14", id(3), 3); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 226, characters 7-14", id({ TAG: "MB", x: 1 }), { TAG: "MB", x: 1 }); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 226, characters 7-14", id("MC"), "MC"); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 227, characters 7-14", mixedName(id(3)), "MA"); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 228, characters 7-14", mixedName(id({ + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 227, characters 7-14", id("MC"), "MC"); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 228, characters 7-14", mixedName(id(3)), "MA"); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 229, characters 7-14", mixedName(id({ TAG: "MB", x: 1 })), "MB"); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 229, characters 7-14", mixedName(id("MC")), "MC"); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 230, characters 7-14", mixedName(id("MC")), "MC"); }); Mocha.test("rule 2: representation of each case", () => { - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 233, characters 7-14", id("Named"), "Named"); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 234, characters 7-14", id("s"), "s"); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 235, characters 7-14", id(1), 1); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 236, characters 7-14", id(2.5), 2.5); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 237, characters 7-14", id(3n), 3n); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 238, characters 7-14", id(true), true); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 239, characters 7-14", id(null), null); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 240, characters 7-14", id(undefined), undefined); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 241, characters 7-14", id({ + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 234, characters 7-14", id("Named"), "Named"); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 235, characters 7-14", id("s"), "s"); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 236, characters 7-14", id(1), 1); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 237, characters 7-14", id(2.5), 2.5); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 238, characters 7-14", id(3n), 3n); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 239, characters 7-14", id(true), true); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 240, characters 7-14", id(null), null); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 241, characters 7-14", id(undefined), undefined); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 242, characters 7-14", id({ x: 1 }), { x: 1 }); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 242, characters 7-14", id({ + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 243, characters 7-14", id({ x: 1 }), { x: 1 }); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 243, characters 7-14", id("v"), "v"); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 244, characters 7-14", id("v"), "v"); }); Mocha.test("rule 4: typeof kinds", () => { - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 247, characters 7-14", kindName(id("a")), "string"); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 248, characters 7-14", kindName(id(1.5)), "number"); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 249, characters 7-14", kindName(id(1n)), "bigint"); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 250, characters 7-14", kindName(id(false)), "bool"); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 251, characters 7-14", kindName(id(x => x)), "function"); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 252, characters 7-14", kindName(id({ + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 248, characters 7-14", kindName(id("a")), "string"); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 249, characters 7-14", kindName(id(1.5)), "number"); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 250, characters 7-14", kindName(id(1n)), "bigint"); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 251, characters 7-14", kindName(id(false)), "bool"); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 252, characters 7-14", kindName(id(x => x)), "function"); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 253, characters 7-14", kindName(id({ x: 1 })), "object"); }); Mocha.test("rule 4: tuples are array cases, dicts are object cases", () => { - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 256, characters 7-14", tupleName(id([ + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 257, characters 7-14", tupleName(id([ 1, "a" ])), "array"); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 257, characters 7-14", tupleName(id({ + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 258, characters 7-14", tupleName(id({ a: 1 })), "object"); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 258, characters 7-14", aliasDictName(id({ + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 259, characters 7-14", aliasDictName(id({ a: 1 })), "object"); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 259, characters 7-14", aliasDictName(id([1])), "array"); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 260, characters 7-14", aliasDictName(id([1])), "array"); }); Mocha.test("rules 4 and 5: instance cases", () => { - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 263, characters 7-14", instanceName(id(new Date())), "Date"); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 264, characters 7-14", instanceName(id(/a/)), "RegExp"); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 265, characters 7-14", instanceName(id(Promise.resolve(1))), "Promise"); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 266, characters 7-14", instanceName(id(new Map())), "Map"); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 267, characters 7-14", instanceName(id(new Set())), "Set"); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 268, characters 7-14", instanceName(id(new WeakMap())), "WeakMap"); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 269, characters 7-14", instanceName(id(new WeakSet())), "WeakSet"); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 264, characters 7-14", instanceName(id(new Date())), "Date"); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 265, characters 7-14", instanceName(id(/a/)), "RegExp"); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 266, characters 7-14", instanceName(id(Promise.resolve(1))), "Promise"); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 267, characters 7-14", instanceName(id(new Map())), "Map"); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 268, characters 7-14", instanceName(id(new Set())), "Set"); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 269, characters 7-14", instanceName(id(new WeakMap())), "WeakMap"); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 270, characters 7-14", instanceName(id(new WeakSet())), "WeakSet"); let buffer = new ArrayBuffer(8); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 271, characters 7-14", instanceName(id(buffer)), "ArrayBuffer"); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 272, characters 7-14", instanceName(id(new DataView(buffer))), "DataView"); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 273, characters 7-14", instanceName(id(new Float64Array([1.0]))), "Float64Array"); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 274, characters 7-14", instanceName(id("a")), "string"); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 272, characters 7-14", instanceName(id(buffer)), "ArrayBuffer"); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 273, characters 7-14", instanceName(id(new DataView(buffer))), "DataView"); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 274, characters 7-14", instanceName(id(new Float64Array([1.0]))), "Float64Array"); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 275, characters 7-14", instanceName(id("a")), "string"); }); Mocha.test("rule 4: the unknown case receives every non-literal value", () => { - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 278, characters 7-14", unknownName(id(null)), "null"); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 279, characters 7-14", unknownName(id("none")), "none"); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 280, characters 7-14", unknownName(id({hd: 1, tl: /* [] */0})), "list"); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 281, characters 7-14", unknownName(id(/* [] */0)), "list"); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 279, characters 7-14", unknownName(id(null)), "null"); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 280, characters 7-14", unknownName(id("none")), "none"); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 281, characters 7-14", unknownName(id({hd: 1, tl: /* [] */0})), "list"); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 282, characters 7-14", unknownName(id(/* [] */0)), "list"); }); Mocha.test("rule 7: a payload value equal to a literal is that literal's case", () => { - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 285, characters 7-14", overlapStringName(id("a")), "LitA"); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 286, characters 7-14", overlapStringName(id("b")), "AnyString"); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 287, characters 7-14", overlapNumberName(id(1)), "LitOne"); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 288, characters 7-14", overlapNumberName(id(2)), "AnyInt"); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 289, characters 7-14", overlapBoolName(id(true)), "LitTrue"); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 290, characters 7-14", overlapBoolName(id(false)), "AnyBool"); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 291, characters 7-14", overlapNullName(id(null)), "LitNull"); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 292, characters 7-14", overlapNullName(id({ + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 286, characters 7-14", overlapStringName(id("a")), "LitA"); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 287, characters 7-14", overlapStringName(id("b")), "AnyString"); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 288, characters 7-14", overlapNumberName(id(1)), "LitOne"); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 289, characters 7-14", overlapNumberName(id(2)), "AnyInt"); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 290, characters 7-14", overlapBoolName(id(true)), "LitTrue"); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 291, characters 7-14", overlapBoolName(id(false)), "AnyBool"); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 292, characters 7-14", overlapNullName(id(null)), "LitNull"); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 293, characters 7-14", overlapNullName(id({ x: 1 })), "AnyObject"); }); Mocha.test("rule 8: instance checks precede typeof checks", () => { - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 296, characters 7-14", objectFirstName(id(new Date())), "Date"); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 297, characters 7-14", objectFirstName(id({ + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 297, characters 7-14", objectFirstName(id(new Date())), "Date"); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 298, characters 7-14", objectFirstName(id({ x: 1 })), "object"); }); Mocha.test("rule 8: an int case receives any number", () => { - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 301, characters 7-14", intOrStringName(id(1)), "int"); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 302, characters 7-14", intOrStringName(id(1.5)), "int"); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 303, characters 7-14", intOrStringName(id("1")), "string"); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 302, characters 7-14", intOrStringName(id(1)), "int"); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 303, characters 7-14", intOrStringName(id(1.5)), "int"); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 304, characters 7-14", intOrStringName(id("1")), "string"); }); Mocha.test("rule 9: null and arrays are not objects", () => { - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 307, characters 7-14", guardedName(id([])), "array"); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 308, characters 7-14", guardedName(id(null)), "null"); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 309, characters 7-14", guardedName(id({ + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 308, characters 7-14", guardedName(id([])), "array"); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 309, characters 7-14", guardedName(id(null)), "null"); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 310, characters 7-14", guardedName(id({ x: 1 })), "object"); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 310, characters 7-14", guardedObjectOnly(id([])), "other"); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 311, characters 7-14", guardedObjectOnly(id(null)), "other"); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 312, characters 7-14", guardedObjectOnly(id({ + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 311, characters 7-14", guardedObjectOnly(id([])), "other"); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 312, characters 7-14", guardedObjectOnly(id(null)), "other"); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 313, characters 7-14", guardedObjectOnly(id({ x: 1 })), "object"); }); Mocha.test("rule 10: a value outside every case lands in the unchecked branch", () => { - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 316, characters 7-14", intOrStringName(true), "string"); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 317, characters 7-14", twoInstancesName({ + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 317, characters 7-14", intOrStringName(true), "string"); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 318, characters 7-14", twoInstancesName({ x: 1 }), "RegExp"); let thenable = ({then: (resolve) => resolve(1)}); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 319, characters 7-14", promiseOrFloatName(thenable), "float"); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 320, characters 7-14", promiseOrFloatName(thenable), "float"); }); Mocha.test("rule 12: option of an untagged variant", () => { - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 323, characters 7-14", someWithNull(null), null); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 324, characters 7-14", someWithNull(null) === undefined, false); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 325, characters 7-14", someWithUndefined(id(undefined)) === undefined, false); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 326, characters 7-14", someWithUndefined("a"), "a"); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 329, characters 7-14", someWithOpaque(undefined) === undefined, false); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 330, characters 7-14", someWithVariable(undefined) === undefined, false); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 331, characters 7-14", someWithBlob(undefined) === undefined, false); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 332, characters 7-14", someWithDate(undefined) === undefined, true); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 333, characters 7-14", someWithRecord(undefined) === undefined, true); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 334, characters 7-14", someWithInlineRecord(undefined) === undefined, false); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 335, characters 7-14", someWithUnboxedOption(undefined) === undefined, false); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 336, characters 7-14", someWithUnboxedString(undefined) === undefined, true); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 324, characters 7-14", someWithNull(null), null); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 325, characters 7-14", someWithNull(null) === undefined, false); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 326, characters 7-14", someWithUndefined(id(undefined)) === undefined, false); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 327, characters 7-14", someConstantUndefined === undefined, false); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 328, characters 7-14", someWithUndefined("a"), "a"); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 331, characters 7-14", someWithOpaque(undefined) === undefined, false); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 332, characters 7-14", someWithVariable(undefined) === undefined, false); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 333, characters 7-14", someWithBlob(undefined) === undefined, false); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 334, characters 7-14", someWithDate(undefined) === undefined, true); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 335, characters 7-14", someWithRecord(undefined) === undefined, true); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 336, characters 7-14", someWithInlineRecord(undefined) === undefined, false); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 337, characters 7-14", someWithUnboxedOption(undefined) === undefined, false); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 338, characters 7-14", someWithUnboxedString(undefined) === undefined, true); }); Mocha.test("rule 13: @notUndefined unboxes option of a variant holding the type", () => { - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 340, characters 7-14", someWithDefined(undefined) === undefined, true); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 341, characters 7-14", someWithDefined(5), 5); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 342, characters 7-14", someWithDefined(undefined) === undefined, true); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 343, characters 7-14", someWithDefined(5), 5); }); Mocha.test("rule 14: @tag and @as on a tagged variant", () => { - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 345, characters 7-14", id({ + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 347, characters 7-14", id({ kind: "TA", x: 1 }), { kind: "TA", x: 1 }); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 346, characters 7-14", id({ + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 348, characters 7-14", id({ kind: "bee", y: 1 }), { kind: "bee", y: 1 }); - Test_utils.eq("File \"untagged_variants_spec_test.res\", line 347, characters 7-14", id("TC"), "TC"); + Test_utils.eq("File \"untagged_variants_spec_test.res\", line 349, characters 7-14", id("TC"), "TC"); }); }); @@ -408,6 +411,7 @@ export { promiseOrFloatName, someWithNull, someWithUndefined, + someConstantUndefined, someWithOpaque, someWithVariable, someWithBlob, diff --git a/tests/tests/src/untagged_variants_spec_test.res b/tests/tests/src/untagged_variants_spec_test.res index 9976b193c9..6ae8b9780d 100644 --- a/tests/tests/src/untagged_variants_spec_test.res +++ b/tests/tests/src/untagged_variants_spec_test.res @@ -185,6 +185,7 @@ let promiseOrFloatName = v => @inline(never) let someWithNull = (v: withNull): option => Some(v) @unboxed type withUndefined = | @as(undefined) WUndefined | WUString(string) @inline(never) let someWithUndefined = (v: withUndefined): option => Some(v) +let someConstantUndefined: option = Some(WUndefined) type opaque @unboxed type withOpaque = | @as("n") ONone | OPaque(opaque) @inline(never) let someWithOpaque = (v: withOpaque): option => Some(v) @@ -323,6 +324,7 @@ describe(__MODULE__, () => { eq(__LOC__, raw(someWithNull(WNull)), Nullable.null) eq(__LOC__, someWithNull(WNull) == None, false) eq(__LOC__, someWithUndefined(id(WUndefined)) == None, false) + eq(__LOC__, someConstantUndefined == None, false) eq(__LOC__, raw(someWithUndefined(WUString("a"))), "a") // An undefined payload shows whether `Some` boxes: a boxed `Some` stays // distinct from `None`, an unboxed one does not.