Skip to content

Commit

Permalink
change async function call syntax
Browse files Browse the repository at this point in the history
 * 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)
  • Loading branch information
andrewrk committed Mar 21, 2018
1 parent 66fec3a commit f885a1a
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 17 deletions.
2 changes: 1 addition & 1 deletion doc/langref.html.in
Expand Up @@ -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

Expand Down
19 changes: 10 additions & 9 deletions src/parser.cpp
Expand Up @@ -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) "]"
Expand All @@ -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 {
Expand Down
25 changes: 19 additions & 6 deletions test/cases/coroutines.zig
Expand Up @@ -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);
}
Expand All @@ -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');
Expand All @@ -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);
Expand All @@ -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');
Expand Down Expand Up @@ -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"));
Expand Down Expand Up @@ -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 => {},
Expand All @@ -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);
}
2 changes: 1 addition & 1 deletion test/compile_errors.zig
Expand Up @@ -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;
Expand Down

0 comments on commit f885a1a

Please sign in to comment.