-
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.
initial implementation of expanding variant type spreads
- Loading branch information
Showing
5 changed files
with
239 additions
and
18 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
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,134 @@ | ||
let mk_constructor_comes_from_spread_attr () : Parsetree.attribute = | ||
(Location.mknoloc "res.constructor_from_spread", PStr []) | ||
|
||
(* Spreads in variants are parsed as constructors named "...", with a single payload that's an identifier | ||
pointing to the type that's spread. We need to expand those constructors as soon as we can, before type | ||
checking. So, here we look for constructors named "...", look up their type, and add the constructors that | ||
type itself has. | ||
*) | ||
let expand_variant_spreads (env : Env.t) | ||
(sdecl_list : Parsetree.type_declaration list) = | ||
sdecl_list | ||
|> List.map (fun (sdecl : Parsetree.type_declaration) -> | ||
match sdecl with | ||
| {ptype_kind = Ptype_variant constructors} -> | ||
{ | ||
sdecl with | ||
ptype_kind = | ||
Ptype_variant | ||
(constructors | ||
|> List.map (fun (c : Parsetree.constructor_declaration) -> | ||
match c with | ||
| { | ||
pcd_name = {txt = "..."}; | ||
pcd_args = | ||
Pcstr_tuple | ||
[{ptyp_loc; ptyp_desc = Ptyp_constr (loc, [])}]; | ||
} -> ( | ||
try | ||
let _path, type_decl = | ||
Typetexp.find_type env ptyp_loc loc.txt | ||
in | ||
match type_decl with | ||
| {type_kind = Type_variant cstrs} -> | ||
let spread_constructor_name = | ||
"..." | ||
^ (Longident.flatten loc.txt | ||
|> String.concat ".") | ||
in | ||
{ | ||
c with | ||
pcd_name = | ||
{ | ||
c.pcd_name with | ||
txt = spread_constructor_name; | ||
}; | ||
} | ||
:: (cstrs | ||
|> List.map | ||
(fun | ||
(cstr : Types.constructor_declaration) | ||
: | ||
Parsetree.constructor_declaration | ||
-> | ||
{ | ||
(* This will mark this constructor as originating from a variant type spread. | ||
We use that hint to fill in the real, typed constructor arguments (if any) | ||
at a later stage when that information is available. *) | ||
pcd_attributes = | ||
mk_constructor_comes_from_spread_attr | ||
() | ||
:: cstr.cd_attributes; | ||
pcd_loc = cstr.cd_loc; | ||
pcd_res = None; | ||
(* It's important that we _don't_ fill in pcd_args here, since we have no way to produce | ||
a valid set of args for the parsetree at this stage. Inserting dummies here instead | ||
of later means that our dummies would end up being typechecked, and we don't want that. | ||
We'll fill this in with dummy info later. *) | ||
pcd_args = Pcstr_tuple []; | ||
pcd_name = | ||
Location.mkloc cstr.cd_id.name | ||
cstr.cd_loc; | ||
})) | ||
| _ -> [c] | ||
with _ -> | ||
(* Did not find type. Can't spread here, report as error that types need to be known before hand. *) | ||
[c]) | ||
| _ -> [c]) | ||
|> List.concat); | ||
} | ||
| _ -> sdecl) | ||
|
||
let constructor_is_from_spread (attrs : Parsetree.attributes) = | ||
attrs | ||
|> List.exists (fun (a : Parsetree.attribute) -> | ||
match a with | ||
| {txt = "res.constructor_from_spread"}, PStr [] -> true | ||
| _ -> false) | ||
|
||
(* The type checker matches lengths of constructor arguments etc between the parsetree and | ||
typed constructor definitions at various places. However, it doesn't use the parsetree | ||
definition for anything but the loc after having done the initial type check. So, here | ||
we add dummy constructor arguments that match the actual number of arguments that the | ||
type checker has told us each constructor has, so the various invariants check out. *) | ||
let expand_dummy_constructor_args (sdecl_list : Parsetree.type_declaration list) | ||
(decls : (Ident.t * Types.type_declaration) list) = | ||
List.map2 | ||
(fun sdecl (_, decl) -> | ||
match (sdecl, decl) with | ||
| ( {Parsetree.ptype_kind = Ptype_variant c1}, | ||
{Types.type_kind = Type_variant c2} ) -> | ||
{ | ||
sdecl with | ||
ptype_kind = | ||
Ptype_variant | ||
(c1 | ||
|> List.map (fun (c : Parsetree.constructor_declaration) -> | ||
if constructor_is_from_spread c.pcd_attributes then | ||
match | ||
c2 | ||
|> List.find_opt | ||
(fun (cc : Types.constructor_declaration) -> | ||
Ident.name cc.cd_id = c.pcd_name.txt) | ||
with | ||
| None -> c | ||
| Some constructor -> ( | ||
match constructor with | ||
| {cd_args = Cstr_tuple args} -> | ||
{ | ||
c with | ||
pcd_args = | ||
Pcstr_tuple | ||
(args | ||
|> List.map (fun _t -> | ||
{ | ||
Parsetree.ptyp_loc = c.pcd_loc; | ||
ptyp_attributes = []; | ||
ptyp_desc = Ptyp_any; | ||
})); | ||
} | ||
| _ -> c) | ||
else c)); | ||
} | ||
| _ -> sdecl) | ||
sdecl_list decls |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
module S = { | ||
type x = Foo | Bar | ||
type s = Five(int) | Six | ||
} | ||
|
||
type a = One(bool, S.x) | Two | ||
|
||
type b = | ...a | Three | Four | ...S.s | ||
|
||
let b1: b = Two | ||
let b2: b = One(true, Bar) | ||
|
||
let c: b = Five(2) | ||
|
||
let ddd: b = Six |
Large diffs are not rendered by default.
Oops, something went wrong.