Skip to content
Permalink
Browse files

`_` as implicit instantiation

Summary:
This diff adds typechecking semantics for `_`. It is intended to be used when the user doesn't know how to annotate a call to a constructor or function. Specifically, this will help when annotating exported calls that don't require annotations on _every_ type parameter.

`_` allows the user to explicitly ask for a type variable to be instantiate for a type parameter. We have two relevant functions in flow that are used when calling a polymorphic function or constructor: `instantiate_poly` and `instantiate_poly_with_targs`.

`instantiate_poly` creates fresh tvars for each parameter and then calls `instantiate_poly_with_targs`. This is how we create type variables to calls to polymorphic functions that don't specify explicit arguments.

Our current implementation is "all or nothing": you either provide all concrete type arguments, or we implicitly instantiate all of them. `_` changes this to allow the user to specify only some and not others.

Reviewed By: samwgoldman

Differential Revision: D10109218

fbshipit-source-id: 0b3fbcbaed8481aded5ad29967a902bdb26bf5b0
  • Loading branch information...
jbrown215 authored and facebook-github-bot committed Oct 10, 2018
1 parent 1a23ba9 commit ec70da4510d3a092fa933081c083bd0e513d0518
@@ -102,6 +102,7 @@ type reason_desc =
| RThis
| RThisType
| RExistential
| RImplicitInstantiation
| RTooFewArgs
| RTooFewArgsExpectedRest
| RConstructorReturn
@@ -456,6 +457,7 @@ let rec string_of_desc = function
| RThis -> "this"
| RThisType -> "`this` type"
| RExistential -> "existential"
| RImplicitInstantiation -> "implicit instantiation"
| RTooFewArgs -> "undefined (too few arguments)"
| RTooFewArgsExpectedRest ->
"undefined (too few arguments, expected default/rest parameters)"
@@ -651,6 +653,7 @@ let is_instantiable_reason r =
| RTypeParam _
| RThisType
| RExistential -> true
| RImplicitInstantiation -> true
| _ -> false

(* TODO: Property accesses create unresolved tvars to hold results, even when
@@ -1135,6 +1138,7 @@ let classification_of_reason r = match desc_of_reason ~unwrap:true r with
| RThis
| RThisType
| RExistential
| RImplicitInstantiation
| RTooFewArgs
| RTooFewArgsExpectedRest
| RConstructorReturn
@@ -51,6 +51,7 @@ type reason_desc =
| RThis
| RThisType
| RExistential
| RImplicitInstantiation
| RTooFewArgs
| RTooFewArgsExpectedRest
| RConstructorReturn
@@ -89,6 +89,12 @@ and _json_of_tvar json_cx id = Hh_json.(
]
)

and _json_of_targ json_cx t = Hh_json.(
JSON_Object (match t with
| ImplicitArg _ -> ["kind", JSON_String "implicit"]
| ExplicitArg t -> ["kind", JSON_String "explicit"; "type", _json_of_t json_cx t]
))

and _json_of_t_impl json_cx t = Hh_json.(
JSON_Object ([
"reason", json_of_reason ~strip_root:json_cx.strip_root (reason_of_t t);
@@ -482,7 +488,7 @@ and _json_of_use_t_impl json_cx t = Hh_json.(
| ConstructorT (_, _, targs, args, t) -> [
"typeArgs", (match targs with
| None -> JSON_Null
| Some ts -> JSON_Array (List.map (_json_of_t json_cx) ts));
| Some ts -> JSON_Array (List.map (_json_of_targ json_cx) ts));
"argTypes", JSON_Array (List.map (json_of_funcallarg json_cx) args);
"type", _json_of_t json_cx t
]
@@ -1068,7 +1074,7 @@ and json_of_funcalltype_impl json_cx {
"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));
| Some ts -> JSON_Array (List.map (_json_of_targ json_cx) ts));
"argTypes", JSON_Array arg_types;
"tout", _json_of_t json_cx call_tout;
"closureIndex", int_ call_closure_t;
@@ -2804,5 +2810,3 @@ let dump_flow_error =
(dump_reason cx reason_op)
| ESignatureVerification sve ->
spf "ESignatureVerification (%s)" (Signature_builder_deps.Error.to_string sve)
| EImplicitInstantiationNotYetSupported loc ->
spf "EImplicitInstantiationNotYetSupported (%s)" (string_of_loc loc)
@@ -170,7 +170,6 @@ type error_message =
| EInexactSpread of reason * reason
| EDeprecatedCallSyntax of Loc.t
| ESignatureVerification of Signature_builder_deps.Error.t
| EImplicitInstantiationNotYetSupported of Loc.t

and binding_error =
| ENameAlreadyBound
@@ -398,7 +397,6 @@ let util_use_op_of_msg nope util = function
| EInexactSpread _
| EDeprecatedCallSyntax _
| ESignatureVerification _
| EImplicitInstantiationNotYetSupported _
-> nope

(* Rank scores for signals of different strength on an x^2 scale so that greater
@@ -1699,21 +1697,25 @@ let rec error_of_msg ~trace_reasons ~source_file =
[text "Use an array literal instead of "; code "new Array(...)"; text "."]

| EMissingAnnotation (reason, trace_reasons) ->
let tail = match (desc_of_reason reason) with
let default = [text "Missing type annotation for "; desc reason; text "."] in
let msg = match (desc_of_reason reason) with
| RTypeParam (_, (RImplicitInstantiation, _), _) ->
[text "Please use a concrete type annotation instead of "; code "_";
text " in this position."]
| RTypeParam (_, (reason_op_desc, reason_op_loc), (reason_tapp_desc, reason_tapp_loc)) ->
let reason_op = mk_reason reason_op_desc (reason_op_loc |> ALoc.of_loc) in
let reason_tapp = mk_reason reason_tapp_desc (reason_tapp_loc |> ALoc.of_loc) in
[text " "; desc reason; text " is a type parameter declared in "; ref reason_tapp;
default @ [text " "; desc reason; text " is a type parameter declared in "; ref reason_tapp;
text " and was implicitly instantiated at "; ref reason_op; text "."]
| _ -> [] in
| _ -> default in

(* We don't collect trace info in the assert_ground_visitor because traces
* represent tests of lower bounds to upper bounds, and the assert_ground
* visitor is just visiting types. Instead, we collect a list of types we
* visited to get to the missing annotation error and report that as the
* trace *)
let trace_infos = List.map info_of_reason trace_reasons in
mk_error ~trace_infos (aloc_of_reason reason)
([text "Missing type annotation for "; desc reason; text "."] @ tail)
mk_error ~trace_infos (aloc_of_reason reason) msg

| EBindingError (binding_error, loc, x, entry) ->
let desc =
@@ -2073,10 +2075,6 @@ let rec error_of_msg ~trace_reasons ~source_file =
mk_error ~trace_infos ~kind:(LintError Lints.DeprecatedCallSyntax) (loc |> ALoc.of_loc)
[text "Deprecated $call syntax. Use callable property syntax instead."]

| EImplicitInstantiationNotYetSupported loc ->
mk_error ~trace_infos (loc |> ALoc.of_loc)
[text "Implicit instantiation via "; code "_"; text " is not supported yet."]

| EUnusedSuppression loc ->
mk_error ~trace_infos (loc |> ALoc.of_loc)
[text "Unused suppression comment."]
@@ -2413,7 +2413,7 @@ let rec __flow cx ((l: Type.t), (u: Type.use_t)) trace =
| None ->
add_output cx ~trace (FlowError.ETooFewTypeArgs (reason, reason, 1));
AnyT.at fun_loc
| Some [t] ->
| Some [ExplicitArg t] ->
let kind, return_t = begin match l with
| CustomFunT (_, TypeAssertIs) -> Context.Is, BoolT.at fun_loc
| CustomFunT (_, TypeAssertThrows) -> Context.Throws, t
@@ -4000,13 +4000,13 @@ let rec __flow cx ((l: Type.t), (u: Type.use_t)) trace =
~use_op ~reason_op ~reason_tapp ~cache:arg_reasons (tparams_loc,ids,t) in
rec_flow cx trace (t_, u)
| Some targs ->
let t_ = instantiate_poly_with_targs cx trace (tparams_loc, ids, t) targs
let t_ = instantiate_poly_call_or_new cx trace (tparams_loc, ids, t) targs
~use_op ~reason_op ~reason_tapp in
rec_flow cx trace (t_,
CallT (use_op, reason_op, {calltype with call_targs = None}))
end
| ConstructorT (use_op, reason_op, Some targs, args, tout) ->
let t_ = instantiate_poly_with_targs cx trace (tparams_loc, ids, t) targs
let t_ = instantiate_poly_call_or_new cx trace (tparams_loc, ids, t) targs
~use_op ~reason_op ~reason_tapp in
rec_flow cx trace (t_, ConstructorT (use_op, reason_op, None, args, tout))
| _ ->
@@ -7628,6 +7628,30 @@ and poly_minimum_arity =
in
Nel.fold_left f 0

(* Instantiate a polymorphic definition given tparam instantiations in a Call or
* New expression. *)
and instantiate_poly_call_or_new
cx
trace
~use_op
~reason_op
~reason_tapp
?cache
?errs_ref
(tparams_loc, xs, t)
targs
=
let _, ts = Nel.fold_left (fun (targs, ts) typeparam -> match targs with
| [] -> ([], ts)
| (ExplicitArg t)::targs -> (targs, t::ts)
| (ImplicitArg aloc)::targs ->
let reason = mk_reason RImplicitInstantiation aloc in
let t = ImplicitTypeArgument.mk_targ cx typeparam reason reason_tapp in
(targs, t::ts)) (targs, []) xs
in
instantiate_poly_with_targs cx trace ~use_op ~reason_op ~reason_tapp ?cache ?errs_ref
(tparams_loc, xs, t) (List.rev ts)

(* Instantiate a polymorphic definition given type arguments. *)
and instantiate_poly_with_targs
cx
@@ -48,11 +48,11 @@ let convert_tparam_instantiations cx tparams_map instantiations =
begin match ast with
| Explicit ast ->
let (_, t), _ as tast = Anno.convert cx tparams_map ast in
loop (t::ts) ((Explicit tast)::tasts) cx tparams_map asts
loop ((ExplicitArg t)::ts) ((Explicit tast)::tasts) cx tparams_map asts
| Implicit loc ->
let (_, t), _ as tast = Anno.error_type cx loc
(Flow_error.EImplicitInstantiationNotYetSupported loc) in
loop (t::ts) ((Explicit tast)::tasts) cx tparams_map asts
(* TODO: (jmbrown) create a tvar for type-at-pos here *)
loop ((ImplicitArg (ALoc.of_loc loc))::ts)
((Implicit (loc, AnyT.at (loc |> ALoc.of_loc)))::tasts) cx tparams_map asts
end
in
loop [] [] cx tparams_map instantiations
@@ -62,7 +62,12 @@ let convert_targs cx = function
| Some (loc, args) ->
let targts, targs_ast = convert_tparam_instantiations cx SMap.empty args in
List.iter (fun t ->
Type_table.set_targ (Context.type_table cx) (TypeUtil.loc_of_t t) t
match t with
| ExplicitArg t ->
Type_table.set_targ (Context.type_table cx) (TypeUtil.loc_of_t t) t
| ImplicitArg _ ->
(* TODO: Figure out to do with implicit instantiation in the type table *)
()
) targts;
Some targts, Some (loc, targs_ast)

@@ -2983,7 +2988,8 @@ and expression_ ~is_cond cx loc e : (Loc.t, Loc.t * Type.t) Ast.Expression.t =
replace_reason_const (RCustom "array length") reason in
Flow.flow_t cx (arg_t, DefT (length_reason, NumT AnyLiteral));
let t, targs = match targ_t with
| Some (loc, ast, t) -> t, Some (loc, [ast])
| Some (loc, ast, ExplicitArg t) -> t, Some (loc, [ast])
| Some (_, _, ImplicitArg _)
| None ->
let element_reason =
replace_reason_const (RCustom "array element") reason in
@@ -390,7 +390,7 @@ module rec TypeTerm : sig
| ReposUseT of reason * bool (* use_desc *) * use_op * t

(* operations on runtime types, such as classes and functions *)
| ConstructorT of use_op * reason * t list option * call_arg list * t
| ConstructorT of use_op * reason * targ list option * call_arg list * t
| SuperT of use_op * reason * derived_type
| ImplementsT of use_op * t
| MixinT of reason * t
@@ -762,14 +762,16 @@ module rec TypeTerm : sig
(* Used by CallT and similar constructors *)
and funcalltype = {
call_this_t: t;
call_targs: t list option;
call_targs: targ list option;
call_args_tlist: call_arg list;
call_tout: t;
call_closure_t: int;
call_strict_arity: bool;
}

and opt_funcalltype = t * t list option * call_arg list * int * bool
and targ = ImplicitArg of ALoc.t | ExplicitArg of t

and opt_funcalltype = t * targ list option * call_arg list * int * bool

and call_arg =
| Arg of t
@@ -1642,7 +1642,7 @@ let mk_declare_class_sig =
iface_sig
else
let reason = replace_reason_const RDefaultConstructor reason in
Class_sig.add_default_constructor reason iface_sig
add_default_constructor reason iface_sig
in
iface_sig, self,
{ Ast.Statement.DeclareClass.
@@ -161,6 +161,14 @@ class virtual ['a] t = object(self)

method virtual tvar: Context.t -> 'a -> Reason.t -> Constraint.ident -> Constraint.ident

method targ cx map_cx t =
match t with
| ImplicitArg _ -> t
| ExplicitArg t' ->
let t'' = self#type_ cx map_cx t' in
if t'' == t' then t
else ExplicitArg t''

method def_type cx map_cx t =
match t with
| NumT _
@@ -657,7 +665,7 @@ class virtual ['a] t_with_uses = object(self)
if t'' == t' then t
else ReposUseT (r, use_desc, use_op, t'')
| ConstructorT (op, r, targs, args, t') ->
let targs' = OptionUtils.ident_map (ListUtils.ident_map (self#type_ cx map_cx)) targs in
let targs' = OptionUtils.ident_map (ListUtils.ident_map (self#targ cx map_cx)) targs in
let args' = ListUtils.ident_map (self#call_arg cx map_cx) args in
let t'' = self#type_ cx map_cx t' in
if targs' == targs && args' == args && t'' == t' then t
@@ -990,7 +998,7 @@ class virtual ['a] t_with_uses = object(self)

method private opt_fun_call_type cx map_cx ((this, targs, args, clos, strict) as t) =
let this' = self#type_ cx map_cx this in
let targs' = OptionUtils.ident_map (ListUtils.ident_map (self#type_ cx map_cx)) targs in
let targs' = OptionUtils.ident_map (ListUtils.ident_map (self#targ cx map_cx)) targs in
let args' = ListUtils.ident_map (self#call_arg cx map_cx) args in
if this' == this && targs' == targs && args' == args
then t
@@ -1088,7 +1096,8 @@ class virtual ['a] t_with_uses = object(self)
call_strict_arity;
} = t in
let call_this_t' = self#type_ cx map_cx call_this_t in
let call_targs' = OptionUtils.ident_map (ListUtils.ident_map (self#type_ cx map_cx)) call_targs in
let call_targs' = OptionUtils.ident_map (ListUtils.ident_map (self#targ cx map_cx)) call_targs
in
let call_args_tlist' = ListUtils.ident_map (self#call_arg cx map_cx) call_args_tlist in
let call_tout' = self#type_ cx map_cx call_tout in
if (
@@ -35,6 +35,7 @@ class virtual ['a] t :
method virtual props : Context.t -> 'a -> Type.Properties.id -> Type.Properties.id
method selector :
Context.t -> 'a -> Type.selector -> Type.selector
method targ : Context.t -> 'a -> Type.targ -> Type.targ
method virtual tvar :
Context.t -> 'a -> Reason.t -> Constraint.ident -> Constraint.ident
method type_ : Context.t -> 'a -> Type.t -> Type.t
@@ -178,6 +179,7 @@ class virtual ['a] t_with_uses :
Type.React.CreateClass.stack_head * Type.t list *
Type.React.CreateClass.spec Type.React.CreateClass.maybe_known
list
method targ : Context.t -> 'a -> Type.targ -> Type.targ
method virtual tvar :
Context.t -> 'a -> Reason.t -> Constraint.ident -> Constraint.ident
method type_ : Context.t -> 'a -> Type.t -> Type.t
@@ -183,6 +183,10 @@ class ['a] t = object(self)
| IdxWrapper t ->
self#type_ cx pole acc t

method targ cx pole acc = function
| ImplicitArg _ -> acc
| ExplicitArg t -> self#type_ cx pole acc t


method private defer_use_type cx acc = function
| DestructuringT (_, s) -> self#selector cx acc s
@@ -319,7 +323,7 @@ class ['a] t = object(self)
| ReposUseT (_, _, _, t) -> self#type_ cx pole_TODO acc t

| ConstructorT (_, _, targs, args, t) ->
let acc = Option.fold ~init:acc ~f:(List.fold_left (self#type_ cx pole_TODO)) targs in
let acc = Option.fold ~init:acc ~f:(List.fold_left (self#targ cx pole_TODO)) targs in
let acc = List.fold_left (self#call_arg cx) acc args in
let acc = self#type_ cx pole_TODO acc t in
acc
@@ -784,7 +788,7 @@ class ['a] t = object(self)
call_strict_arity = _;
} = call in
let acc = self#type_ cx pole_TODO acc call_this_t in
let acc = self#opt (self#list (self#type_ cx pole_TODO)) acc call_targs in
let acc = self#opt (self#list (self#targ cx pole_TODO)) acc call_targs in
let acc = self#list (self#call_arg cx) acc call_args_tlist in
let acc = self#type_ cx pole_TODO acc call_tout in
acc
@@ -9,6 +9,7 @@ class ['a] t: object
(* Only exposing a few methods for now. *)
method type_ : Context.t -> Type.polarity -> 'a -> Type.t -> 'a
method def_type : Context.t -> Type.polarity -> 'a -> Type.def_t -> 'a
method targ : Context.t -> Type.polarity -> 'a -> Type.targ -> 'a
method use_type_ : Context.t -> 'a -> Type.use_t -> 'a
method tvar : Context.t -> Type.polarity -> 'a -> Reason.reason -> Constraint.ident -> 'a
method props : Context.t -> Type.polarity -> 'a -> Type.Properties.id -> 'a

0 comments on commit ec70da4

Please sign in to comment.
You can’t perform that action at this time.