Skip to content

Commit

Permalink
Simplify support for async/await in type_sig_merge
Browse files Browse the repository at this point in the history
Summary:
This diff updates the types produced by Type_sig_merge in the following:
* The unary `await e` should not be reachable from exports, as it is a parse error
if not within an async context.
* The return type of `async () => {}` is always `Promise<void>`.

Reviewed By: samwgoldman

Differential Revision: D28696969

fbshipit-source-id: 9afaec68b8dc324c67634f0fed12b0b78d77ed8c
  • Loading branch information
panagosg7 authored and facebook-github-bot committed Jun 2, 2021
1 parent f3206ba commit a1a24ed
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 27 deletions.
12 changes: 10 additions & 2 deletions src/parser_utils/type_sig/type_sig_parse.ml
Expand Up @@ -2541,8 +2541,16 @@ let rec expression opts scope locs (loc, expr) =
object_literal opts scope locs loc ~frozen:false properties
| E.Array { E.Array.elements; comments = _ } -> array_literal opts scope locs loc elements
| E.Unary { E.Unary.operator; argument; comments = _ } ->
let t = expression opts scope locs argument in
Eval (loc, t, Unary operator)
begin
match operator with
| E.Unary.Await ->
(* This is already a parse error *)
let e = Signature_error.UnexpectedExpression (loc, Flow_ast_utils.ExpressionSort.Unary) in
Err (loc, SigError e)
| _ ->
let t = expression opts scope locs argument in
Eval (loc, t, Unary operator)
end
| E.Binary { E.Binary.operator; left = _; right = _; comments = _ } -> binary loc operator
| E.Update { E.Update.operator; argument = _; prefix = _; comments = _ } -> update loc operator
| E.Sequence { E.Sequence.expressions; comments = _ } ->
Expand Down
29 changes: 4 additions & 25 deletions src/typing/type_sig_merge.ml
Expand Up @@ -157,17 +157,8 @@ module Make (Tvar : TVAR) (ConsGen : CONS_GEN) : S = struct
| U.Void -> Type.VoidT.at loc trust
| U.Delete -> Type.BoolT.at loc trust
| U.Await ->
let reason = Reason.(mk_reason (RCustom "await") loc) in
let await =
Flow_js_utils.lookup_builtin_strict file.cx (Reason.OrdinaryName "$await") reason
in
(* TODO: use_op *)
let use_op = Type.unknown_use in
Tvar.mk_no_wrap_where file.cx reason (fun tout ->
ConsGen.flow
file.cx
( await,
Type.CallT (use_op, reason, Type.mk_functioncalltype reason None [Type.Arg t] tout) ))
(* This is a parse error *)
Type.(AnyT.at (AnyError None) loc)

let eval file loc t = function
| Unary op -> eval_unary file loc t op
Expand All @@ -187,23 +178,11 @@ module Make (Tvar : TVAR) (ConsGen : CONS_GEN) : S = struct
ConsGen.flow file.cx (t, Type.GetElemT (use_op, reason, index, tout)))

let async_void_return file loc =
let t = Type.VoidT.at loc trust in
let reason = Reason.(mk_reason (RCustom "async return") loc) in
Flow_js_utils.lookup_builtin_typeapp
file.cx
reason
Reason.(mk_reason (RCustom "async return") loc)
(Reason.OrdinaryName "Promise")
[
Tvar.mk_derivable_where file.cx reason (fun tvar ->
let funt =
Flow_js_utils.lookup_builtin_strict file.cx (Reason.OrdinaryName "$await") reason
in
let callt = Type.mk_functioncalltype reason None [Type.Arg t] (Type.open_tvar tvar) in
let reason =
Reason.repos_reason (Reason.aloc_of_reason (TypeUtil.reason_of_t t)) reason
in
ConsGen.flow file.cx (funt, Type.CallT (Type.unknown_use, reason, callt)));
]
[Type.VoidT.at loc trust]

let add_default_constructor reason extends props =
match extends with
Expand Down
11 changes: 11 additions & 0 deletions tests/type_sig_merge/.flowconfig
@@ -0,0 +1,11 @@
[ignore]

[include]

[libs]

[lints]

[options]

[strict]
5 changes: 5 additions & 0 deletions tests/type_sig_merge/async_void_return-export.js
@@ -0,0 +1,5 @@
// @flow

export const f = async () => {};

(f(): empty); // error: Promise ~> empty
5 changes: 5 additions & 0 deletions tests/type_sig_merge/async_void_return-import.js
@@ -0,0 +1,5 @@
// @flow

import {f} from './async_void_return-export';

(f(): empty); // error: Promise ~> empty
36 changes: 36 additions & 0 deletions tests/type_sig_merge/type_sig_merge.exp
@@ -0,0 +1,36 @@
Error ---------------------------------------------------------------------------------- async_void_return-export.js:5:2

Cannot cast `f()` to empty because `Promise` [1] is incompatible with empty [2]. [incompatible-cast]

async_void_return-export.js:5:2
5| (f(): empty); // error: Promise ~> empty
^^^

References:
async_void_return-export.js:3:29
3| export const f = async () => {};
^ [1]
async_void_return-export.js:5:7
5| (f(): empty); // error: Promise ~> empty
^^^^^ [2]


Error ---------------------------------------------------------------------------------- async_void_return-import.js:5:2

Cannot cast `f()` to empty because `Promise` [1] is incompatible with empty [2]. [incompatible-cast]

async_void_return-import.js:5:2
5| (f(): empty); // error: Promise ~> empty
^^^

References:
<BUILTINS>/prelude.js:40:15
40| declare class Promise<+R> {}
^^^^^^^ [1]
async_void_return-import.js:5:7
5| (f(): empty); // error: Promise ~> empty
^^^^^ [2]



Found 2 errors

0 comments on commit a1a24ed

Please sign in to comment.