Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changes/some-undefined-constant.fix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Keep `Some(U)` distinct from `None` when `U` is a constant case represented by `undefined` (`@as(undefined)`).
20 changes: 3 additions & 17 deletions compiler/ml/UntaggedVariants.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)` |
| --- | --- |
Expand All @@ -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`

Expand Down Expand Up @@ -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<u> = Some(U) // let x = undefined;
@inline(never) let mk = (v: u): option<u> => 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
Expand Down
5 changes: 4 additions & 1 deletion compiler/ml/lambda.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
Loading
Loading