-
Notifications
You must be signed in to change notification settings - Fork 442
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Allow type variables when spreading record type definitions (#6309)
* exploration * simple substitution of type parameters, and some cleanup * do deep substitution of type variables * allow renaming type variables, and spreading with instantiated type variables * cleanup * redo pairing type params logic * refactor: better type inference * Don't ask. * remove open * rename record type spread utils file * move more things related to record type spreads into dedicated file * test for deep sub * extend test a bit * changelog --------- Co-authored-by: Gabriel Nordeborn <gabbe.nord@gmail.com>
- Loading branch information
1 parent
adee7d0
commit 508e2b3
Showing
7 changed files
with
214 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
14 changes: 14 additions & 0 deletions
14
jscomp/build_tests/super_errors/expected/record_type_spreads_deep_sub.res.expected
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
|
||
[1;31mWe've found a bug for you![0m | ||
[36m/.../fixtures/record_type_spreads_deep_sub.res[0m:[2m8:9-21[0m | ||
|
||
6 [2m│[0m | ||
7 [2m│[0m let d: d = { | ||
[1;31m8[0m [2m│[0m x: Ok([1;31m"this errors"[0m), | ||
9 [2m│[0m } | ||
10 [2m│[0m | ||
|
||
This has type: [1;31mstring[0m | ||
Somewhere wanted: [1;33mint[0m | ||
|
||
You can convert [1;33mstring[0m to [1;33mint[0m with [1;33mBelt.Int.fromString[0m. |
9 changes: 9 additions & 0 deletions
9
jscomp/build_tests/super_errors/fixtures/record_type_spreads_deep_sub.res
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
// Checks that deep subsitution works as intended | ||
type t<'a, 'b> = {x: result<'a, 'b>} | ||
type d = { | ||
...t<int, int>, | ||
} | ||
|
||
let d: d = { | ||
x: Ok("this errors"), | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
module StringMap = Map.Make (String) | ||
|
||
let t_equals t1 t2 = t1.Types.level = t2.Types.level && t1.id = t2.id | ||
|
||
let substitute_types ~type_map (t : Types.type_expr) = | ||
if StringMap.is_empty type_map then t | ||
else | ||
let apply_substitution type_variable_name t = | ||
match StringMap.find_opt type_variable_name type_map with | ||
| None -> t | ||
| Some substituted_type -> substituted_type | ||
in | ||
let rec loop (t : Types.type_expr) = | ||
match t.desc with | ||
| Tlink t -> {t with desc = Tlink (loop t)} | ||
| Tvar (Some type_variable_name) -> | ||
apply_substitution type_variable_name t | ||
| Tvar None -> t | ||
| Tunivar _ -> t | ||
| Tconstr (path, args, _memo) -> | ||
{t with desc = Tconstr (path, args |> List.map loop, ref Types.Mnil)} | ||
| Tsubst t -> {t with desc = Tsubst (loop t)} | ||
| Tvariant rd -> {t with desc = Tvariant (row_desc rd)} | ||
| Tnil -> t | ||
| Tarrow (lbl, t1, t2, c) -> | ||
{t with desc = Tarrow (lbl, loop t1, loop t2, c)} | ||
| Ttuple tl -> {t with desc = Ttuple (tl |> List.map loop)} | ||
| Tobject (t, r) -> {t with desc = Tobject (loop t, r)} | ||
| Tfield (n, k, t1, t2) -> {t with desc = Tfield (n, k, loop t1, loop t2)} | ||
| Tpoly (t, []) -> loop t | ||
| Tpoly (t, tl) -> {t with desc = Tpoly (loop t, tl |> List.map loop)} | ||
| Tpackage (p, l, tl) -> | ||
{t with desc = Tpackage (p, l, tl |> List.map loop)} | ||
and row_desc (rd : Types.row_desc) = | ||
let row_fields = | ||
rd.row_fields |> List.map (fun (l, rf) -> (l, row_field rf)) | ||
in | ||
let row_more = loop rd.row_more in | ||
let row_name = | ||
match rd.row_name with | ||
| None -> None | ||
| Some (p, tl) -> Some (p, tl |> List.map loop) | ||
in | ||
{rd with row_fields; row_more; row_name} | ||
and row_field (rf : Types.row_field) = | ||
match rf with | ||
| Rpresent None -> rf | ||
| Rpresent (Some t) -> Rpresent (Some (loop t)) | ||
| Reither (b1, tl, b2, r) -> Reither (b1, tl |> List.map loop, b2, r) | ||
| Rabsent -> Rabsent | ||
in | ||
loop t | ||
|
||
let substitute_type_vars (type_vars : (string * Types.type_expr) list) | ||
(typ : Types.type_expr) = | ||
let type_map = | ||
type_vars | ||
|> List.fold_left | ||
(fun acc (tvar_name, tvar_typ) -> StringMap.add tvar_name tvar_typ acc) | ||
StringMap.empty | ||
in | ||
substitute_types ~type_map typ | ||
|
||
let has_type_spread (lbls : Typedtree.label_declaration list) = | ||
lbls | ||
|> List.exists (fun (l : Typedtree.label_declaration) -> | ||
match l with | ||
| {ld_name = {txt = "..."}} -> true | ||
| _ -> false) | ||
|
||
let extract_type_vars (type_params : Types.type_expr list) | ||
(typ : Types.type_expr) = | ||
(* The type variables applied to the record spread itself. *) | ||
let applied_type_vars = | ||
match Ctype.repr typ with | ||
| {desc = Tpoly ({desc = Tconstr (_, tvars, _)}, _)} -> tvars | ||
| _ -> [] | ||
in | ||
if List.length type_params = List.length applied_type_vars then | ||
(* Track which type param in the record we're spreading | ||
belongs to which type variable applied to the spread itself. *) | ||
let paired_type_vars = List.combine type_params applied_type_vars in | ||
paired_type_vars | ||
|> List.filter_map (fun (t, applied_tvar) -> | ||
match t.Types.desc with | ||
| Tvar (Some tname) -> Some (tname, applied_tvar) | ||
| _ -> None) | ||
else [] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters