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
10 changes: 8 additions & 2 deletions src/typing/debug_js.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)))
Expand Down
19 changes: 15 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),
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 Down Expand Up @@ -5710,6 +5714,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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't look right. Look at the FunT _, BindT _ rule for guidance.

  1. The call_this_t field of the BindT use is the this type corresponding to the call of bind. E.g., x in x.bind(...). You want the first argument instead.
  2. Looking at FunT _, BindT _ as a guide, you'll notice some complexity missing from here. Specifically, the constraints involving ResolveSpreadsToMultiflowPartial. This is needed to figure out the full argument list in the presence of spread arguments.

Copy link
Contributor Author

@goodmind goodmind Jul 9, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What first argument? This is forwarding From

declare var regularApply: $Function$Prototype$Apply;
const b = regularApply.bind(fn) // would be `$Function$Prototype$Apply<typeof fn>`

to $Function$Prototype$Apply<typeof fn>

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you write a test, I think you will see what I mean. You want the first argument passed to the bind call, which is not call_this_t.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, sorry, my mistake -- I was confusing BindT with FunProtoBindT. You're right that call_this_t is the first parameter.

You do still need to do something with call_args_tlist, though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean something like this?

const appliedFn = Function.apply.bind(x); // ok
const reappliedFn = appliedFn.bind(y); // wrong
reappliedFn(null, [""]); // no error

Copy link
Contributor Author

@goodmind goodmind Jul 9, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@samwgoldman I think you were right 😀

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@samwgoldman would this mean adding second argument with applied parameters? I think one argument is already too much


| _, BindT (_, _, { call_tout; _ }, true) ->
rec_flow_t cx trace (l, call_tout)

Expand Down Expand Up @@ -6512,7 +6519,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 +7303,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 _
Expand Down
5 changes: 4 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,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
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 (* 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) -> FunProtoApplyT (f reason, t1)
| FunProtoT (reason) -> FunProtoT (f reason)
| FunProtoBindT (reason) -> FunProtoBindT (f reason)
| FunProtoCallT (reason) -> FunProtoCallT (f reason)
Expand Down
9 changes: 6 additions & 3 deletions src/typing/type_annotation.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think this is the only place we'd allow a sometimes-parameterized generic. the most similar concept is having a default, but in that case you'd have to use an empty <> (https://flow.org/en/docs/types/generics/#toc-adding-defaults-to-parameterized-generics).

this feels like a new behavior that we should consider carefully. also, we should consider how it interacts with annotating this, because it might be closer to Function$Prototype$Apply<this=T> or whatever syntax we end up with for that -- we've gone back and forth about annotating this for functions as either function f(this: T, ...) or function f<this = T>(...) (cc @gabelevi )

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like first one because it mirrors TypeScript

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How can I make this support <>?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mroch you can mimic second one with first one
type F<T> = (this: T) => void

| [t], targs ->
reconstruct_ast (FunProtoApplyT (reason, Some t)) targs
| _, targs ->
reconstruct_ast (FunProtoApplyT (reason, None)) targs
)

| "Function$Prototype$Bind" ->
Expand Down
6 changes: 5 additions & 1 deletion src/typing/type_mapper.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 5 additions & 1 deletion src/typing/type_visitor.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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) ->
Expand Down