Commit f885a1ab61

Andrew Kelley <superjoe30@gmail.com>
2018-03-22 00:56:41
change async function call syntax
* instead of `async(allocator) call()`, now it is `async<allocator> call()`. * Fixes syntax ambiguity when leaving off the allocator * Fixes parse failure when call is a field access This sets a precedent for using `<` to pass arguments to a keyword. This will affect `enum`, `union`, and `fn` (see #661)
1 parent 66fec3a
doc/langref.html.in
@@ -5845,7 +5845,7 @@ MultiplyOperator = "||" | "*" | "/" | "%" | "**" | "*%"
 
 PrefixOpExpression = PrefixOp TypeExpr | SuffixOpExpression
 
-SuffixOpExpression = ("async" option("(" Expression ")") PrimaryExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression)
+SuffixOpExpression = ("async" option("&lt;" SuffixOpExpression "&gt;") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression)
 
 FieldAccessExpression = "." Symbol
 
src/parser.cpp
@@ -956,7 +956,7 @@ static AstNode *ast_parse_curly_suffix_expr(ParseContext *pc, size_t *token_inde
 }
 
 /*
-SuffixOpExpression = ("async" option("(" Expression ")") PrimaryExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression)
+SuffixOpExpression = ("async" option("<" SuffixOpExpression ">") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression)
 FnCallExpression : token(LParen) list(Expression, token(Comma)) token(RParen)
 ArrayAccessExpression : token(LBracket) Expression token(RBracket)
 SliceExpression = "[" Expression ".." option(Expression) "]"
@@ -972,19 +972,20 @@ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, size_t *token_index,
 
         AstNode *allocator_expr_node = nullptr;
         Token *async_lparen_tok = &pc->tokens->at(*token_index);
-        if (async_lparen_tok->id == TokenIdLParen) {
+        if (async_lparen_tok->id == TokenIdCmpLessThan) {
             *token_index += 1;
-            allocator_expr_node = ast_parse_expression(pc, token_index, true);
-            ast_eat_token(pc, token_index, TokenIdRParen);
+            allocator_expr_node = ast_parse_prefix_op_expr(pc, token_index, true);
+            ast_eat_token(pc, token_index, TokenIdCmpGreaterThan);
         }
 
-        AstNode *fn_ref_expr_node = ast_parse_primary_expr(pc, token_index, true);
-        Token *lparen_tok = ast_eat_token(pc, token_index, TokenIdLParen);
-        AstNode *node = ast_create_node(pc, NodeTypeFnCallExpr, lparen_tok);
+        Token *fncall_token = &pc->tokens->at(*token_index);
+        AstNode *node = ast_parse_suffix_op_expr(pc, token_index, true);
+        if (node->type != NodeTypeFnCallExpr) {
+            ast_error(pc, fncall_token, "expected function call, found '%s'", token_name(fncall_token->id));
+        }
         node->data.fn_call_expr.is_async = true;
         node->data.fn_call_expr.async_allocator = allocator_expr_node;
-        node->data.fn_call_expr.fn_ref_expr = fn_ref_expr_node;
-        ast_parse_fn_call_param_list(pc, token_index, &node->data.fn_call_expr.params);
+        assert(node->data.fn_call_expr.fn_ref_expr != nullptr);
 
         primary_expr = node;
     } else {
test/cases/coroutines.zig
@@ -4,7 +4,7 @@ const assert = std.debug.assert;
 var x: i32 = 1;
 
 test "create a coroutine and cancel it" {
-    const p = try async(std.debug.global_allocator) simpleAsyncFn();
+    const p = try async<std.debug.global_allocator> simpleAsyncFn();
     cancel p;
     assert(x == 2);
 }
@@ -17,7 +17,7 @@ async fn simpleAsyncFn() void {
 
 test "coroutine suspend, resume, cancel" {
     seq('a');
-    const p = try async(std.debug.global_allocator) testAsyncSeq();
+    const p = try async<std.debug.global_allocator> testAsyncSeq();
     seq('c');
     resume p;
     seq('f');
@@ -43,7 +43,7 @@ fn seq(c: u8) void {
 }
 
 test "coroutine suspend with block" {
-    const p = try async(std.debug.global_allocator) testSuspendBlock();
+    const p = try async<std.debug.global_allocator> testSuspendBlock();
     std.debug.assert(!result);
     resume a_promise;
     std.debug.assert(result);
@@ -65,7 +65,7 @@ var await_final_result: i32 = 0;
 
 test "coroutine await" {
     await_seq('a');
-    const p = async(std.debug.global_allocator) await_amain() catch unreachable;
+    const p = async<std.debug.global_allocator> await_amain() catch unreachable;
     await_seq('f');
     resume await_a_promise;
     await_seq('i');
@@ -104,7 +104,7 @@ var early_final_result: i32 = 0;
 
 test "coroutine await early return" {
     early_seq('a');
-    const p = async(std.debug.global_allocator) early_amain() catch unreachable;
+    const p = async<std.debug.global_allocator> early_amain() catch unreachable;
     early_seq('f');
     assert(early_final_result == 1234);
     assert(std.mem.eql(u8, early_points, "abcdef"));
@@ -133,7 +133,7 @@ fn early_seq(c: u8) void {
 
 test "coro allocation failure" {
     var failing_allocator = std.debug.FailingAllocator.init(std.debug.global_allocator, 0);
-    if (async(&failing_allocator.allocator) asyncFuncThatNeverGetsRun()) {
+    if (async<&failing_allocator.allocator> asyncFuncThatNeverGetsRun()) {
         @panic("expected allocation failure");
     } else |err| switch (err) {
         error.OutOfMemory => {},
@@ -143,3 +143,16 @@ test "coro allocation failure" {
 async fn asyncFuncThatNeverGetsRun() void {
     @panic("coro frame allocation should fail");
 }
+
+test "async function with dot syntax" {
+    const S = struct {
+        var y: i32 = 1;
+        async fn foo() void {
+            y += 1;
+            suspend;
+        }
+    };
+    const p = try async<std.debug.global_allocator> S.foo();
+    cancel p;
+    assert(S.y == 2);
+}
test/compile_errors.zig
@@ -17,7 +17,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
     cases.add("returning error from void async function",
         \\const std = @import("std");
         \\export fn entry() void {
-        \\    const p = async(std.debug.global_allocator) amain() catch unreachable;
+        \\    const p = async<std.debug.global_allocator> amain() catch unreachable;
         \\}
         \\async fn amain() void {
         \\    return error.ShouldBeCompileError;