Skip to content

Require return type annotation for async functions to be of promise type #5913

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

Closed
wants to merge 1 commit into from
Closed
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -46,6 +46,7 @@ These are only breaking changes for unformatted code.
- Fix compiler ppx issue when combining `async` and uncurried application https://github.com/rescript-lang/rescript-compiler/pull/5856
- Fix issue where the internal representation of uncurried types would leak when a non-function is applied in a curried way https://github.com/rescript-lang/rescript-compiler/pull/5892
- In GenType, check annotations also in module types to decide whether to produce the `.gen.tsx` file https://github.com/rescript-lang/rescript-compiler/pull/5903
- Require return type annotation for async functions to be of promise type https://github.com/rescript-lang/rescript-compiler/issues/5912

#### :nail_care: Polish

12 changes: 11 additions & 1 deletion jscomp/frontend/ast_async.ml
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
let isResReturnType : Parsetree.attribute -> bool =
fun ({ txt }, _) -> txt = "res.returnType"

let add_promise_type ~async (result : Parsetree.expression) =
if async then
let loc = result.pexp_loc in
let isTypeAnnotated = Ext_list.exists result.pexp_attributes isResReturnType in
let unsafe_async =
Ast_helper.Exp.ident ~loc
{ txt = Ldot (Ldot (Lident "Js", "Promise"), "unsafe_async"); loc }
in
Ast_helper.Exp.apply ~loc unsafe_async [ (Nolabel, result) ]
match result.pexp_desc with
| Pexp_constraint (e,t) when isTypeAnnotated ->
let loc = t.ptyp_loc in
let e = Ast_helper.Exp.apply ~loc unsafe_async [ (Nolabel, e) ] in
Ast_helper.Exp.constraint_ ~loc e t
| _ ->
Ast_helper.Exp.apply ~loc unsafe_async [ (Nolabel, result) ]
else result

let add_async_attribute ~async (body : Parsetree.expression) =
15 changes: 15 additions & 0 deletions jscomp/test/async_await.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
'use strict';


async function withAnnotations1(param) {
return 3;
}

async function withAnnotations2(param) {
return 3;
}

async function withAnnotations3(param) {
return 3;
}

function next(n) {
return n + 1 | 0;
}
@@ -18,6 +30,9 @@ function Make(I) {
};
}

exports.withAnnotations1 = withAnnotations1;
exports.withAnnotations2 = withAnnotations2;
exports.withAnnotations3 = withAnnotations3;
exports.next = next;
exports.useNext = useNext;
exports.Make = Make;
4 changes: 4 additions & 0 deletions jscomp/test/async_await.res
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
let withAnnotations1 = async () : promise<int> => 3
let withAnnotations2 = async () : promise<int> => 3
let withAnnotations3 = async () : promise<int> => (3:int)

@@uncurried

let next = n => n + 1
19 changes: 11 additions & 8 deletions res_syntax/src/res_core.ml
Original file line number Diff line number Diff line change
@@ -1477,8 +1477,8 @@ and parseTernaryExpr leftOperand p =
(Some falseBranch)
| _ -> leftOperand

and parseEs6ArrowExpression ?(arrowAttrs = []) ?(arrowStartPos = None) ?context
?parameters p =
and parseEs6ArrowExpression ?(arrowAttrs = []) ?(arrowStartPos = None) ~async
?context ?parameters p =
let startPos = p.Parser.startPos in
Parser.leaveBreadcrumb p Grammar.Es6ArrowExpr;
(* Parsing function parameters and attributes:
@@ -1535,9 +1535,12 @@ and parseEs6ArrowExpression ?(arrowAttrs = []) ?(arrowStartPos = None) ?context
let expr = parseExpr ?context p in
match returnType with
| Some typ ->
Ast_helper.Exp.constraint_
~loc:(mkLoc expr.pexp_loc.loc_start typ.Parsetree.ptyp_loc.loc_end)
expr typ
let loc = mkLoc expr.pexp_loc.loc_start typ.Parsetree.ptyp_loc.loc_end in
let attrs =
if async then [(Location.mkloc "res.returnType" loc, Parsetree.PStr [])]
else []
in
Ast_helper.Exp.constraint_ ~attrs ~loc expr typ
| None -> expr
in
Parser.eatBreadcrumb p;
@@ -2148,7 +2151,7 @@ and parseOperandExpr ~context p =
then
let arrowAttrs = !attrs in
let () = attrs := [] in
parseEs6ArrowExpression ~arrowAttrs ~context p
parseEs6ArrowExpression ~arrowAttrs ~async:false ~context p
else parseUnaryExpr p
in
(* let endPos = p.Parser.prevEndPos in *)
@@ -2924,7 +2927,7 @@ and parseBracedOrRecordExpr p =
let loc = mkLoc startPos identEndPos in
let ident = Location.mkloc (Longident.last pathIdent.txt) loc in
let a =
parseEs6ArrowExpression
parseEs6ArrowExpression ~async:false
~parameters:
[
TermParameter
@@ -3218,7 +3221,7 @@ and parseAsyncArrowExpression ?(arrowAttrs = []) p =
Parser.expect (Lident "async") p;
let asyncAttr = makeAsyncAttr (mkLoc startPos p.prevEndPos) in
parseEs6ArrowExpression ~arrowAttrs:(asyncAttr :: arrowAttrs)
~arrowStartPos:(Some startPos) p
~arrowStartPos:(Some startPos) ~async:true p

and parseAwaitExpression p =
let awaitLoc = mkLoc p.Parser.startPos p.endPos in
15 changes: 12 additions & 3 deletions res_syntax/src/res_parsetree_viewer.ml
Original file line number Diff line number Diff line change
@@ -95,6 +95,13 @@ let hasAwaitAttribute attrs =
| _ -> false)
attrs

let hasReturnTypeAttribute attrs =
List.exists
(function
| {Location.txt = "res.returnType"}, _ -> true
| _ -> false)
attrs

let collectListExpressions expr =
let rec collect acc expr =
match expr.pexp_desc with
@@ -205,7 +212,7 @@ let filterParsingAttrs attrs =
Location.txt =
( "bs" | "res.uapp" | "res.arity" | "res.braces" | "res.iflet"
| "res.namedArgLoc" | "res.optional" | "res.ternary" | "res.async"
| "res.await" | "res.template" );
| "res.await" | "res.template" | "res.returnType" );
},
_ ) ->
false
@@ -353,7 +360,8 @@ let hasAttributes attrs =
| ( {
Location.txt =
( "bs" | "res.uapp" | "res.arity" | "res.braces" | "res.iflet"
| "res.ternary" | "res.async" | "res.await" | "res.template" );
| "res.ternary" | "res.async" | "res.await" | "res.template"
| "res.returnType" );
},
_ ) ->
false
@@ -535,7 +543,8 @@ let isPrintableAttribute attr =
| ( {
Location.txt =
( "bs" | "res.uapp" | "res.arity" | "res.iflet" | "res.braces" | "JSX"
| "res.async" | "res.await" | "res.template" | "res.ternary" );
| "res.async" | "res.await" | "res.template" | "res.ternary"
| "res.returnType" );
},
_ ) ->
false
2 changes: 2 additions & 0 deletions res_syntax/src/res_parsetree_viewer.mli
Original file line number Diff line number Diff line change
@@ -31,6 +31,8 @@ val processFunctionAttributes : Parsetree.attributes -> functionAttributesInfo

val hasAwaitAttribute : Parsetree.attributes -> bool

val hasReturnTypeAttribute : Parsetree.attributes -> bool

type ifConditionKind =
| If of Parsetree.expression
| IfLet of Parsetree.pattern * Parsetree.expression
10 changes: 8 additions & 2 deletions res_syntax/src/res_printer.ml
Original file line number Diff line number Diff line change
@@ -2602,7 +2602,10 @@ and printExpression ~state (e : Parsetree.expression) cmtTbl =
let uncurried = uncurried || bs in
let returnExpr, typConstraint =
match returnExpr.pexp_desc with
| Pexp_constraint (expr, typ) ->
| Pexp_constraint (expr, typ)
when (not async)
|| Res_parsetree_viewer.hasReturnTypeAttribute
returnExpr.pexp_attributes ->
( {
expr with
pexp_attributes =
@@ -3308,7 +3311,10 @@ and printPexpFun ~state ~inCallback e cmtTbl =
let uncurried = bs || uncurried in
let returnExpr, typConstraint =
match returnExpr.pexp_desc with
| Pexp_constraint (expr, typ) ->
| Pexp_constraint (expr, typ)
when (not async)
|| Res_parsetree_viewer.hasReturnTypeAttribute
returnExpr.pexp_attributes ->
( {
expr with
pexp_attributes =
Original file line number Diff line number Diff line change
@@ -26,8 +26,10 @@ let async =
[@res.braces ])
let f =
((if isPositive
then ((fun a -> fun b -> (a + b : int))[@res.async ])
else (((fun c -> fun d -> (c - d : int)))[@res.async ]))
then ((fun a -> fun b -> (((a + b : int))[@res.returnType ]))
[@res.async ])
else (((fun c -> fun d -> (((c - d : int))[@res.returnType ])))
[@res.async ]))
[@res.ternary ])
let foo = async ~a:((34)[@res.namedArgLoc ])
let bar = ((fun ~a:((a)[@res.namedArgLoc ]) -> a + 1)[@res.async ])
7 changes: 7 additions & 0 deletions res_syntax/tests/printer/expr/asyncAwait.res
Original file line number Diff line number Diff line change
@@ -120,3 +120,10 @@ let c3 = (. x) => @foo y => x+y

type t1 = (. int) => string => bool
type t2 = (. int, string) => bool

let ann1 = async () => (3:int)
let ann2 = async () : promise<int> => 3
let ann3 = async () : promise<int> => (3:int)
let ann4 = foo(async () => (3:int))
let ann5 = foo(async () : promise<int> => 3)
let ann6 = foo(async () : promise<int> => (3:int))
7 changes: 7 additions & 0 deletions res_syntax/tests/printer/expr/expected/asyncAwait.res.txt
Original file line number Diff line number Diff line change
@@ -142,3 +142,10 @@ let c3 = (. x) => {@foo y => x + y}

type t1 = (. int) => string => bool
type t2 = (. int, string) => bool

let ann1 = async () => (3: int)
let ann2 = async (): promise<int> => 3
let ann3 = async (): promise<int> => (3: int)
let ann4 = foo(async () => (3: int))
let ann5 = foo(async (): promise<int> => 3)
let ann6 = foo(async (): promise<int> => (3: int))