Skip to content

Commit

Permalink
Add type checking for explicit type arguments in new/call
Browse files Browse the repository at this point in the history
Summary:
In the previous diff, I added support for parsing call and new expressions with
explicit type arguments, e.g., `f<T>(x)`. This diff continues that work by
adding support to the type system.

During the AST pass, the type arguments are converted from annotations into
types and stored along with the value arguments inside a CallT and a
ConstructorT use.

Currently, when calling a polymorphic function or instantiating a polymorphic
class, the type parameters are implicitly instantiated and their types are
inferred.

Using the type arguments, we can instantiate a polymorphic type explicitly,
which gives developers a way to constrain the types.

This capability is also very useful for adding annotations for exports. For
example, if I export a `new Map()` instance, the implicit instantiations
become missing annotation errors. With this feature, it becomes possible to
export `new Map<K,V>()` instead.

Fixes #926
Fixes #2630

Reviewed By: avikchaudhuri

Differential Revision: D7657630

fbshipit-source-id: b829db79d3503e40990f0fb1cf145303457e5aa9
  • Loading branch information
samwgoldman authored and facebook-github-bot committed May 2, 2018
1 parent 40fbcdd commit 73145b2
Show file tree
Hide file tree
Showing 15 changed files with 903 additions and 186 deletions.
12 changes: 11 additions & 1 deletion src/typing/debug_js.ml
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,10 @@ and _json_of_use_t_impl json_cx t = Hh_json.(
"type", _json_of_t json_cx t
]

| ConstructorT (_, _, args, t) -> [
| ConstructorT (_, _, targs, args, t) -> [
"typeArgs", (match targs with
| None -> JSON_Null
| Some ts -> JSON_Array (List.map (_json_of_t json_cx) ts));
"argTypes", JSON_Array (List.map (json_of_funcallarg json_cx) args);
"type", _json_of_t json_cx t
]
Expand Down Expand Up @@ -1033,6 +1036,7 @@ and json_of_funtype_impl json_cx {
and json_of_funcalltype json_cx = check_depth json_of_funcalltype_impl json_cx
and json_of_funcalltype_impl json_cx {
call_this_t;
call_targs;
call_args_tlist;
call_tout;
call_closure_t;
Expand All @@ -1041,6 +1045,9 @@ and json_of_funcalltype_impl json_cx {
let arg_types = List.map (json_of_funcallarg json_cx) call_args_tlist in
JSON_Object ([
"thisType", _json_of_t json_cx call_this_t;
"typeArgs", (match call_targs with
| None -> JSON_Null
| Some ts -> JSON_Array (List.map (_json_of_t json_cx) ts));
"argTypes", JSON_Array arg_types;
"tout", _json_of_t json_cx call_tout;
"closureIndex", int_ call_closure_t;
Expand Down Expand Up @@ -2577,6 +2584,9 @@ let dump_flow_error =
spf "ETypeParamArity (%s, %d)" (string_of_loc loc) expected
| ETypeParamMinArity (loc, expected) ->
spf "ETypeParamMinArity (%s, %d)" (string_of_loc loc) expected
| ECallTypeArity { call_loc; is_new; reason_arity; expected_arity } ->
spf "ECallTypeArity { call_loc=%s; is_new=%b; reason_arity=%s; expected_arity=%d; }"
(string_of_loc call_loc) is_new (dump_reason cx reason_arity) expected_arity
| ETooManyTypeArgs (reason_tapp, reason_arity, maximum_arity) ->
spf "ETooManyTypeArgs (%s, %s, %d)"
(dump_reason cx reason_tapp)
Expand Down
20 changes: 20 additions & 0 deletions src/typing/flow_error.ml
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,12 @@ type error_message =
| EIdxUse2 of reason
| EUnexpectedThisType of Loc.t
| ETypeParamArity of Loc.t * int
| ECallTypeArity of {
call_loc: Loc.t;
is_new: bool;
reason_arity: reason;
expected_arity: int;
}
| ETypeParamMinArity of Loc.t * int
| ETooManyTypeArgs of reason * reason * int
| ETooFewTypeArgs of reason * reason * int
Expand Down Expand Up @@ -322,6 +328,7 @@ let util_use_op_of_msg nope util = function
| EIdxUse2 (_)
| EUnexpectedThisType (_)
| ETypeParamArity (_, _)
| ECallTypeArity _
| ETypeParamMinArity (_, _)
| ETooManyTypeArgs (_, _, _)
| ETooFewTypeArgs (_, _, _)
Expand Down Expand Up @@ -1254,6 +1261,19 @@ let rec error_of_msg ~trace_reasons ~source_file =
text (spf "%n type %s." n (if n == 1 then "argument" else "arguments"));
]

| ECallTypeArity { call_loc; is_new; reason_arity; expected_arity = n } ->
let use = if is_new then "construct " else "call " in
if n = 0 then
mk_error ~trace_infos call_loc [
text "Cannot "; text use; text "non-polymorphic "; ref reason_arity;
text " with type arguments.";
]
else
mk_error ~trace_infos call_loc [
text "Cannot "; text use; ref reason_arity; text " without exactly ";
text (spf "%n type argument%s." n (if n == 1 then "" else "s"));
]

| EValueUsedAsType reasons ->
let (value, _) = reasons in
mk_error ~trace_infos (loc_of_reason value) [
Expand Down
Loading

0 comments on commit 73145b2

Please sign in to comment.