From 69b856f972c575e31ec2f6b3ac3fe4909c8bab87 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Mon, 25 Dec 2023 12:35:12 +0100 Subject: [PATCH 1/2] allow coercing int and float in addition to strings with unboxed variants with catch all cases --- jscomp/ml/ctype.ml | 10 +++++----- jscomp/ml/variant_coercion.ml | 9 ++++++--- jscomp/test/VariantCoercion.js | 20 ++++++++++++++++++++ jscomp/test/VariantCoercion.res | 24 ++++++++++++++++++++++++ 4 files changed, 55 insertions(+), 8 deletions(-) diff --git a/jscomp/ml/ctype.ml b/jscomp/ml/ctype.ml index 7dd114fed8..d615cefb75 100644 --- a/jscomp/ml/ctype.ml +++ b/jscomp/ml/ctype.ml @@ -3959,20 +3959,20 @@ let rec subtype_rec env trace t1 t2 cstrs = subtype_rec env trace (expand_abbrev_opt env t1) t2 cstrs | (Tconstr(p1, [], _), Tconstr(p2, [], _)) when Path.same p1 Predef.path_int && Path.same p2 Predef.path_float -> cstrs - | (Tconstr(path, [], _), Tconstr(_, [], _)) when Path.same path Predef.path_string && + | (Tconstr(path, [], _), Tconstr(_, [], _)) when Variant_coercion.can_coerce_primitive path && extract_concrete_typedecl env t2 |> Variant_coercion.can_try_coerce_variant_to_primitive |> Option.is_some -> - (* type coercion for strings to elgible unboxed variants: + (* type coercion for primitives (int/float/string) to elgible unboxed variants: - must be unboxed - - must have a constructor case with a string payload *) + - must have a constructor case with a supported and matching primitive payload *) (match Variant_coercion.can_try_coerce_variant_to_primitive (extract_concrete_typedecl env t2) with | Some (constructors, true) -> - if constructors |> Variant_coercion.variant_has_catch_all_string_case then + if Variant_coercion.variant_has_catch_all_case constructors (fun p -> Path.same p path) then cstrs else (trace, t1, t2, !univar_pairs)::cstrs | _ -> (trace, t1, t2, !univar_pairs)::cstrs) - | (Tconstr(_, [], _), Tconstr(path, [], _)) when Variant_coercion.can_coerce_path path && + | (Tconstr(_, [], _), Tconstr(path, [], _)) when Variant_coercion.can_coerce_primitive path && extract_concrete_typedecl env t1 |> Variant_coercion.can_try_coerce_variant_to_primitive |> Option.is_some -> (* type coercion for variants to primitives *) diff --git a/jscomp/ml/variant_coercion.ml b/jscomp/ml/variant_coercion.ml index 7912c76f0a..17494a2e96 100644 --- a/jscomp/ml/variant_coercion.ml +++ b/jscomp/ml/variant_coercion.ml @@ -1,7 +1,7 @@ (* TODO: Improve error messages? Say why we can't coerce. *) (* Right now we only allow coercing to primitives string/int/float *) -let can_coerce_path (path : Path.t) = +let can_coerce_primitive (path : Path.t) = Path.same path Predef.path_string || Path.same path Predef.path_int || Path.same path Predef.path_float @@ -9,17 +9,20 @@ let can_coerce_path (path : Path.t) = let check_paths_same p1 p2 target_path = Path.same p1 target_path && Path.same p2 target_path -let variant_has_catch_all_string_case (constructors : Types.constructor_declaration list) = +let variant_has_catch_all_case (constructors : Types.constructor_declaration list) path_is_same = let has_catch_all_string_case (c : Types.constructor_declaration) = let args = c.cd_args in match args with | Cstr_tuple [{desc = Tconstr (p, [], _)}] -> - Path.same p Predef.path_string + path_is_same p | _ -> false in constructors |> List.exists has_catch_all_string_case +let variant_has_relevant_primitive_catch_all (constructors : Types.constructor_declaration list) = + variant_has_catch_all_case constructors can_coerce_primitive + (* Checks if every case of the variant has the same runtime representation as the target type. *) let variant_has_same_runtime_representation_as_target ~(targetPath : Path.t) ~unboxed (constructors : Types.constructor_declaration list) = diff --git a/jscomp/test/VariantCoercion.js b/jscomp/test/VariantCoercion.js index 6c471714c9..0918041039 100644 --- a/jscomp/test/VariantCoercion.js +++ b/jscomp/test/VariantCoercion.js @@ -44,6 +44,24 @@ var CoerceFromStringToVariant = { cc: c$1 }; +var CoerceFromIntToVariant = { + a: 100, + aa: 1, + b: 100, + bb: 1, + c: 120, + cc: 120 +}; + +var CoerceFromFloatToVariant = { + a: 100, + aa: 1, + b: 100, + bb: 1, + c: 120, + cc: 120 +}; + var a$2 = "Three"; var b = "Three"; @@ -65,4 +83,6 @@ exports.dd = dd; exports.CoerceVariants = CoerceVariants; exports.CoerceWithPayload = CoerceWithPayload; exports.CoerceFromStringToVariant = CoerceFromStringToVariant; +exports.CoerceFromIntToVariant = CoerceFromIntToVariant; +exports.CoerceFromFloatToVariant = CoerceFromFloatToVariant; /* No side effect */ diff --git a/jscomp/test/VariantCoercion.res b/jscomp/test/VariantCoercion.res index 9aee78a3e0..2c653f2a9d 100644 --- a/jscomp/test/VariantCoercion.res +++ b/jscomp/test/VariantCoercion.res @@ -56,3 +56,27 @@ module CoerceFromStringToVariant = { let c = "Hi" let cc: mixed = (c :> mixed) } + +module CoerceFromIntToVariant = { + @unboxed type ints = Int(int) | @as(1) First | @as(2) Second | @as(3) Third + let a = 100 + let aa = 1 + let b: ints = (a :> ints) + let bb: ints = (aa :> ints) + + @unboxed type mixed = Int(int) | @as(1) One | @as(null) Null | Two + let c = 120 + let cc: mixed = (c :> mixed) +} + +module CoerceFromFloatToVariant = { + @unboxed type floats = Float(float) | @as(1.) First | @as(2.) Second | @as(3.) Third + let a = 100. + let aa = 1. + let b: floats = (a :> floats) + let bb: floats = (aa :> floats) + + @unboxed type mixed = Float(float) | @as(1.) One | @as(null) Null | Two + let c = 120. + let cc: mixed = (c :> mixed) +} From 3efaa2ae60a504a5c39d83341bc3b8c526c5ff8d Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Wed, 27 Dec 2023 09:31:47 +0100 Subject: [PATCH 2/2] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f8af33ca5..4db9fdfdcf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ #### :rocket: New Feature - GenType: support `@deriving(accessors)` outputs. https://github.com/rescript-lang/rescript-compiler/pull/6537 +- Allow coercing ints and floats to unboxed variants that have a catch-all unboxed int or float case. https://github.com/rescript-lang/rescript-compiler/pull/6540 # 11.0.0-rc.8