Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Add support for [[call]] internal slot properties
Summary:
In the previous commit, I deprecated the use of `$call` properties. Some uses
can be subsumed by callable property syntax, but not all. The existing callable
property syntax can only be used with function types, for example. This syntax
can be used with any type.

Unlike deprecated `$call` syntax, the `[[call]]` syntax will define an
overloaded call signature if combined with callable properties or other
`[[call]]` properties.

This syntax can also be used to resolve the ambiguity static callable
properties in declared classes. `declare class C { static (): void }`
currently defines a callable class, but it could also be a class with an
instance method named `static`. This diff preserves the existing behavior.

Reviewed By: panagosg7

Differential Revision: D8042915

fbshipit-source-id: 70111db89295e593999ed5999a90c178aeeb168a
  • Loading branch information
samwgoldman authored and facebook-github-bot committed Jun 11, 2018
1 parent df8ee96 commit 954a727
Show file tree
Hide file tree
Showing 3 changed files with 194 additions and 15 deletions.
54 changes: 40 additions & 14 deletions src/typing/type_annotation.ml
Expand Up @@ -862,13 +862,26 @@ let rec convert cx tparams_map = Ast.Type.(function
add_dict loc i o, ts, spread
| Object.Property (loc, p) ->
add_prop loc p o, ts, spread
| Object.InternalSlot (loc, { Object.InternalSlot.id = (_, name); _ }) ->
Flow.add_output cx FlowError.(
EUnsupportedSyntax (loc, UnsupportedInternalSlot {
name;
static = false;
}));
o, ts, spread
| Object.InternalSlot (loc, slot) ->
let { Object.InternalSlot.
id = (_, name);
value;
static=_; (* object props are never static *)
optional;
_method=_;
} = slot in
if name = "call" then
let t = convert cx tparams_map value in
let t = if optional then Type.optional t else t in
add_call t o, ts, spread
else (
Flow.add_output cx FlowError.(
EUnsupportedSyntax (loc, UnsupportedInternalSlot {
name;
static = false;
}));
o, ts, spread
)
| Object.SpreadProperty (_, { Object.SpreadProperty.argument }) ->
let ts = match o with
| None -> ts
Expand Down Expand Up @@ -1192,13 +1205,26 @@ and add_interface_properties cx tparams_map properties s =

)

| InternalSlot (loc, { InternalSlot.id = (_, name); static; _ }) ->
Flow.add_output cx Flow_error.(
EUnsupportedSyntax (loc, UnsupportedInternalSlot {
name;
static;
}));
x
| InternalSlot (loc, slot) ->
let { InternalSlot.
id = (_, name);
value;
optional;
static;
_method=_;
} = slot in
if name = "call" then
let t = convert cx tparams_map value in
let t = if optional then Type.optional t else t in
append_call ~static t x
else (
Flow.add_output cx Flow_error.(
EUnsupportedSyntax (loc, UnsupportedInternalSlot {
name;
static;
}));
x
)

| SpreadProperty (loc, _) ->
Flow.add_output cx Flow_error.(EInternal (loc, InterfaceTypeSpread));
Expand Down
126 changes: 125 additions & 1 deletion tests/call_properties/call_properties.exp
Expand Up @@ -327,6 +327,130 @@ Deprecated $call syntax. Use callable property syntax instead. (`deprecated-call
^^^^^


Error --------------------------------------------------------------------------------------------- internal_slot.js:5:2

Cannot cast object literal to `O` because a callable signature is missing in object literal [1] but exists in `O` [2].

internal_slot.js:5:2
5| ({}: O); // err: no callable property
^^ [1]

References:
internal_slot.js:5:6
5| ({}: O); // err: no callable property
^ [2]


Error --------------------------------------------------------------------------------------------- internal_slot.js:6:2

Cannot cast function to `O` because number [1] is incompatible with undefined [2] in the return value.

internal_slot.js:6:2
6| (function() { return 0 }: O); // err: number ~> void
^^^^^^^^^^^^^^^^^^^^^^^

References:
internal_slot.js:6:22
6| (function() { return 0 }: O); // err: number ~> void
^ [1]
internal_slot.js:2:15
2| [[call]](): void;
^^^^ [2]


Error -------------------------------------------------------------------------------------------- internal_slot.js:13:2

Cannot cast object literal to `I` because a callable signature is missing in object literal [1] but exists in `I` [2].

internal_slot.js:13:2
13| ({}: I); // err: no callable property
^^ [1]

References:
internal_slot.js:13:6
13| ({}: I); // err: no callable property
^ [2]


Error -------------------------------------------------------------------------------------------- internal_slot.js:14:2

Cannot cast function to `I` because number [1] is incompatible with undefined [2] in the return value.

internal_slot.js:14:2
14| (function() { return 0 }: I); // err: number ~> void
^^^^^^^^^^^^^^^^^^^^^^^

References:
internal_slot.js:14:22
14| (function() { return 0 }: I); // err: number ~> void
^ [1]
internal_slot.js:10:15
10| [[call]](): void;
^^^^ [2]


Error -------------------------------------------------------------------------------------------- internal_slot.js:20:2

Cannot cast `C1()` to empty because undefined [1] is incompatible with empty [2].

internal_slot.js:20:2
20| (C1(): empty); // error: void ~> empty
^^^^

References:
internal_slot.js:18:22
18| static [[call]](): void;
^^^^ [1]
internal_slot.js:20:8
20| (C1(): empty); // error: void ~> empty
^^^^^ [2]


Error -------------------------------------------------------------------------------------------- internal_slot.js:23:1

Cannot call `mixed_callable` because mixed [1] is not a function.

internal_slot.js:23:1
23| mixed_callable();
^^^^^^^^^^^^^^^^

References:
internal_slot.js:22:41
22| declare var mixed_callable: { [[call]]: mixed };
^^^^^ [1]


Error ------------------------------------------------------------------------------------------- internal_slot.js:28:16

Cannot call `annot_callable` with `0` bound to the first parameter because number [1] is incompatible with string [2].

internal_slot.js:28:16
28| annot_callable(0); // error: number ~> string
^ [1]

References:
internal_slot.js:26:11
26| type Fn = string => number;
^^^^^^ [2]


Error -------------------------------------------------------------------------------------------- internal_slot.js:29:2

Cannot cast `annot_callable(...)` to empty because number [1] is incompatible with empty [2].

internal_slot.js:29:2
29| (annot_callable("foo"): empty); // error: number ~> empty
^^^^^^^^^^^^^^^^^^^^^

References:
internal_slot.js:26:21
26| type Fn = string => number;
^^^^^^ [1]
internal_slot.js:29:25
29| (annot_callable("foo"): empty); // error: number ~> empty
^^^^^ [2]


Error --------------------------------------------------------------------------------------------------- use_ops.js:4:2

Cannot cast `a` to `B` because a callable signature is missing in object type [1] but exists in function type [2] in
Expand All @@ -346,7 +470,7 @@ References:



Found 23 errors
Found 31 errors

Only showing the most relevant union/intersection branches.
To see all branches, re-run Flow with --show-all-branches
29 changes: 29 additions & 0 deletions tests/call_properties/internal_slot.js
@@ -0,0 +1,29 @@
type O = {
[[call]](): void;
}

({}: O); // err: no callable property
(function() { return 0 }: O); // err: number ~> void
(function() {}: O); // ok

interface I {
[[call]](): void;
}

({}: I); // err: no callable property
(function() { return 0 }: I); // err: number ~> void
(function() {}: I); // ok

declare class C1 {
static [[call]](): void;
}
(C1(): empty); // error: void ~> empty

declare var mixed_callable: { [[call]]: mixed };
mixed_callable();

declare var annot_callable: { [[call]]: Fn }
type Fn = string => number;
(annot_callable("foo"): number); // OK
annot_callable(0); // error: number ~> string
(annot_callable("foo"): empty); // error: number ~> empty

0 comments on commit 954a727

Please sign in to comment.