Skip to content

Commit cedf6ac

Browse files
committed
handle inline records in variant coercion
1 parent e4f0585 commit cedf6ac

File tree

4 files changed

+64
-33
lines changed

4 files changed

+64
-33
lines changed

jscomp/ml/ctype.ml

Lines changed: 15 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3979,16 +3979,23 @@ let rec subtype_rec env trace t1 t2 cstrs =
39793979
Hashtbl.add constructor_map (Ident.name c.cd_id) c);
39803980
if c1 |> List.for_all (fun (c : Types.constructor_declaration) ->
39813981
match (c, Hashtbl.find_opt constructor_map (Ident.name c.cd_id)) with
3982-
| ( {Types.cd_args = Cstr_record _fields1},
3983-
Some {Types.cd_args = Cstr_record _fields2} ) ->
3984-
(* TODO: Reuse logic from record coercion *)
3985-
false
3982+
| ( {Types.cd_args = Cstr_record fields1; cd_attributes=c1_attributes},
3983+
Some {Types.cd_args = Cstr_record fields2; cd_attributes=c2_attributes} ) ->
3984+
if Variant_coercion.variant_representation_matches c1_attributes c2_attributes then
3985+
let violation, tl1, tl2 = Record_coercion.check_record_fields fields1 fields2 in
3986+
if violation then false
3987+
else
3988+
begin try
3989+
let lst = subtype_list env trace tl1 tl2 cstrs in
3990+
List.length lst = List.length cstrs
3991+
with | _ -> false end
3992+
else false
39863993
| ( {Types.cd_args = Cstr_tuple tl1; cd_attributes=c1_attributes},
39873994
Some {Types.cd_args = Cstr_tuple tl2; cd_attributes=c2_attributes} ) ->
39883995
if Variant_coercion.variant_representation_matches c1_attributes c2_attributes then
39893996
begin try
3990-
let lst = subtype_list env trace tl1 tl2 cstrs in
3991-
List.length lst = List.length cstrs
3997+
let lst = subtype_list env trace tl1 tl2 cstrs in
3998+
List.length lst = List.length cstrs
39923999
with | _ -> false end
39934000
else false
39944001
| _ -> false)
@@ -4003,30 +4010,8 @@ let rec subtype_rec env trace t1 t2 cstrs =
40034010
| Record_extension, Record_extension -> true
40044011
| _ -> false in
40054012
if same_repr then
4006-
let field_is_optional id repr = match repr with
4007-
| Record_optional_labels lbls -> List.mem (Ident.name id) lbls
4008-
| _ -> false in
4009-
let violation = ref false in
4010-
let label_decl_sub (acc1, acc2) ld2 =
4011-
match Ext_list.find_first fields1 (fun ld1 -> ld1.ld_id.name = ld2.ld_id.name) with
4012-
| Some ld1 ->
4013-
if field_is_optional ld1.ld_id repr1 <> (field_is_optional ld2.ld_id repr2) then
4014-
(* optional field can't be modified *)
4015-
violation := true;
4016-
let get_as (({txt}, payload) : Parsetree.attribute) =
4017-
if txt = "as" then Ast_payload.is_single_string payload
4018-
else None in
4019-
let get_as_name ld = match Ext_list.filter_map ld.ld_attributes get_as with
4020-
| [] -> ld.ld_id.name
4021-
| (s,_)::_ -> s in
4022-
if get_as_name ld1 <> get_as_name ld2 then violation := true;
4023-
ld1.ld_type :: acc1, ld2.ld_type :: acc2
4024-
| None ->
4025-
(* field must be present *)
4026-
violation := true;
4027-
(acc1, acc2) in
4028-
let tl1, tl2 = List.fold_left label_decl_sub ([], []) fields2 in
4029-
if !violation
4013+
let violation, tl1, tl2 = Record_coercion.check_record_fields ~repr1 ~repr2 fields1 fields2 in
4014+
if violation
40304015
then (trace, t1, t2, !univar_pairs)::cstrs
40314016
else
40324017
subtype_list env trace tl1 tl2 cstrs

jscomp/ml/record_coercion.ml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
let check_record_fields ?repr1 ?repr2 (fields1 : Types.label_declaration list)
2+
(fields2 : Types.label_declaration list) =
3+
let field_is_optional id repr =
4+
match repr with
5+
| Some (Types.Record_optional_labels lbls) -> List.mem (Ident.name id) lbls
6+
| _ -> false
7+
in
8+
let violation = ref false in
9+
let label_decl_sub (acc1, acc2) (ld2 : Types.label_declaration) =
10+
match
11+
Ext_list.find_first fields1 (fun ld1 -> ld1.ld_id.name = ld2.ld_id.name)
12+
with
13+
| Some ld1 ->
14+
if field_is_optional ld1.ld_id repr1 <> field_is_optional ld2.ld_id repr2
15+
then (* optional field can't be modified *)
16+
violation := true;
17+
let get_as (({txt}, payload) : Parsetree.attribute) =
18+
if txt = "as" then Ast_payload.is_single_string payload else None
19+
in
20+
let get_as_name (ld : Types.label_declaration) =
21+
match Ext_list.filter_map ld.ld_attributes get_as with
22+
| [] -> ld.ld_id.name
23+
| (s, _) :: _ -> s
24+
in
25+
if get_as_name ld1 <> get_as_name ld2 then violation := true;
26+
(ld1.ld_type :: acc1, ld2.ld_type :: acc2)
27+
| None ->
28+
(* field must be present *)
29+
violation := true;
30+
(acc1, acc2)
31+
in
32+
let tl1, tl2 = List.fold_left label_decl_sub ([], []) fields2 in
33+
(!violation, tl1, tl2)

jscomp/test/VariantCoercion.js

Lines changed: 8 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

jscomp/test/VariantCoercion.res

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,16 @@ let ii = Onef
1717
let dd = (ii :> float)
1818

1919
module CoerceVariants = {
20-
type a = One(int) | @as(1.1) Two
21-
type b = One(int) | @as(1.1) Two | Three
20+
@unboxed type a = One(int) | @as(1.1) Two | @as(null) T2
21+
@unboxed type b = One(int) | @as(1.1) Two | @as(null) T2 | Three
2222

2323
let a: a = Two
2424

2525
let b: b = (a :> b)
26+
27+
@tag("kind") type x = One({age: int, name?: string})
28+
@tag("kind") type y = One({age: int, name?: string}) | Two({two: string})
29+
30+
let x: x = One({age: 1})
31+
let y: y = (x :> y)
2632
}

0 commit comments

Comments
 (0)