| author | |
| committer | |
| log | f885a1ab61a664378c3f3062a3de1150b6a4ee07 |
| tree | 87e29d3ebcd6d7d7b859cc9ef9c33b83c8a3433c |
| parent | 66fec3a3d726733b162761211dee58e896370e95 |
* 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)4 files changed, 31 insertions(+), 17 deletions(-)
doc/langref.html.in+1-1| ... | ... | @@ -5845,7 +5845,7 @@ MultiplyOperator = "||" | "*" | "/" | "%" | "**" | "*%" |
| 5845 | 5845 | |
| 5846 | 5846 | PrefixOpExpression = PrefixOp TypeExpr | SuffixOpExpression |
| 5847 | 5847 | |
| 5848 | SuffixOpExpression = ("async" option("(" Expression ")") PrimaryExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression) | |
| 5848 | SuffixOpExpression = ("async" option("&lt;" SuffixOpExpression "&gt;") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression) | |
| 5849 | 5849 | |
| 5850 | 5850 | FieldAccessExpression = "." Symbol |
| 5851 | 5851 |
src/parser.cpp+10-9| ... | ... | @@ -956,7 +956,7 @@ static AstNode *ast_parse_curly_suffix_expr(ParseContext *pc, size_t *token_inde |
| 956 | 956 | } |
| 957 | 957 | |
| 958 | 958 | /* |
| 959 | SuffixOpExpression = ("async" option("(" Expression ")") PrimaryExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression) | |
| 959 | SuffixOpExpression = ("async" option("<" SuffixOpExpression ">") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression) | |
| 960 | 960 | FnCallExpression : token(LParen) list(Expression, token(Comma)) token(RParen) |
| 961 | 961 | ArrayAccessExpression : token(LBracket) Expression token(RBracket) |
| 962 | 962 | SliceExpression = "[" Expression ".." option(Expression) "]" |
| ... | ... | @@ -972,19 +972,20 @@ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, size_t *token_index, |
| 972 | 972 | |
| 973 | 973 | AstNode *allocator_expr_node = nullptr; |
| 974 | 974 | Token *async_lparen_tok = &pc->tokens->at(*token_index); |
| 975 | if (async_lparen_tok->id == TokenIdLParen) { | |
| 975 | if (async_lparen_tok->id == TokenIdCmpLessThan) { | |
| 976 | 976 | *token_index += 1; |
| 977 | allocator_expr_node = ast_parse_expression(pc, token_index, true); | |
| 978 | ast_eat_token(pc, token_index, TokenIdRParen); | |
| 977 | allocator_expr_node = ast_parse_prefix_op_expr(pc, token_index, true); | |
| 978 | ast_eat_token(pc, token_index, TokenIdCmpGreaterThan); | |
| 979 | 979 | } |
| 980 | 980 | |
| 981 | AstNode *fn_ref_expr_node = ast_parse_primary_expr(pc, token_index, true); | |
| 982 | Token *lparen_tok = ast_eat_token(pc, token_index, TokenIdLParen); | |
| 983 | AstNode *node = ast_create_node(pc, NodeTypeFnCallExpr, lparen_tok); | |
| 981 | Token *fncall_token = &pc->tokens->at(*token_index); | |
| 982 | AstNode *node = ast_parse_suffix_op_expr(pc, token_index, true); | |
| 983 | if (node->type != NodeTypeFnCallExpr) { | |
| 984 | ast_error(pc, fncall_token, "expected function call, found '%s'", token_name(fncall_token->id)); | |
| 985 | } | |
| 984 | 986 | node->data.fn_call_expr.is_async = true; |
| 985 | 987 | node->data.fn_call_expr.async_allocator = allocator_expr_node; |
| 986 | node->data.fn_call_expr.fn_ref_expr = fn_ref_expr_node; | |
| 987 | ast_parse_fn_call_param_list(pc, token_index, &node->data.fn_call_expr.params); | |
| 988 | assert(node->data.fn_call_expr.fn_ref_expr != nullptr); | |
| 988 | 989 | |
| 989 | 990 | primary_expr = node; |
| 990 | 991 | } else { |
test/cases/coroutines.zig+19-6| ... | ... | @@ -4,7 +4,7 @@ const assert = std.debug.assert; |
| 4 | 4 | var x: i32 = 1; |
| 5 | 5 | |
| 6 | 6 | test "create a coroutine and cancel it" { |
| 7 | const p = try async(std.debug.global_allocator) simpleAsyncFn(); | |
| 7 | const p = try async<std.debug.global_allocator> simpleAsyncFn(); | |
| 8 | 8 | cancel p; |
| 9 | 9 | assert(x == 2); |
| 10 | 10 | } |
| ... | ... | @@ -17,7 +17,7 @@ async fn simpleAsyncFn() void { |
| 17 | 17 | |
| 18 | 18 | test "coroutine suspend, resume, cancel" { |
| 19 | 19 | seq('a'); |
| 20 | const p = try async(std.debug.global_allocator) testAsyncSeq(); | |
| 20 | const p = try async<std.debug.global_allocator> testAsyncSeq(); | |
| 21 | 21 | seq('c'); |
| 22 | 22 | resume p; |
| 23 | 23 | seq('f'); |
| ... | ... | @@ -43,7 +43,7 @@ fn seq(c: u8) void { |
| 43 | 43 | } |
| 44 | 44 | |
| 45 | 45 | test "coroutine suspend with block" { |
| 46 | const p = try async(std.debug.global_allocator) testSuspendBlock(); | |
| 46 | const p = try async<std.debug.global_allocator> testSuspendBlock(); | |
| 47 | 47 | std.debug.assert(!result); |
| 48 | 48 | resume a_promise; |
| 49 | 49 | std.debug.assert(result); |
| ... | ... | @@ -65,7 +65,7 @@ var await_final_result: i32 = 0; |
| 65 | 65 | |
| 66 | 66 | test "coroutine await" { |
| 67 | 67 | await_seq('a'); |
| 68 | const p = async(std.debug.global_allocator) await_amain() catch unreachable; | |
| 68 | const p = async<std.debug.global_allocator> await_amain() catch unreachable; | |
| 69 | 69 | await_seq('f'); |
| 70 | 70 | resume await_a_promise; |
| 71 | 71 | await_seq('i'); |
| ... | ... | @@ -104,7 +104,7 @@ var early_final_result: i32 = 0; |
| 104 | 104 | |
| 105 | 105 | test "coroutine await early return" { |
| 106 | 106 | early_seq('a'); |
| 107 | const p = async(std.debug.global_allocator) early_amain() catch unreachable; | |
| 107 | const p = async<std.debug.global_allocator> early_amain() catch unreachable; | |
| 108 | 108 | early_seq('f'); |
| 109 | 109 | assert(early_final_result == 1234); |
| 110 | 110 | assert(std.mem.eql(u8, early_points, "abcdef")); |
| ... | ... | @@ -133,7 +133,7 @@ fn early_seq(c: u8) void { |
| 133 | 133 | |
| 134 | 134 | test "coro allocation failure" { |
| 135 | 135 | var failing_allocator = std.debug.FailingAllocator.init(std.debug.global_allocator, 0); |
| 136 | if (async(&failing_allocator.allocator) asyncFuncThatNeverGetsRun()) { | |
| 136 | if (async<&failing_allocator.allocator> asyncFuncThatNeverGetsRun()) { | |
| 137 | 137 | @panic("expected allocation failure"); |
| 138 | 138 | } else |err| switch (err) { |
| 139 | 139 | error.OutOfMemory => {}, |
| ... | ... | @@ -143,3 +143,16 @@ test "coro allocation failure" { |
| 143 | 143 | async fn asyncFuncThatNeverGetsRun() void { |
| 144 | 144 | @panic("coro frame allocation should fail"); |
| 145 | 145 | } |
| 146 | ||
| 147 | test "async function with dot syntax" { | |
| 148 | const S = struct { | |
| 149 | var y: i32 = 1; | |
| 150 | async fn foo() void { | |
| 151 | y += 1; | |
| 152 | suspend; | |
| 153 | } | |
| 154 | }; | |
| 155 | const p = try async<std.debug.global_allocator> S.foo(); | |
| 156 | cancel p; | |
| 157 | assert(S.y == 2); | |
| 158 | } |
test/compile_errors.zig+1-1| ... | ... | @@ -17,7 +17,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { |
| 17 | 17 | cases.add("returning error from void async function", |
| 18 | 18 | \\const std = @import("std"); |
| 19 | 19 | \\export fn entry() void { |
| 20 | \\ const p = async(std.debug.global_allocator) amain() catch unreachable; | |
| 20 | \\ const p = async<std.debug.global_allocator> amain() catch unreachable; | |
| 21 | 21 | \\} |
| 22 | 22 | \\async fn amain() void { |
| 23 | 23 | \\ return error.ShouldBeCompileError; |