Skip to content

Commit 503a54b

Browse files
committed
Explore type coercion for records.
Rough initial version. Error messages do not explain why sub-typing fails.
1 parent 03234b8 commit 503a54b

File tree

5 files changed

+51
-2
lines changed

5 files changed

+51
-2
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
1313
# 11.0.0-alpha.2 (Unreleased)
1414

15+
## :rocket: Main New Features
16+
17+
- Add support for type coercion `:>` for records. https://github.com/rescript-lang/rescript-compiler/pull/5721
18+
1519
# 11.0.0-alpha.1
1620

1721
## :rocket: Main New Features

jscomp/ml/ctype.ml

+27-1
Original file line numberDiff line numberDiff line change
@@ -3951,7 +3951,33 @@ let rec subtype_rec env trace t1 t2 cstrs =
39513951
end
39523952
| (Tconstr(p1, _, _), _) when generic_private_abbrev env p1 ->
39533953
subtype_rec env trace (expand_abbrev_opt env t1) t2 cstrs
3954-
(* | (_, Tconstr(p2, _, _)) when generic_private_abbrev false env p2 ->
3954+
| (Tconstr(_, [], _), Tconstr(_, [], _)) -> (* type coercion for records *)
3955+
(match extract_concrete_typedecl env t1, extract_concrete_typedecl env t2 with
3956+
| (_, _, {type_kind=Type_record (fields1, repr1)}), (_, _, {type_kind=Type_record (fields2, repr2)}) ->
3957+
let field_is_optional id repr = match repr with
3958+
| Record_optional_labels lbls -> List.mem (Ident.name id) lbls
3959+
| _ -> false in
3960+
let violation = ref false in
3961+
let label_decl_sub (acc1, acc2) ld2 =
3962+
match fields1 |> List.find_opt (fun ld1 -> Ident.name ld1.ld_id = Ident.name ld2.ld_id) with
3963+
| Some ld1 ->
3964+
if field_is_optional ld1.ld_id repr1 && not (field_is_optional ld2.ld_id repr2) then
3965+
(* optional field can't be cast to non-optional one *)
3966+
violation := true;
3967+
ld1.ld_type :: acc1, ld2.ld_type :: acc2
3968+
| None ->
3969+
(* field must be present *)
3970+
violation := true;
3971+
(acc1, acc2) in
3972+
let tl1, tl2 = List.fold_left label_decl_sub ([], []) fields2 in
3973+
if !violation
3974+
then (trace, t1, t2, !univar_pairs)::cstrs
3975+
else
3976+
subtype_list env trace tl1 tl2 cstrs
3977+
| _ -> (trace, t1, t2, !univar_pairs)::cstrs
3978+
| exception Not_found -> (trace, t1, t2, !univar_pairs)::cstrs
3979+
)
3980+
(* | (_, Tconstr(p2, _, _)) when generic_private_abbrev false env p2 ->
39553981
subtype_rec env trace t1 (expand_abbrev_opt env t2) cstrs *)
39563982
| (Tobject (f1, _), Tobject (f2, _))
39573983
when is_Tvar (object_row f1) && is_Tvar (object_row f2) ->

jscomp/test/RecordCoercion.js

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/* This output is empty. Its source's type definitions, externals and/or unused code got optimized away. */

jscomp/test/RecordCoercion.res

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
type r1 = {a: option<int>, b: int}
2+
3+
type r2 = {a?: int, b: int}
4+
5+
type r3 = {a?: int}
6+
7+
type r4 = {}
8+
9+
let _ = (x: r1) => (x :> r2) // Convert a from mandatory to optional
10+
// let _ = (x: r2) => (x :> r1) can't turn an optional field to a mandatory one
11+
let _ = (x: r2) => (x :> r3) // can omit field
12+
let _ = (x: r1) => (x :> r3) // omit field and convert from mandatory to optional
13+
let _ = (x: r3) => (x :> r4) // omit everything
14+
15+
type nested1 = {n: r1, extra: int}
16+
type nested2 = {n: r2}
17+
let _ = (x: nested1) => (x :> nested2)

jscomp/test/build.ninja

+2-1
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)