From 1ec4772c2a3831f244c4c0a8a5dcb988fa0adbfa Mon Sep 17 00:00:00 2001 From: andretshurotshka Date: Tue, 2 Jul 2019 23:18:44 +0500 Subject: [PATCH 1/9] [typing] Fix hello.apply.bind --- src/typing/debug_js.ml | 10 ++++- src/typing/flow_js.ml | 73 +++++++++++++++++++++++++++++++-- src/typing/resolvableTypeJob.ml | 5 ++- src/typing/type.ml | 6 +-- src/typing/type_annotation.ml | 9 ++-- src/typing/type_mapper.ml | 6 ++- src/typing/type_visitor.ml | 6 ++- 7 files changed, 101 insertions(+), 14 deletions(-) diff --git a/src/typing/debug_js.ml b/src/typing/debug_js.ml index bb051e309bb..b7bdb309a86 100644 --- a/src/typing/debug_js.ml +++ b/src/typing/debug_js.ml @@ -170,11 +170,15 @@ and _json_of_t_impl json_cx t = Hh_json.( | NullProtoT _ | ObjProtoT _ | FunProtoT _ - | FunProtoApplyT _ + | FunProtoApplyT (_, None) | FunProtoBindT _ | FunProtoCallT _ -> [] + | FunProtoApplyT (_, Some t) -> [ + "thisType", _json_of_t json_cx t; + ] + | DefT (_, _, FunT (static, proto, funtype)) -> [ "static", _json_of_t json_cx static; "prototype", _json_of_t json_cx proto; @@ -1794,9 +1798,11 @@ let rec dump_t_ (depth, tvars) cx t = | NullProtoT _ | ObjProtoT _ | FunProtoT _ - | FunProtoApplyT _ + | FunProtoApplyT (_, None) | FunProtoBindT _ | FunProtoCallT _ -> p t + | FunProtoApplyT (_, Some arg) -> + 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..d7916163c86 100644 --- a/src/typing/flow_js.ml +++ b/src/typing/flow_js.ml @@ -5586,7 +5586,7 @@ let rec __flow cx ((l: Type.t), (u: Type.use_t)) trace = (*******************************************) (* resolves the arguments... *) - | FunProtoApplyT lreason, + | FunProtoApplyT (lreason, None), CallT (use_op, reason_op, ({call_this_t = func; call_args_tlist; _} as funtype)) -> (* Drop the specific AST derived argument reasons. Our new arguments come * from arbitrary positions in the array. *) @@ -5646,6 +5646,66 @@ let rec __flow cx ((l: Type.t), (u: Type.use_t)) trace = ) |> add_output cx ~trace; end + | FunProtoApplyT (lreason, Some func), + CallT (use_op, reason_op, ({call_args_tlist; _} as funtype)) -> + (* Drop the specific AST derived argument reasons. Our new arguments come + * from arbitrary positions in the array. *) + let use_op = match use_op with + | Op FunCall {op; fn; args = _; local} -> Op (FunCall {op; fn; args = []; local}) + | Op FunCallMethod {op; fn; prop; args = _; local} -> + Op (FunCallMethod {op; fn; prop; args = []; local}) + | _ -> use_op + in + + begin match call_args_tlist with + (* func.apply() *) + | [] -> + let funtype = { funtype with + call_this_t = VoidT.why reason_op |> with_trust bogus_trust; + call_args_tlist = []; + } in + rec_flow cx trace (func, CallT (use_op, reason_op, funtype)) + + (* func.apply(this_arg) *) + | (Arg this_arg)::[] -> + let funtype = { funtype with call_this_t = this_arg; call_args_tlist = [] } in + rec_flow cx trace (func, CallT (use_op, reason_op, funtype)) + + (* func.apply(this_arg, ts) *) + | first_arg::(Arg ts)::[] -> + let call_this_t = extract_non_spread cx ~trace first_arg in + let call_args_tlist = [ SpreadArg ts ] in + let funtype = { funtype with call_this_t; call_args_tlist; } in + (* Ignoring `this_arg`, we're basically doing func(...ts). Normally + * spread arguments are resolved for the multiflow application, however + * there are a bunch of special-cased functions like bind(), call(), + * apply, etc which look at the arguments a little earlier. If we delay + * resolving the spread argument, then we sabotage them. So we resolve + * it early *) + let t = Tvar.mk_where cx reason_op (fun t -> + let resolve_to = ResolveSpreadsToCallT (funtype, t) in + resolve_call_list cx ~trace ~use_op reason_op call_args_tlist resolve_to + ) in + rec_flow_t cx trace (func, t) + + | (SpreadArg t1)::(SpreadArg t2)::[] -> + add_output cx ~trace + (Error_message.(EUnsupportedSyntax (loc_of_t t1, SpreadArgument))); + add_output cx ~trace + (Error_message.(EUnsupportedSyntax (loc_of_t t2, SpreadArgument))) + | (SpreadArg t)::[] + | (Arg _)::(SpreadArg t)::[] -> + add_output cx ~trace + (Error_message.(EUnsupportedSyntax (loc_of_t t, SpreadArgument))) + | _::_::_::_ -> + Error_message.EFunctionCallExtraArg ( + mk_reason RFunctionUnusedArgument (aloc_of_reason lreason), + lreason, + 2, + use_op + ) |> add_output cx ~trace; + end + (************************************************************************) (* functions may be bound by passing a receiver and (partial) arguments *) (************************************************************************) @@ -5710,6 +5770,9 @@ 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, _), BindT (_, _, { call_this_t; call_tout; _ }, _) -> + rec_flow_t cx trace (FunProtoApplyT (lreason, Some call_this_t), call_tout) + | _, BindT (_, _, { call_tout; _ }, true) -> rec_flow_t cx trace (l, call_tout) @@ -6512,7 +6575,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 +7359,13 @@ and any_propagated_use cx trace use_op any l = covariant_flow ~use_op instance; true + | FunProtoApplyT (_, Some t) -> + 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..8a43de3264c 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,9 @@ and collect_of_type ?log_unresolved cx acc = function -> acc + | FunProtoApplyT (_, Some t) -> + collect_of_type ?log_unresolved cx acc 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..1a208a5483b 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 (* 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) -> FunProtoApplyT (f reason, t1) | 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..3523873af5b 100644 --- a/src/typing/type_annotation.ml +++ b/src/typing/type_annotation.ml @@ -852,9 +852,12 @@ 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 + let reason = mk_reason RFunctionType loc in + (match convert_type_params () with + | [t], targs -> + reconstruct_ast (FunProtoApplyT (reason, Some t)) targs + | _, targs -> + reconstruct_ast (FunProtoApplyT (reason, None)) targs ) | "Function$Prototype$Bind" -> diff --git a/src/typing/type_mapper.ml b/src/typing/type_mapper.ml index 04f7ce8c309..b903ac9d118 100644 --- a/src/typing/type_mapper.ml +++ b/src/typing/type_mapper.ml @@ -105,9 +105,13 @@ class virtual ['a] t = object(self) | FunProtoT _ | ObjProtoT _ | NullProtoT _ - | FunProtoApplyT _ + | FunProtoApplyT (_, None) | FunProtoBindT _ | FunProtoCallT _ -> t + | FunProtoApplyT (r, Some t') -> + let t'' = self#type_ cx map_cx t' in + if t'' == t' then t + else FunProtoApplyT (r, Some t'') | AnyWithLowerBoundT t' -> let t'' = self#type_ cx map_cx t' in if t'' == t' then t diff --git a/src/typing/type_visitor.ml b/src/typing/type_visitor.ml index fc14976c7e4..b20e779dec0 100644 --- a/src/typing/type_visitor.ml +++ b/src/typing/type_visitor.ml @@ -32,13 +32,17 @@ class ['a] t = object(self) acc | FunProtoT _ - | FunProtoApplyT _ + | FunProtoApplyT (_, None) | FunProtoBindT _ | FunProtoCallT _ | ObjProtoT _ | NullProtoT _ -> acc + | FunProtoApplyT (_, Some t) -> + let acc = self#type_ cx pole acc t in + acc + | CustomFunT (_, kind) -> self#custom_fun_kind cx acc kind | EvalT (t, defer_use_t, id) -> From e0a50e9afa5c71fad9084e23dff418bc134fb82f Mon Sep 17 00:00:00 2001 From: andretshurotshka Date: Tue, 2 Jul 2019 23:24:36 +0500 Subject: [PATCH 2/9] [typing] Remove copy-paste from FunProtoApplyT match --- src/typing/flow_js.ml | 66 ++++--------------------------------------- 1 file changed, 5 insertions(+), 61 deletions(-) diff --git a/src/typing/flow_js.ml b/src/typing/flow_js.ml index d7916163c86..50a6fedcc82 100644 --- a/src/typing/flow_js.ml +++ b/src/typing/flow_js.ml @@ -5586,68 +5586,12 @@ let rec __flow cx ((l: Type.t), (u: Type.use_t)) trace = (*******************************************) (* resolves the arguments... *) - | FunProtoApplyT (lreason, None), - CallT (use_op, reason_op, ({call_this_t = func; call_args_tlist; _} as funtype)) -> - (* Drop the specific AST derived argument reasons. Our new arguments come - * from arbitrary positions in the array. *) - let use_op = match use_op with - | Op FunCall {op; fn; args = _; local} -> Op (FunCall {op; fn; args = []; local}) - | Op FunCallMethod {op; fn; prop; args = _; local} -> - Op (FunCallMethod {op; fn; prop; args = []; local}) - | _ -> use_op + | FunProtoApplyT (lreason, arg), + 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 - - begin match call_args_tlist with - (* func.apply() *) - | [] -> - let funtype = { funtype with - call_this_t = VoidT.why reason_op |> with_trust bogus_trust; - call_args_tlist = []; - } in - rec_flow cx trace (func, CallT (use_op, reason_op, funtype)) - - (* func.apply(this_arg) *) - | (Arg this_arg)::[] -> - let funtype = { funtype with call_this_t = this_arg; call_args_tlist = [] } in - rec_flow cx trace (func, CallT (use_op, reason_op, funtype)) - - (* func.apply(this_arg, ts) *) - | first_arg::(Arg ts)::[] -> - let call_this_t = extract_non_spread cx ~trace first_arg in - let call_args_tlist = [ SpreadArg ts ] in - let funtype = { funtype with call_this_t; call_args_tlist; } in - (* Ignoring `this_arg`, we're basically doing func(...ts). Normally - * spread arguments are resolved for the multiflow application, however - * there are a bunch of special-cased functions like bind(), call(), - * apply, etc which look at the arguments a little earlier. If we delay - * resolving the spread argument, then we sabotage them. So we resolve - * it early *) - let t = Tvar.mk_where cx reason_op (fun t -> - let resolve_to = ResolveSpreadsToCallT (funtype, t) in - resolve_call_list cx ~trace ~use_op reason_op call_args_tlist resolve_to - ) in - rec_flow_t cx trace (func, t) - - | (SpreadArg t1)::(SpreadArg t2)::[] -> - add_output cx ~trace - (Error_message.(EUnsupportedSyntax (loc_of_t t1, SpreadArgument))); - add_output cx ~trace - (Error_message.(EUnsupportedSyntax (loc_of_t t2, SpreadArgument))) - | (SpreadArg t)::[] - | (Arg _)::(SpreadArg t)::[] -> - add_output cx ~trace - (Error_message.(EUnsupportedSyntax (loc_of_t t, SpreadArgument))) - | _::_::_::_ -> - Error_message.EFunctionCallExtraArg ( - mk_reason RFunctionUnusedArgument (aloc_of_reason lreason), - lreason, - 2, - use_op - ) |> add_output cx ~trace; - end - - | FunProtoApplyT (lreason, Some func), - CallT (use_op, reason_op, ({call_args_tlist; _} as funtype)) -> (* Drop the specific AST derived argument reasons. Our new arguments come * from arbitrary positions in the array. *) let use_op = match use_op with From 17225b0e9ab53ebce858c016bb5fc3c85f9f5a18 Mon Sep 17 00:00:00 2001 From: andretshurotshka Date: Tue, 9 Jul 2019 23:37:49 +0500 Subject: [PATCH 3/9] [tests] Add Function.apply.bind tests --- tests/function/bind.js | 12 +++++++++++- tests/function/function.exp | 22 ++++++++++++++++++---- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/tests/function/bind.js b/tests/function/bind.js index e43c9534ffe..6469f0bc271 100644 --- a/tests/function/bind.js +++ b/tests/function/bind.js @@ -20,9 +20,19 @@ 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); // wrong, should error + reappliedFn(null, ['']); // wrong + } ]; diff --git a/tests/function/function.exp b/tests/function/function.exp index b4ac55e7b38..b54062e8fe8 100644 --- a/tests/function/function.exp +++ b/tests/function/function.exp @@ -246,9 +246,23 @@ 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 ----------------------------------------------------------------------------------------------------- call.js:4:10 @@ -717,4 +731,4 @@ References: -Found 52 errors +Found 53 errors From f99e5f5478bcfaaab22093aef65437454d33adcc Mon Sep 17 00:00:00 2001 From: andretshurotshka Date: Tue, 9 Jul 2019 23:50:00 +0500 Subject: [PATCH 4/9] [tests] Add arguments bind test --- tests/function/bind.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/function/bind.js b/tests/function/bind.js index 6469f0bc271..090aea251bf 100644 --- a/tests/function/bind.js +++ b/tests/function/bind.js @@ -30,9 +30,15 @@ let tests = [ appliedFn(null, ['']); // error }, + function(x: (x: number) => void, y: (x: string) => void) { const appliedFn = Function.apply.bind(x); // ok const reappliedFn = appliedFn.bind(y); // wrong, should error reappliedFn(null, ['']); // wrong - } + }, + + function(x: (x: number) => void) { + const appliedFn = Function.apply.bind(x, null); // should be ok + appliedFn(['']); // wrong + }, ]; From 12429f33ce5dd67d437a3bf0c7530383b98ddee3 Mon Sep 17 00:00:00 2001 From: andretshurotshka Date: Wed, 10 Jul 2019 00:31:49 +0500 Subject: [PATCH 5/9] [typing] Add partial application to FunProtoApplyT --- src/typing/debug_js.ml | 15 +++++++++------ src/typing/flow_js.ml | 14 ++++++++------ src/typing/resolvableTypeJob.ml | 8 +++++--- src/typing/type.ml | 6 +++--- src/typing/type_annotation.ml | 4 ++-- src/typing/type_mapper.ml | 31 ++++++++++++++++--------------- src/typing/type_mapper.mli | 4 ++-- src/typing/type_visitor.ml | 5 +++-- 8 files changed, 48 insertions(+), 39 deletions(-) diff --git a/src/typing/debug_js.ml b/src/typing/debug_js.ml index b7bdb309a86..31afcc5d38c 100644 --- a/src/typing/debug_js.ml +++ b/src/typing/debug_js.ml @@ -170,14 +170,17 @@ and _json_of_t_impl json_cx t = Hh_json.( | NullProtoT _ | ObjProtoT _ | FunProtoT _ - | FunProtoApplyT (_, None) + | FunProtoApplyT (_, None, _) | FunProtoBindT _ | FunProtoCallT _ -> [] - | FunProtoApplyT (_, Some t) -> [ - "thisType", _json_of_t json_cx t; - ] + | 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; @@ -1798,10 +1801,10 @@ let rec dump_t_ (depth, tvars) cx t = | NullProtoT _ | ObjProtoT _ | FunProtoT _ - | FunProtoApplyT (_, None) + | FunProtoApplyT (_, None, _) | FunProtoBindT _ | FunProtoCallT _ -> p t - | FunProtoApplyT (_, Some arg) -> + | 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) diff --git a/src/typing/flow_js.ml b/src/typing/flow_js.ml index 50a6fedcc82..cf63edfa2a5 100644 --- a/src/typing/flow_js.ml +++ b/src/typing/flow_js.ml @@ -5586,7 +5586,7 @@ let rec __flow cx ((l: Type.t), (u: Type.use_t)) trace = (*******************************************) (* resolves the arguments... *) - | FunProtoApplyT (lreason, arg), + | 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 @@ -5601,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() *) | [] -> @@ -5714,8 +5716,8 @@ 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, _), BindT (_, _, { call_this_t; call_tout; _ }, _) -> - rec_flow_t cx trace (FunProtoApplyT (lreason, Some call_this_t), 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) @@ -6519,7 +6521,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) @@ -7303,13 +7305,13 @@ and any_propagated_use cx trace use_op any l = covariant_flow ~use_op instance; true - | FunProtoApplyT (_, Some t) -> + | FunProtoApplyT (_, Some t, _ (* TODO *)) -> contravariant_flow ~use_op t; true (* These types have no negative positions in their lower bounds *) | ExistsT _ - | FunProtoApplyT (_, None) + | FunProtoApplyT (_, None, _) | FunProtoBindT _ | FunProtoCallT _ | FunProtoT _ diff --git a/src/typing/resolvableTypeJob.ml b/src/typing/resolvableTypeJob.ml index 8a43de3264c..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 (_, None) + | FunProtoApplyT (_, None, _) | FunProtoT _ | NullProtoT _ | ObjProtoT _ @@ -258,8 +258,10 @@ and collect_of_type ?log_unresolved cx acc = function -> acc - | FunProtoApplyT (_, Some t) -> - collect_of_type ?log_unresolved cx acc t + | 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 diff --git a/src/typing/type.ml b/src/typing/type.ml index 1a208a5483b..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 * t option (* 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, t1) -> FunProtoApplyT (f reason, t1) + | 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 3523873af5b..8fb88563431 100644 --- a/src/typing/type_annotation.ml +++ b/src/typing/type_annotation.ml @@ -855,9 +855,9 @@ let rec convert cx tparams_map = Ast.Type.(function let reason = mk_reason RFunctionType loc in (match convert_type_params () with | [t], targs -> - reconstruct_ast (FunProtoApplyT (reason, Some t)) targs + reconstruct_ast (FunProtoApplyT (reason, Some t, [])) targs | _, targs -> - reconstruct_ast (FunProtoApplyT (reason, None)) targs + reconstruct_ast (FunProtoApplyT (reason, None, [])) targs ) | "Function$Prototype$Bind" -> diff --git a/src/typing/type_mapper.ml b/src/typing/type_mapper.ml index b903ac9d118..df95fd6b210 100644 --- a/src/typing/type_mapper.ml +++ b/src/typing/type_mapper.ml @@ -105,13 +105,14 @@ class virtual ['a] t = object(self) | FunProtoT _ | ObjProtoT _ | NullProtoT _ - | FunProtoApplyT (_, None) + | FunProtoApplyT (_, None, _) | FunProtoBindT _ | FunProtoCallT _ -> t - | FunProtoApplyT (r, Some t') -> + | FunProtoApplyT (r, Some t', call_args_tlist) -> let t'' = self#type_ cx map_cx t' in - if t'' == t' then t - else FunProtoApplyT (r, Some t'') + 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 @@ -215,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 _ @@ -1158,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 b20e779dec0..d67a8863a04 100644 --- a/src/typing/type_visitor.ml +++ b/src/typing/type_visitor.ml @@ -32,15 +32,16 @@ class ['a] t = object(self) acc | FunProtoT _ - | FunProtoApplyT (_, None) + | FunProtoApplyT (_, None, _) | FunProtoBindT _ | FunProtoCallT _ | ObjProtoT _ | NullProtoT _ -> acc - | FunProtoApplyT (_, Some t) -> + | 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 From 1d65d5c125e01a28002cffdff2f80bf7ffef9492 Mon Sep 17 00:00:00 2001 From: andretshurotshka Date: Wed, 10 Jul 2019 00:34:18 +0500 Subject: [PATCH 6/9] [tests] Fix partial arguments test --- tests/function/bind.js | 4 ++-- tests/function/function.exp | 16 +++++++++++++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/tests/function/bind.js b/tests/function/bind.js index 090aea251bf..0fd91f971f5 100644 --- a/tests/function/bind.js +++ b/tests/function/bind.js @@ -38,7 +38,7 @@ let tests = [ }, function(x: (x: number) => void) { - const appliedFn = Function.apply.bind(x, null); // should be ok - appliedFn(['']); // wrong + const appliedFn = Function.apply.bind(x, null); // ok + appliedFn(['']); // error }, ]; diff --git a/tests/function/function.exp b/tests/function/function.exp index b54062e8fe8..0241c31c562 100644 --- a/tests/function/function.exp +++ b/tests/function/function.exp @@ -265,6 +265,20 @@ References: ^^^^^^ [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 Cannot get `this.length` because: @@ -731,4 +745,4 @@ References: -Found 53 errors +Found 54 errors From 25594b38ad1147c25a185b52910e6352d627306e Mon Sep 17 00:00:00 2001 From: andretshurotshka Date: Wed, 10 Jul 2019 01:12:15 +0500 Subject: [PATCH 7/9] [typing] Fix rebinding already applied function --- src/typing/flow_js.ml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/typing/flow_js.ml b/src/typing/flow_js.ml index cf63edfa2a5..7d70d25f3fb 100644 --- a/src/typing/flow_js.ml +++ b/src/typing/flow_js.ml @@ -5716,6 +5716,10 @@ 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) From 981f52346394e604aa85faf79c6e87387c782956 Mon Sep 17 00:00:00 2001 From: andretshurotshka Date: Wed, 10 Jul 2019 01:13:20 +0500 Subject: [PATCH 8/9] [tests] Fix rebinding test --- tests/function/bind.js | 4 ++-- tests/function/function.exp | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/tests/function/bind.js b/tests/function/bind.js index 0fd91f971f5..328069c6762 100644 --- a/tests/function/bind.js +++ b/tests/function/bind.js @@ -33,8 +33,8 @@ let tests = [ function(x: (x: number) => void, y: (x: string) => void) { const appliedFn = Function.apply.bind(x); // ok - const reappliedFn = appliedFn.bind(y); // wrong, should error - reappliedFn(null, ['']); // wrong + const reappliedFn = appliedFn.bind(y); // error + reappliedFn(null, ['']); // error }, function(x: (x: number) => void) { diff --git a/tests/function/function.exp b/tests/function/function.exp index 0241c31c562..80aec5091d0 100644 --- a/tests/function/function.exp +++ b/tests/function/function.exp @@ -265,6 +265,37 @@ References: ^^^^^^ [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]. @@ -745,4 +776,4 @@ References: -Found 54 errors +Found 56 errors From c56ab68986167661f7bfcefbcaf75dd45933b0c7 Mon Sep 17 00:00:00 2001 From: andretshurotshka Date: Wed, 10 Jul 2019 01:56:22 +0500 Subject: [PATCH 9/9] [typing] Remove argument from Function$Prototype$Apply --- src/typing/type_annotation.ml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/typing/type_annotation.ml b/src/typing/type_annotation.ml index 8fb88563431..c7ca2284359 100644 --- a/src/typing/type_annotation.ml +++ b/src/typing/type_annotation.ml @@ -852,12 +852,9 @@ let rec convert cx tparams_map = Ast.Type.(function ) | "Function$Prototype$Apply" -> - let reason = mk_reason RFunctionType loc in - (match convert_type_params () with - | [t], targs -> - reconstruct_ast (FunProtoApplyT (reason, Some t, [])) targs - | _, targs -> - reconstruct_ast (FunProtoApplyT (reason, None, [])) targs + check_type_arg_arity cx loc t_ast targs 0 (fun () -> + let reason = mk_reason RFunctionType loc in + reconstruct_ast (FunProtoApplyT (reason, None, [])) None ) | "Function$Prototype$Bind" ->