Reject coercions and variant spreads that treat bool and unit as variants
Why
bool and unit are declared as variants, with the constructors false, true and () and no @as, but their values are false, true and undefined. The coercion between a variant and a primitive left out bool but took unit for a variant whose constructor is the string "()", so (() :> string) compiled to nothing and left undefined typed as a string. The coercion from a closed polymorphic variant left out neither type and matched #"true" to the constructor true by name. A variant that spread either type built true, false and undefined, but matched and coerced them as strings.
Variant_coercion.is_bool_or_unit now keeps both types out of the coercions between a variant and a primitive, the coercion from a polymorphic variant (type_is_variant, renamed can_try_coerce_polyvariant_to_variant) and variant spreads. Ctype.subtype_rec also rejects a coercion between either type and another variant or record before the variant checks, whose messages described them as variants.
addedDiagnostic unit coerced to string
let s: string = (() :> string)let n = s->String.length
js
let n = undefined.length; let s;
diagnostic
A.res:1:18-29Type unit is not a subtype of string
addedDiagnostic polymorphic variant coerced to bool
let toBool = (p: [#"true" | #"false"]): bool => (p :> bool)let isFalse = toBool(#"false") == false
js
function toBool(p) { return p;} let isFalse = "false" === false;
diagnostic
A.res:1:50-58Type [#"false" | #"true"] is not a subtype of bool
addedDiagnostic bool spread into a variant
type t = | ...bool | Otherlet x: t = truelet r = switch x { | true => "yes" | false => "no" | Other => "other" }
js
r stays undefinedswitch (true) { case "false" : r = "no"; break; case "true" : r = "yes"; break; …}
diagnostic
A.res:1:15-18This type is not a valid type to spread. It's only possible to spread other variants.
modifiedDiagnostic unit coerced to int
let i: int = (() :> int)diagnostic
modifiedDiagnostic coercion suggested for unit
let u = (): unit => ()let s: string = u()
diagnostic
Review
- Does a working program stop compiling? One that coerces
unittostringor a polymorphic variant toboolorunit, which gave a value of another type than its own, or that spreadsboolorunitinto a variant, whose switches never matchedtrue,falseor(). A variant that only used its other constructors can declare them without the spread. - Which coercions still work?
boolandunitcoerce to themselves, from their aliases and from a private type defined as one, and a variantFalse | Truestill coerces tostring.option,listandresulthave type parameters, so these coercions never applied to them. No existing snapshot or test output changes.