Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Function.prototype.apply.bind #7879

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions src/typing/debug_js.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)))
Expand Down
25 changes: 21 additions & 4 deletions src/typing/flow_js.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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() *)
| [] ->
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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 _
Expand Down
7 changes: 6 additions & 1 deletion src/typing/resolvableTypeJob.ml
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ and collect_of_type ?log_unresolved cx acc = function

| FunProtoBindT _
| FunProtoCallT _
| FunProtoApplyT _
| FunProtoApplyT (_, None, _)
| FunProtoT _
| NullProtoT _
| ObjProtoT _
Expand All @@ -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
Expand Down
6 changes: 3 additions & 3 deletions src/typing/type.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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 *)

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion src/typing/type_annotation.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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" ->
Expand Down
29 changes: 17 additions & 12 deletions src/typing/type_mapper.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 _
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions src/typing/type_mapper.mli
Original file line number Diff line number Diff line change
Expand Up @@ -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 :
Expand Down Expand Up @@ -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
Expand Down
7 changes: 6 additions & 1 deletion src/typing/type_visitor.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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) ->
Expand Down
18 changes: 17 additions & 1 deletion tests/function/bind.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
},
];
67 changes: 63 additions & 4 deletions tests/function/function.exp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -717,4 +776,4 @@ References:



Found 52 errors
Found 56 errors