diff --git a/src/typing/debug_js.ml b/src/typing/debug_js.ml index bb051e309bb..31afcc5d38c 100644 --- a/src/typing/debug_js.ml +++ b/src/typing/debug_js.ml @@ -170,11 +170,18 @@ and _json_of_t_impl json_cx t = Hh_json.( | NullProtoT _ | ObjProtoT _ | FunProtoT _ - | FunProtoApplyT _ + | FunProtoApplyT (_, None, _) | FunProtoBindT _ | FunProtoCallT _ -> [] + | FunProtoApplyT (_, Some t, call_args_tlist) -> + let arg_types = Core_list.map ~f:(json_of_funcallarg json_cx) call_args_tlist in + [ + "thisType", _json_of_t json_cx t; + "argTypes", JSON_Array arg_types; + ] + | DefT (_, _, FunT (static, proto, funtype)) -> [ "static", _json_of_t json_cx static; "prototype", _json_of_t json_cx proto; @@ -1794,9 +1801,11 @@ let rec dump_t_ (depth, tvars) cx t = | NullProtoT _ | ObjProtoT _ | FunProtoT _ - | FunProtoApplyT _ + | FunProtoApplyT (_, None, _) | FunProtoBindT _ | FunProtoCallT _ -> p t + | FunProtoApplyT (_, Some arg, _ (* TODO *)) -> + p ~extra:(kid arg) t | DefT (_, trust, PolyT (_, tps, c, id)) -> p ~trust:(Some trust) ~extra:(spf "%s [%s] #%d" (kid c) (String.concat "; " (Core_list.map ~f:(fun tp -> tp.name) (Nel.to_list tps))) diff --git a/src/typing/flow_js.ml b/src/typing/flow_js.ml index de3ec3688e5..7d70d25f3fb 100644 --- a/src/typing/flow_js.ml +++ b/src/typing/flow_js.ml @@ -5586,8 +5586,12 @@ let rec __flow cx ((l: Type.t), (u: Type.use_t)) trace = (*******************************************) (* resolves the arguments... *) - | FunProtoApplyT lreason, - CallT (use_op, reason_op, ({call_this_t = func; call_args_tlist; _} as funtype)) -> + | FunProtoApplyT (lreason, arg, arg_tlist), + CallT (use_op, reason_op, ({call_this_t; call_args_tlist; _} as funtype)) -> + let func = match arg with + | Some t -> t + | None -> call_this_t + in (* Drop the specific AST derived argument reasons. Our new arguments come * from arbitrary positions in the array. *) let use_op = match use_op with @@ -5597,6 +5601,8 @@ let rec __flow cx ((l: Type.t), (u: Type.use_t)) trace = | _ -> use_op in + let call_args_tlist = arg_tlist @ call_args_tlist in + begin match call_args_tlist with (* func.apply() *) | [] -> @@ -5710,6 +5716,13 @@ let rec __flow cx ((l: Type.t), (u: Type.use_t)) trace = ) call_args_tlist; rec_flow_t cx trace (l, call_tout) + | FunProtoApplyT (lreason, Some this_t, _), BindT (_, _, { call_this_t; call_args_tlist; call_tout; _ }, _) -> + rec_flow_t cx trace (call_this_t, this_t); + rec_flow_t cx trace (FunProtoApplyT (lreason, Some this_t, call_args_tlist), call_tout) + + | FunProtoApplyT (lreason, _, _), BindT (_, _, { call_this_t; call_args_tlist; call_tout; _ }, _) -> + rec_flow_t cx trace (FunProtoApplyT (lreason, Some call_this_t, call_args_tlist), call_tout) + | _, BindT (_, _, { call_tout; _ }, true) -> rec_flow_t cx trace (l, call_tout) @@ -6512,7 +6525,7 @@ let rec __flow cx ((l: Type.t), (u: Type.use_t)) trace = rec_flow cx trace (AnyT.make AnyError lreason, u); (* Special cases of FunT *) - | FunProtoApplyT reason, _ + | FunProtoApplyT (reason, _, _), _ | FunProtoBindT reason, _ | FunProtoCallT reason, _ -> rec_flow cx trace (FunProtoT reason, u) @@ -7296,9 +7309,13 @@ and any_propagated_use cx trace use_op any l = covariant_flow ~use_op instance; true + | FunProtoApplyT (_, Some t, _ (* TODO *)) -> + contravariant_flow ~use_op t; + true + (* These types have no negative positions in their lower bounds *) | ExistsT _ - | FunProtoApplyT _ + | FunProtoApplyT (_, None, _) | FunProtoBindT _ | FunProtoCallT _ | FunProtoT _ diff --git a/src/typing/resolvableTypeJob.ml b/src/typing/resolvableTypeJob.ml index b2381056218..753da788708 100644 --- a/src/typing/resolvableTypeJob.ml +++ b/src/typing/resolvableTypeJob.ml @@ -247,7 +247,7 @@ and collect_of_type ?log_unresolved cx acc = function | FunProtoBindT _ | FunProtoCallT _ - | FunProtoApplyT _ + | FunProtoApplyT (_, None, _) | FunProtoT _ | NullProtoT _ | ObjProtoT _ @@ -258,6 +258,11 @@ and collect_of_type ?log_unresolved cx acc = function -> acc + | FunProtoApplyT (_, Some t, ts) -> + let arg_types = + Core_list.map ~f:(function Arg t | SpreadArg t -> t) ts in + collect_of_types ?log_unresolved cx acc (arg_types @ [t]) + and collect_of_destructor ?log_unresolved cx acc = function | NonMaybeType -> acc | PropertyType _ -> acc diff --git a/src/typing/type.ml b/src/typing/type.ml index f53a38b1ce8..1bce1a9c6c2 100644 --- a/src/typing/type.ml +++ b/src/typing/type.ml @@ -93,7 +93,7 @@ module rec TypeTerm : sig appears as an upper bound of an object type, otherwise the same. *) | NullProtoT of reason - | FunProtoApplyT of reason (* Function.prototype.apply *) + | FunProtoApplyT of reason * t option (* this type *) * call_arg list (* Function.prototype.apply *) | FunProtoBindT of reason (* Function.prototype.bind *) | FunProtoCallT of reason (* Function.prototype.call *) @@ -2140,7 +2140,7 @@ end = struct | ExistsT reason -> reason | InternalT (ExtendsT (reason, _, _)) -> reason | FunProtoT reason -> reason - | FunProtoApplyT reason -> reason + | FunProtoApplyT (reason, _, _) -> reason | FunProtoBindT reason -> reason | FunProtoCallT reason -> reason | KeysT (reason, _) -> reason @@ -2303,7 +2303,7 @@ end = struct | ExactT (reason, t) -> ExactT (f reason, t) | ExistsT reason -> ExistsT (f reason) | InternalT (ExtendsT (reason, t1, t2)) -> InternalT (ExtendsT (f reason, t1, t2)) - | FunProtoApplyT (reason) -> FunProtoApplyT (f reason) + | FunProtoApplyT (reason, t1, args) -> FunProtoApplyT (f reason, t1, args) | FunProtoT (reason) -> FunProtoT (f reason) | FunProtoBindT (reason) -> FunProtoBindT (f reason) | FunProtoCallT (reason) -> FunProtoCallT (f reason) diff --git a/src/typing/type_annotation.ml b/src/typing/type_annotation.ml index 746fdb97f20..c7ca2284359 100644 --- a/src/typing/type_annotation.ml +++ b/src/typing/type_annotation.ml @@ -854,7 +854,7 @@ let rec convert cx tparams_map = Ast.Type.(function | "Function$Prototype$Apply" -> check_type_arg_arity cx loc t_ast targs 0 (fun () -> let reason = mk_reason RFunctionType loc in - reconstruct_ast (FunProtoApplyT reason) None + reconstruct_ast (FunProtoApplyT (reason, None, [])) None ) | "Function$Prototype$Bind" -> diff --git a/src/typing/type_mapper.ml b/src/typing/type_mapper.ml index 04f7ce8c309..df95fd6b210 100644 --- a/src/typing/type_mapper.ml +++ b/src/typing/type_mapper.ml @@ -105,9 +105,14 @@ class virtual ['a] t = object(self) | FunProtoT _ | ObjProtoT _ | NullProtoT _ - | FunProtoApplyT _ + | FunProtoApplyT (_, None, _) | FunProtoBindT _ | FunProtoCallT _ -> t + | FunProtoApplyT (r, Some t', call_args_tlist) -> + let t'' = self#type_ cx map_cx t' in + let call_args_tlist' = ListUtils.ident_map (self#call_arg cx map_cx) call_args_tlist in + if t'' == t' && call_args_tlist' == call_args_tlist' then t + else FunProtoApplyT (r, Some t'', call_args_tlist') | AnyWithLowerBoundT t' -> let t'' = self#type_ cx map_cx t' in if t'' == t' then t @@ -211,6 +216,17 @@ class virtual ['a] t = object(self) if t'' == t' then t else ExplicitArg t'' + method call_arg cx map_cx t = + match t with + | Arg t' -> + let t'' = self#type_ cx map_cx t' in + if t'' == t' then t + else Arg t'' + | SpreadArg t' -> + let t'' = self#type_ cx map_cx t' in + if t'' == t' then t + else SpreadArg t'' + method def_type cx map_cx t = match t with | NumT _ @@ -1154,17 +1170,6 @@ class virtual ['a] t_with_uses = object(self) call_strict_arity; } - method call_arg cx map_cx t = - match t with - | Arg t' -> - let t'' = self#type_ cx map_cx t' in - if t'' == t' then t - else Arg t'' - | SpreadArg t' -> - let t'' = self#type_ cx map_cx t' in - if t'' == t' then t - else SpreadArg t'' - method lookup_kind cx map_cx t = match t with | Strict _ -> t diff --git a/src/typing/type_mapper.mli b/src/typing/type_mapper.mli index 187e573e435..165e437fae3 100644 --- a/src/typing/type_mapper.mli +++ b/src/typing/type_mapper.mli @@ -6,6 +6,8 @@ *) class virtual ['a] t : object + method call_arg : + Context.t -> 'a -> Type.call_arg -> Type.call_arg method arr_type : Context.t -> 'a -> Type.arrtype -> Type.arrtype method bounds : @@ -49,8 +51,6 @@ end class virtual ['a] t_with_uses : object inherit ['a] t - method call_arg : - Context.t -> 'a -> Type.call_arg -> Type.call_arg method choice_use_tool : Context.t -> 'a -> Type.choice_use_tool -> Type.choice_use_tool diff --git a/src/typing/type_visitor.ml b/src/typing/type_visitor.ml index fc14976c7e4..d67a8863a04 100644 --- a/src/typing/type_visitor.ml +++ b/src/typing/type_visitor.ml @@ -32,13 +32,18 @@ class ['a] t = object(self) acc | FunProtoT _ - | FunProtoApplyT _ + | FunProtoApplyT (_, None, _) | FunProtoBindT _ | FunProtoCallT _ | ObjProtoT _ | NullProtoT _ -> acc + | FunProtoApplyT (_, Some t, call_args_tlist) -> + let acc = self#type_ cx pole acc t in + let acc = self#list (self#call_arg cx) acc call_args_tlist in + acc + | CustomFunT (_, kind) -> self#custom_fun_kind cx acc kind | EvalT (t, defer_use_t, id) -> diff --git a/tests/function/bind.js b/tests/function/bind.js index e43c9534ffe..328069c6762 100644 --- a/tests/function/bind.js +++ b/tests/function/bind.js @@ -20,9 +20,25 @@ let tests = [ }, // callable objects with overridden `bind` method - function(x: {(a: string, b: string): void, bind(a: string): void}) { + function(x: { (a: string, b: string): void, bind(a: string): void }) { (x.bind('foo'): void); // ok (x.bind(123): void); // error, number !~> string }, + function(x: (x: number) => void) { + const appliedFn = Function.apply.bind(x); // ok + appliedFn(null, ['']); // error + }, + + + function(x: (x: number) => void, y: (x: string) => void) { + const appliedFn = Function.apply.bind(x); // ok + const reappliedFn = appliedFn.bind(y); // error + reappliedFn(null, ['']); // error + }, + + function(x: (x: number) => void) { + const appliedFn = Function.apply.bind(x, null); // ok + appliedFn(['']); // error + }, ]; diff --git a/tests/function/function.exp b/tests/function/function.exp index b4ac55e7b38..80aec5091d0 100644 --- a/tests/function/function.exp +++ b/tests/function/function.exp @@ -246,9 +246,68 @@ Cannot call `x.bind` with `123` bound to `a` because number [1] is incompatible ^^^ [1] References: - bind.js:23:54 - 23| function(x: {(a: string, b: string): void, bind(a: string): void}) { - ^^^^^^ [2] + bind.js:23:55 + 23| function(x: { (a: string, b: string): void, bind(a: string): void }) { + ^^^^^^ [2] + + +Error ---------------------------------------------------------------------------------------------------- bind.js:30:22 + +Cannot call `appliedFn` with string bound to `x` because string [1] is incompatible with number [2]. + + bind.js:30:22 + 30| appliedFn(null, ['']); // error + ^^ [1] + +References: + bind.js:28:19 + 28| function(x: (x: number) => void) { + ^^^^^^ [2] + + +Error ---------------------------------------------------------------------------------------------------- bind.js:36:40 + +string [1] is incompatible with number [2]. + + bind.js:36:40 + 36| const reappliedFn = appliedFn.bind(y); // error + ^ + +References: + bind.js:34:43 + 34| function(x: (x: number) => void, y: (x: string) => void) { + ^^^^^^ [1] + bind.js:34:19 + 34| function(x: (x: number) => void, y: (x: string) => void) { + ^^^^^^ [2] + + +Error ---------------------------------------------------------------------------------------------------- bind.js:37:24 + +Cannot call `reappliedFn` with string bound to `x` because string [1] is incompatible with number [2]. + + bind.js:37:24 + 37| reappliedFn(null, ['']); // error + ^^ [1] + +References: + bind.js:34:19 + 34| function(x: (x: number) => void, y: (x: string) => void) { + ^^^^^^ [2] + + +Error ---------------------------------------------------------------------------------------------------- bind.js:42:16 + +Cannot call `appliedFn` with string bound to `x` because string [1] is incompatible with number [2]. + + bind.js:42:16 + 42| appliedFn(['']); // error + ^^ [1] + +References: + bind.js:40:19 + 40| function(x: (x: number) => void) { + ^^^^^^ [2] Error ----------------------------------------------------------------------------------------------------- call.js:4:10 @@ -717,4 +776,4 @@ References: -Found 52 errors +Found 56 errors