diff --git a/src/parser.cpp b/src/parser.cpp index 84ccdbeea8..453ab7ce2c 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -651,37 +651,29 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, size_t *token_index, bool m SuspendExpression(body) = "suspend" option( body ) */ static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, bool mandatory) { - size_t orig_token_index = *token_index; - Token *token = &pc->tokens->at(*token_index); - Token *suspend_token = nullptr; + Token *suspend_token = &pc->tokens->at(*token_index); - if (token->id == TokenIdKeywordSuspend) { + if (suspend_token->id == TokenIdKeywordSuspend) { *token_index += 1; - suspend_token = token; - token = &pc->tokens->at(*token_index); } else if (mandatory) { - ast_expect_token(pc, token, TokenIdKeywordSuspend); + ast_expect_token(pc, suspend_token, TokenIdKeywordSuspend); zig_unreachable(); } else { return nullptr; } - //guessing that semicolon is checked elsewhere? - if (token->id != TokenIdLBrace) { - if (mandatory) { - ast_expect_token(pc, token, TokenIdLBrace); - zig_unreachable(); - } else { - *token_index = orig_token_index; - return nullptr; - } + Token *lbrace = &pc->tokens->at(*token_index); + if (lbrace->id == TokenIdLBrace) { + AstNode *node = ast_create_node(pc, NodeTypeSuspend, suspend_token); + node->data.suspend.block = ast_parse_block(pc, token_index, true); + return node; + } else if (mandatory) { + ast_expect_token(pc, lbrace, TokenIdLBrace); + zig_unreachable(); + } else { + *token_index -= 1; + return nullptr; } - - //Expect that we have a block; - AstNode *node = ast_create_node(pc, NodeTypeSuspend, suspend_token); - node->data.suspend.block = ast_parse_block(pc, token_index, true); - - return node; } /* diff --git a/std/zig/ast.zig b/std/zig/ast.zig index 004f9278b9..95e899fb92 100644 --- a/std/zig/ast.zig +++ b/std/zig/ast.zig @@ -1778,19 +1778,12 @@ pub const Node = struct { pub const Suspend = struct { base: Node, - label: ?TokenIndex, suspend_token: TokenIndex, - payload: ?*Node, body: ?*Node, pub fn iterate(self: *Suspend, index: usize) ?*Node { var i = index; - if (self.payload) |payload| { - if (i < 1) return payload; - i -= 1; - } - if (self.body) |body| { if (i < 1) return body; i -= 1; @@ -1800,7 +1793,6 @@ pub const Node = struct { } pub fn firstToken(self: *Suspend) TokenIndex { - if (self.label) |label| return label; return self.suspend_token; } @@ -1809,10 +1801,6 @@ pub const Node = struct { return body.lastToken(); } - if (self.payload) |payload| { - return payload.lastToken(); - } - return self.suspend_token; } }; diff --git a/std/zig/parse.zig b/std/zig/parse.zig index 73d51e7870..fb49d2a2ba 100644 --- a/std/zig/parse.zig +++ b/std/zig/parse.zig @@ -852,19 +852,6 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { }) catch unreachable; continue; }, - Token.Id.Keyword_suspend => { - const node = try arena.create(ast.Node.Suspend{ - .base = ast.Node{ .id = ast.Node.Id.Suspend }, - .label = ctx.label, - .suspend_token = token_index, - .payload = null, - .body = null, - }); - ctx.opt_ctx.store(&node.base); - stack.append(State{ .SuspendBody = node }) catch unreachable; - try stack.append(State{ .Payload = OptionalCtx{ .Optional = &node.payload } }); - continue; - }, Token.Id.Keyword_inline => { stack.append(State{ .Inline = InlineCtx{ @@ -1415,10 +1402,21 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { }, State.SuspendBody => |suspend_node| { - if (suspend_node.payload != null) { - try stack.append(State{ .AssignmentExpressionBegin = OptionalCtx{ .RequiredNull = &suspend_node.body } }); + const token = nextToken(&tok_it, &tree); + switch (token.ptr.id) { + Token.Id.Semicolon => { + prevToken(&tok_it, &tree); + continue; + }, + Token.Id.LBrace => { + prevToken(&tok_it, &tree); + try stack.append(State{ .AssignmentExpressionBegin = OptionalCtx{ .RequiredNull = &suspend_node.body } }); + continue; + }, + else => { + ((try tree.errors.addOne())).* = Error{ .InvalidToken = Error.InvalidToken{ .token = token.index } }; + }, } - continue; }, State.AsyncAllocator => |async_node| { if (eatToken(&tok_it, &tree, Token.Id.AngleBracketLeft) == null) { @@ -3086,15 +3084,12 @@ fn parseBlockExpr(stack: *std.ArrayList(State), arena: *mem.Allocator, ctx: *con Token.Id.Keyword_suspend => { const node = try arena.create(ast.Node.Suspend{ .base = ast.Node{ .id = ast.Node.Id.Suspend }, - .label = null, .suspend_token = token_index, - .payload = null, .body = null, }); ctx.store(&node.base); stack.append(State{ .SuspendBody = node }) catch unreachable; - try stack.append(State{ .Payload = OptionalCtx{ .Optional = &node.payload } }); return true; }, Token.Id.Keyword_if => { diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 32cdc8121f..582bffdf3d 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -898,11 +898,11 @@ test "zig fmt: union(enum(u32)) with assigned enum values" { ); } -test "zig fmt: labeled suspend" { +test "zig fmt: resume from suspend block" { try testCanonical( \\fn foo() void { - \\ s: suspend |p| { - \\ break :s; + \\ suspend { + \\ resume @handle(); \\ } \\} \\ diff --git a/std/zig/render.zig b/std/zig/render.zig index bc45768fa3..868902a0d1 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -323,21 +323,7 @@ fn renderExpression( ast.Node.Id.Suspend => { const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", base); - if (suspend_node.label) |label| { - try renderToken(tree, stream, label, indent, start_col, Space.None); - try renderToken(tree, stream, tree.nextToken(label), indent, start_col, Space.Space); - } - - if (suspend_node.payload) |payload| { - if (suspend_node.body) |body| { - try renderToken(tree, stream, suspend_node.suspend_token, indent, start_col, Space.Space); - try renderExpression(allocator, stream, tree, indent, start_col, payload, Space.Space); - return renderExpression(allocator, stream, tree, indent, start_col, body, space); - } else { - try renderToken(tree, stream, suspend_node.suspend_token, indent, start_col, Space.Space); - return renderExpression(allocator, stream, tree, indent, start_col, payload, space); - } - } else if (suspend_node.body) |body| { + if (suspend_node.body) |body| { try renderToken(tree, stream, suspend_node.suspend_token, indent, start_col, Space.Space); return renderExpression(allocator, stream, tree, indent, start_col, body, space); } else { diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig index a955eeac37..bd6b6abf6f 100644 --- a/test/cases/coroutines.zig +++ b/test/cases/coroutines.zig @@ -256,19 +256,3 @@ async fn testBreakFromSuspend(my_result: *i32) void { suspend; my_result.* += 1; } - -test "suspend resume @handle()" { - var buf: [500]u8 = undefined; - var a = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator; - var my_result: i32 = 1; - const p = try async testBreakFromSuspend(&my_result); - std.debug.assert(my_result == 2); -} -async fn testSuspendResumeAtHandle() void { - suspend { - resume @handle(); - } - my_result.* += 1; - suspend; - my_result.* += 1; -} \ No newline at end of file diff --git a/test/compile_errors.zig b/test/compile_errors.zig index f4b289f70d..948d212e58 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,27 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "@handle() called outside of function definition", + \\var handle_undef: promise = undefined; + \\var handle_dummy: promise = @handle(); + \\export fn entry() bool { + \\ return handle_undef == handle_dummy; + \\} + , + ".tmp_source.zig:2:29: error: @handle() called outside of function definition", + ); + + cases.add( + "@handle() in non-async function", + \\export fn entry() bool { + \\ var handle_undef: promise = undefined; + \\ return handle_undef == @handle(); + \\} + , + ".tmp_source.zig:3:28: error: @handle() in non-async function", + ); + cases.add( "while loop body expression ignored", \\fn returns() usize { @@ -4738,34 +4759,4 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { , ".tmp_source.zig:3:36: error: @ArgType could not resolve the type of arg 0 because 'fn(var)var' is generic", ); - - cases.add( - "@handle() called outside of function definition", - \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { - \\ @import("std").os.exit(126); - \\} - \\ - \\var handle_undef: promise = undefined; - \\var handle_dummy: promise = @handle(); - \\ - \\pub fn main() void { - \\ if (handle_undef == handle_dummy) return 0; - \\} - , - ".tmp_source.zig:6:29: error: @handle() called outside of function definition", - ); - - cases.add( - "@handle() in non-async function", - \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { - \\ @import("std").os.exit(126); - \\} - \\ - \\pub fn main() void { - \\ var handle_undef: promise = undefined; - \\ if (handle_undef == @handle()) return 0; - \\} - , - ".tmp_source.zig:7:25: error: @handle() in non-async function", - ); }