Skip to content

Commit 6ffba9a

Browse files
committed
wip coerce polyvariant to variant
1 parent ea8a5f2 commit 6ffba9a

File tree

4 files changed

+83
-0
lines changed

4 files changed

+83
-0
lines changed

jscomp/ml/ctype.ml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3701,6 +3701,14 @@ let rec subtype_rec env trace t1 t2 cstrs =
37013701
with Exit ->
37023702
(trace, t1, t2, !univar_pairs)::cstrs
37033703
end
3704+
| (Tvariant {row_closed=true; row_fields}, Tconstr (_, [], _))
3705+
when extract_concrete_typedecl_opt env t2 |> Variant_coercion.type_is_variant ->
3706+
(match extract_concrete_typedecl env t2 with
3707+
| (_, _, {type_kind=Type_variant (constructors); type_attributes}) ->
3708+
(match Variant_coercion.can_coerce_polyvariant_to_variant ~row_fields ~constructors ~type_attributes with
3709+
| Ok _ -> cstrs
3710+
| Error _ -> (trace, t1, t2, !univar_pairs)::cstrs)
3711+
| _ -> (trace, t1, t2, !univar_pairs)::cstrs)
37043712
| Tvariant v, _ when
37053713
!variant_is_subtype env (row_repr v) t2
37063714
->

jscomp/ml/variant_coercion.ml

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,3 +151,48 @@ let variant_configuration_can_be_coerced_raises ~is_spread_context ~left_loc
151151
right_loc;
152152
error = TagName {left_tag; right_tag};
153153
}))
154+
155+
let can_coerce_polyvariant_to_variant ~row_fields ~constructors ~type_attributes
156+
=
157+
let polyvariant_runtime_representations =
158+
row_fields
159+
|> List.filter_map (fun (label, (field : Types.row_field)) ->
160+
match field with
161+
| Rpresent None -> Some label
162+
| _ -> None)
163+
in
164+
if List.length polyvariant_runtime_representations <> List.length row_fields
165+
then
166+
(* Error: At least one polyvariant constructor has a payload. Cannot have payloads. *)
167+
Error `PolyvariantConstructorHasPayload
168+
else
169+
let is_unboxed = Ast_untagged_variants.has_untagged type_attributes in
170+
if
171+
List.for_all
172+
(fun polyvariant_value ->
173+
constructors
174+
|> List.exists (fun (c : Types.constructor_declaration) ->
175+
let constructor_name = Ident.name c.cd_id in
176+
match
177+
Ast_untagged_variants.process_tag_type c.cd_attributes
178+
with
179+
| Some (String as_runtime_string) ->
180+
(* `@as("")`, does the configured string match the polyvariant value? *)
181+
as_runtime_string = polyvariant_value
182+
| Some (Untagged StringType) when is_unboxed ->
183+
(* An unboxed variant that has a catch all case will match _any_ string, so it matches anything here. *)
184+
true
185+
| Some _ ->
186+
(* Any other `@as` can't match since it's by definition not a string *)
187+
false
188+
| None ->
189+
(* No `@as` means the runtime representation will be the constructor name as a string. *)
190+
polyvariant_value = constructor_name))
191+
polyvariant_runtime_representations
192+
then Ok ()
193+
else Error `Unknown
194+
195+
let type_is_variant (typ: (Path.t * Path.t * Types.type_declaration) option) =
196+
match typ with
197+
| Some (_, _, {type_kind = Type_variant _; _}) -> true
198+
| _ -> false

jscomp/test/VariantCoercion.js

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

jscomp/test/VariantCoercion.res

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,3 +92,23 @@ module CoerceFromBigintToVariant = {
9292
let c = 120n
9393
let cc: mixed = (c :> mixed)
9494
}
95+
96+
module CoerceFromPolyvariantToVariant = {
97+
type simple = [#One | #Two]
98+
type simpleP = One | Two
99+
100+
let simple: simple = #One
101+
let simpleP = (simple :> simpleP)
102+
103+
type withAs = [#One | #two]
104+
type withAsP = One | @as("two") Two
105+
106+
let withAs: withAs = #One
107+
let withAsP = (withAs :> withAsP)
108+
109+
type withMoreVariantConstructors = [#One | #two]
110+
type withMoreVariantConstructorsP = One | @as("two") Two | Three
111+
112+
let withMoreVariantConstructors: withMoreVariantConstructors = #One
113+
let withMoreVariantConstructorsP = (withMoreVariantConstructors :> withMoreVariantConstructorsP)
114+
}

0 commit comments

Comments
 (0)