From 6f8d732599461aa816f545b658a068eceb6ac9bc Mon Sep 17 00:00:00 2001 From: Vexu Date: Mon, 9 Mar 2020 11:02:16 +0200 Subject: [PATCH 1/4] update parsers to new noasync syntax --- lib/std/zig/ast.zig | 29 ++++++++++++++++++-- lib/std/zig/parse.zig | 62 +++++++++++++++++++++++++----------------- lib/std/zig/render.zig | 9 ++++-- src/all_types.hpp | 7 ++++- src/ast_render.cpp | 8 ++++++ src/parser.cpp | 36 ++++++++++++++++-------- 6 files changed, 108 insertions(+), 43 deletions(-) diff --git a/lib/std/zig/ast.zig b/lib/std/zig/ast.zig index bc4f6350d6..711806b810 100644 --- a/lib/std/zig/ast.zig +++ b/lib/std/zig/ast.zig @@ -431,6 +431,7 @@ pub const Node = struct { ContainerDecl, Asm, Comptime, + Noasync, Block, // Misc @@ -1078,6 +1079,30 @@ pub const Node = struct { } }; + pub const Noasync = struct { + base: Node = Node{ .id = .Noasync }, + doc_comments: ?*DocComment, + noasync_token: TokenIndex, + expr: *Node, + + pub fn iterate(self: *Noasync, index: usize) ?*Node { + var i = index; + + if (i < 1) return self.expr; + i -= 1; + + return null; + } + + pub fn firstToken(self: *const Noasync) TokenIndex { + return self.noasync_token; + } + + pub fn lastToken(self: *const Noasync) TokenIndex { + return self.expr.lastToken(); + } + }; + pub const Payload = struct { base: Node = Node{ .id = .Payload }, lpipe: TokenIndex, @@ -1560,9 +1585,7 @@ pub const Node = struct { pub const Op = union(enum) { AddressOf, ArrayType: ArrayInfo, - Await: struct { - noasync_token: ?TokenIndex = null, - }, + Await, BitNot, BoolNot, Cancel, diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index cadf25eef6..5392c18dfe 100644 --- a/lib/std/zig/parse.zig +++ b/lib/std/zig/parse.zig @@ -856,6 +856,7 @@ fn parsePrefixExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { /// / IfExpr /// / KEYWORD_break BreakLabel? Expr? /// / KEYWORD_comptime Expr +/// / KEYWORD_noasync Expr /// / KEYWORD_continue BreakLabel? /// / KEYWORD_resume Expr /// / KEYWORD_return Expr? @@ -870,7 +871,7 @@ fn parsePrimaryExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node const label = try parseBreakLabel(arena, it, tree); const expr_node = try parseExpr(arena, it, tree); const node = try arena.create(Node.ControlFlowExpression); - node.* = Node.ControlFlowExpression{ + node.* = .{ .ltoken = token, .kind = Node.ControlFlowExpression.Kind{ .Break = label }, .rhs = expr_node, @@ -883,7 +884,7 @@ fn parsePrimaryExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index }, }); const node = try arena.create(Node.Comptime); - node.* = Node.Comptime{ + node.* = .{ .doc_comments = null, .comptime_token = token, .expr = expr_node, @@ -891,10 +892,23 @@ fn parsePrimaryExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node return &node.base; } + if (eatToken(it, .Keyword_noasync)) |token| { + const expr_node = try expectNode(arena, it, tree, parseExpr, AstError{ + .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index }, + }); + const node = try arena.create(Node.Noasync); + node.* = .{ + .doc_comments = null, + .noasync_token = token, + .expr = expr_node, + }; + return &node.base; + } + if (eatToken(it, .Keyword_continue)) |token| { const label = try parseBreakLabel(arena, it, tree); const node = try arena.create(Node.ControlFlowExpression); - node.* = Node.ControlFlowExpression{ + node.* = .{ .ltoken = token, .kind = Node.ControlFlowExpression.Kind{ .Continue = label }, .rhs = null, @@ -907,7 +921,7 @@ fn parsePrimaryExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index }, }); const node = try arena.create(Node.PrefixOp); - node.* = Node.PrefixOp{ + node.* = .{ .op_token = token, .op = Node.PrefixOp.Op.Resume, .rhs = expr_node, @@ -918,7 +932,7 @@ fn parsePrimaryExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node if (eatToken(it, .Keyword_return)) |token| { const expr_node = try parseExpr(arena, it, tree); const node = try arena.create(Node.ControlFlowExpression); - node.* = Node.ControlFlowExpression{ + node.* = .{ .ltoken = token, .kind = Node.ControlFlowExpression.Kind.Return, .rhs = expr_node, @@ -1126,19 +1140,18 @@ fn parseErrorUnionExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*No /// SuffixExpr /// <- KEYWORD_async PrimaryTypeExpr SuffixOp* FnCallArguments -/// / KEYWORD_noasync PrimaryTypeExpr SuffixOp* FnCallArguments /// / PrimaryTypeExpr (SuffixOp / FnCallArguments)* fn parseSuffixExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { - const maybe_async = eatAnnotatedToken(it, .Keyword_async) orelse eatAnnotatedToken(it, .Keyword_noasync); + const maybe_async = eatToken(it, .Keyword_async); if (maybe_async) |async_token| { const token_fn = eatToken(it, .Keyword_fn); - if (async_token.ptr.id == .Keyword_async and token_fn != null) { + if (token_fn != null) { // HACK: If we see the keyword `fn`, then we assume that // we are parsing an async fn proto, and not a call. // We therefore put back all tokens consumed by the async // prefix... putBackToken(it, token_fn.?); - putBackToken(it, async_token.index); + putBackToken(it, async_token); return parsePrimaryTypeExpr(arena, it, tree); } // TODO: Implement hack for parsing `async fn ...` in ast_parse_suffix_expr @@ -1167,7 +1180,7 @@ fn parseSuffixExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { .op = Node.SuffixOp.Op{ .Call = Node.SuffixOp.Op.Call{ .params = params.list, - .async_token = async_token.index, + .async_token = async_token, }, }, .rtoken = params.rparen, @@ -1224,6 +1237,7 @@ fn parseSuffixExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { /// / IfTypeExpr /// / INTEGER /// / KEYWORD_comptime TypeExpr +/// / KEYWORD_noasync TypeExpr /// / KEYWORD_error DOT IDENTIFIER /// / KEYWORD_false /// / KEYWORD_null @@ -1255,13 +1269,23 @@ fn parsePrimaryTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*N if (eatToken(it, .Keyword_comptime)) |token| { const expr = (try parseTypeExpr(arena, it, tree)) orelse return null; const node = try arena.create(Node.Comptime); - node.* = Node.Comptime{ + node.* = .{ .doc_comments = null, .comptime_token = token, .expr = expr, }; return &node.base; } + if (eatToken(it, .Keyword_noasync)) |token| { + const expr = (try parseTypeExpr(arena, it, tree)) orelse return null; + const node = try arena.create(Node.Noasync); + node.* = .{ + .doc_comments = null, + .noasync_token = token, + .expr = expr, + }; + return &node.base; + } if (eatToken(it, .Keyword_error)) |token| { const period = try expectToken(it, tree, .Period); const identifier = try expectNode(arena, it, tree, parseIdentifier, AstError{ @@ -1269,7 +1293,7 @@ fn parsePrimaryTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*N }); const global_error_set = try createLiteral(arena, Node.ErrorType, token); const node = try arena.create(Node.InfixOp); - node.* = Node.InfixOp{ + node.* = .{ .op_token = period, .lhs = global_error_set, .op = Node.InfixOp.Op.Period, @@ -1281,7 +1305,7 @@ fn parsePrimaryTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*N if (eatToken(it, .Keyword_null)) |token| return createLiteral(arena, Node.NullLiteral, token); if (eatToken(it, .Keyword_anyframe)) |token| { const node = try arena.create(Node.AnyFrameType); - node.* = Node.AnyFrameType{ + node.* = .{ .anyframe_token = token, .result = null, }; @@ -2180,18 +2204,6 @@ fn parsePrefixOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { .Ampersand => ops{ .AddressOf = {} }, .Keyword_try => ops{ .Try = {} }, .Keyword_await => ops{ .Await = .{} }, - .Keyword_noasync => if (eatToken(it, .Keyword_await)) |await_tok| { - const node = try arena.create(Node.PrefixOp); - node.* = Node.PrefixOp{ - .op_token = await_tok, - .op = .{ .Await = .{ .noasync_token = token.index } }, - .rhs = undefined, // set by caller - }; - return &node.base; - } else { - putBackToken(it, token.index); - return null; - }, else => { putBackToken(it, token.index); return null; diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig index 625aef3131..ce9049e35b 100644 --- a/lib/std/zig/render.zig +++ b/lib/std/zig/render.zig @@ -390,6 +390,12 @@ fn renderExpression( try renderToken(tree, stream, comptime_node.comptime_token, indent, start_col, Space.Space); return renderExpression(allocator, stream, tree, indent, start_col, comptime_node.expr, space); }, + .Noasync => { + const noasync_node = @fieldParentPtr(ast.Node.Noasync, "base", base); + + try renderToken(tree, stream, noasync_node.noasync_token, indent, start_col, Space.Space); + return renderExpression(allocator, stream, tree, indent, start_col, noasync_node.expr, space); + }, .Suspend => { const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", base); @@ -590,9 +596,6 @@ fn renderExpression( }, .Await => |await_info| { - if (await_info.noasync_token) |tok| { - try renderToken(tree, stream, tok, indent, start_col, Space.Space); - } try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.Space); }, } diff --git a/src/all_types.hpp b/src/all_types.hpp index 2a3ab2041a..ec839ffb49 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -651,6 +651,7 @@ enum NodeType { NodeTypeSwitchProng, NodeTypeSwitchRange, NodeTypeCompTime, + NodeTypeNoAsync, NodeTypeBreak, NodeTypeContinue, NodeTypeAsmExpr, @@ -991,6 +992,10 @@ struct AstNodeCompTime { AstNode *expr; }; +struct AstNodeNoAsync { + AstNode *expr; +}; + struct AsmOutput { Buf *asm_symbolic_name; Buf *constraint; @@ -1148,7 +1153,6 @@ struct AstNodeErrorType { }; struct AstNodeAwaitExpr { - Token *noasync_token; AstNode *expr; }; @@ -1199,6 +1203,7 @@ struct AstNode { AstNodeSwitchProng switch_prong; AstNodeSwitchRange switch_range; AstNodeCompTime comptime_expr; + AstNodeNoAsync noasync_expr; AstNodeAsmExpr asm_expr; AstNodeFieldAccessExpr field_access_expr; AstNodePtrDerefExpr ptr_deref_expr; diff --git a/src/ast_render.cpp b/src/ast_render.cpp index d8d0d6c0e2..6bc21ce970 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -220,6 +220,8 @@ static const char *node_type_str(NodeType node_type) { return "SwitchRange"; case NodeTypeCompTime: return "CompTime"; + case NodeTypeNoAsync: + return "NoAsync"; case NodeTypeBreak: return "Break"; case NodeTypeContinue: @@ -1091,6 +1093,12 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { render_node_grouped(ar, node->data.comptime_expr.expr); break; } + case NodeTypeNoAsync: + { + fprintf(ar->f, "noasync "); + render_node_grouped(ar, node->data.noasync_expr.expr); + break; + } case NodeTypeForExpr: { if (node->data.for_expr.name != nullptr) { diff --git a/src/parser.cpp b/src/parser.cpp index 3a3e13f1d8..7ff24db184 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1237,6 +1237,7 @@ static AstNode *ast_parse_prefix_expr(ParseContext *pc) { // / IfExpr // / KEYWORD_break BreakLabel? Expr? // / KEYWORD_comptime Expr +// / KEYWORD_noasync Expr // / KEYWORD_continue BreakLabel? // / KEYWORD_resume Expr // / KEYWORD_return Expr? @@ -1271,6 +1272,14 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc) { return res; } + Token *noasync = eat_token_if(pc, TokenIdKeywordNoAsync); + if (noasync != nullptr) { + AstNode *expr = ast_expect(pc, ast_parse_expr); + AstNode *res = ast_create_node(pc, NodeTypeNoAsync, noasync); + res->data.noasync_expr.expr = expr; + return res; + } + Token *continue_token = eat_token_if(pc, TokenIdKeywordContinue); if (continue_token != nullptr) { Token *label = ast_parse_break_label(pc); @@ -1459,13 +1468,11 @@ static AstNode *ast_parse_error_union_expr(ParseContext *pc) { // SuffixExpr // <- KEYWORD_async PrimaryTypeExpr SuffixOp* FnCallArguments -// / KEYWORD_noasync PrimaryTypeExpr SuffixOp* FnCallArguments // / PrimaryTypeExpr (SuffixOp / FnCallArguments)* static AstNode *ast_parse_suffix_expr(ParseContext *pc) { - Token *async_token = eat_token(pc); - bool is_async = async_token->id == TokenIdKeywordAsync; - if (is_async || async_token->id == TokenIdKeywordNoAsync) { - if (is_async && eat_token_if(pc, TokenIdKeywordFn) != nullptr) { + Token *async_token = eat_token_if(pc, TokenIdKeywordAsync); + if (async_token) { + if (eat_token_if(pc, TokenIdKeywordFn) != nullptr) { // HACK: If we see the keyword `fn`, then we assume that // we are parsing an async fn proto, and not a call. // We therefore put back all tokens consumed by the async @@ -1515,13 +1522,12 @@ static AstNode *ast_parse_suffix_expr(ParseContext *pc) { assert(args->type == NodeTypeFnCallExpr); AstNode *res = ast_create_node(pc, NodeTypeFnCallExpr, async_token); - res->data.fn_call_expr.modifier = is_async ? CallModifierAsync : CallModifierNoAsync; + res->data.fn_call_expr.modifier = CallModifierAsync; res->data.fn_call_expr.seen = false; res->data.fn_call_expr.fn_ref_expr = child; res->data.fn_call_expr.params = args->data.fn_call_expr.params; return res; } - put_back_token(pc); AstNode *res = ast_parse_primary_type_expr(pc); if (res == nullptr) @@ -1582,6 +1588,7 @@ static AstNode *ast_parse_suffix_expr(ParseContext *pc) { // / IfTypeExpr // / INTEGER // / KEYWORD_comptime TypeExpr +// / KEYWORD_noasync TypeExpr // / KEYWORD_error DOT IDENTIFIER // / KEYWORD_false // / KEYWORD_null @@ -1683,6 +1690,14 @@ static AstNode *ast_parse_primary_type_expr(ParseContext *pc) { return res; } + Token *noasync = eat_token_if(pc, TokenIdKeywordNoAsync); + if (noasync != nullptr) { + AstNode *expr = ast_expect(pc, ast_parse_type_expr); + AstNode *res = ast_create_node(pc, NodeTypeNoAsync, noasync); + res->data.noasync_expr.expr = expr; + return res; + } + Token *error = eat_token_if(pc, TokenIdKeywordError); if (error != nullptr) { Token *dot = expect_token(pc, TokenIdDot); @@ -2599,14 +2614,10 @@ static AstNode *ast_parse_prefix_op(ParseContext *pc) { return res; } - Token *noasync_token = eat_token_if(pc, TokenIdKeywordNoAsync); Token *await = eat_token_if(pc, TokenIdKeywordAwait); if (await != nullptr) { AstNode *res = ast_create_node(pc, NodeTypeAwaitExpr, await); - res->data.await_expr.noasync_token = noasync_token; return res; - } else if (noasync_token != nullptr) { - put_back_token(pc); } return nullptr; @@ -3125,6 +3136,9 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont case NodeTypeCompTime: visit_field(&node->data.comptime_expr.expr, visit, context); break; + case NodeTypeNoAsync: + visit_field(&node->data.comptime_expr.expr, visit, context); + break; case NodeTypeBreak: // none break; From 3618256c97a9988f7d623eeabb667010ca30656f Mon Sep 17 00:00:00 2001 From: Vexu Date: Mon, 9 Mar 2020 12:31:36 +0200 Subject: [PATCH 2/4] implement noasync scopes --- src/all_types.hpp | 6 +++++ src/analyze.cpp | 9 +++++++ src/analyze.hpp | 1 + src/codegen.cpp | 2 ++ src/ir.cpp | 52 +++++++++++++++++++++++++++++++++++++++-- test/compile_errors.zig | 9 +++++++ 6 files changed, 77 insertions(+), 2 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index ec839ffb49..14b99228ca 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2330,6 +2330,7 @@ enum ScopeId { ScopeIdRuntime, ScopeIdTypeOf, ScopeIdExpr, + ScopeIdNoAsync, }; struct Scope { @@ -2462,6 +2463,11 @@ struct ScopeCompTime { Scope base; }; +// This scope is created for a noasync expression. +// NodeTypeNoAsync +struct ScopeNoAsync { + Scope base; +}; // This scope is created for a function definition. // NodeTypeFnDef diff --git a/src/analyze.cpp b/src/analyze.cpp index c7bd7094d7..d924002426 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -106,6 +106,7 @@ static ScopeExpr *find_expr_scope(Scope *scope) { case ScopeIdDecls: case ScopeIdFnDef: case ScopeIdCompTime: + case ScopeIdNoAsync: case ScopeIdVarDecl: case ScopeIdCImport: case ScopeIdSuspend: @@ -226,6 +227,12 @@ Scope *create_comptime_scope(CodeGen *g, AstNode *node, Scope *parent) { return &scope->base; } +Scope *create_noasync_scope(CodeGen *g, AstNode *node, Scope *parent) { + ScopeNoAsync *scope = heap::c_allocator.create(); + init_scope(g, &scope->base, ScopeIdNoAsync, node, parent); + return &scope->base; +} + Scope *create_typeof_scope(CodeGen *g, AstNode *node, Scope *parent) { ScopeTypeOf *scope = heap::c_allocator.create(); init_scope(g, &scope->base, ScopeIdTypeOf, node, parent); @@ -3755,6 +3762,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) { case NodeTypeCompTime: preview_comptime_decl(g, node, decls_scope); break; + case NodeTypeNoAsync: case NodeTypeParamDecl: case NodeTypeReturnExpr: case NodeTypeDefer: @@ -6176,6 +6184,7 @@ static void mark_suspension_point(Scope *scope) { case ScopeIdDecls: case ScopeIdFnDef: case ScopeIdCompTime: + case ScopeIdNoAsync: case ScopeIdCImport: case ScopeIdSuspend: case ScopeIdTypeOf: diff --git a/src/analyze.hpp b/src/analyze.hpp index fded1e4052..98d09d452f 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -125,6 +125,7 @@ ScopeLoop *create_loop_scope(CodeGen *g, AstNode *node, Scope *parent); ScopeSuspend *create_suspend_scope(CodeGen *g, AstNode *node, Scope *parent); ScopeFnDef *create_fndef_scope(CodeGen *g, AstNode *node, Scope *parent, ZigFn *fn_entry); Scope *create_comptime_scope(CodeGen *g, AstNode *node, Scope *parent); +Scope *create_noasync_scope(CodeGen *g, AstNode *node, Scope *parent); Scope *create_runtime_scope(CodeGen *g, AstNode *node, Scope *parent, IrInstSrc *is_comptime); Scope *create_typeof_scope(CodeGen *g, AstNode *node, Scope *parent); ScopeExpr *create_expr_scope(CodeGen *g, AstNode *node, Scope *parent); diff --git a/src/codegen.cpp b/src/codegen.cpp index ef567c27aa..52ac2e7936 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -687,6 +687,7 @@ static ZigLLVMDIScope *get_di_scope(CodeGen *g, Scope *scope) { case ScopeIdLoop: case ScopeIdSuspend: case ScopeIdCompTime: + case ScopeIdNoAsync: case ScopeIdRuntime: case ScopeIdTypeOf: case ScopeIdExpr: @@ -3934,6 +3935,7 @@ static void render_async_var_decls(CodeGen *g, Scope *scope) { case ScopeIdLoop: case ScopeIdSuspend: case ScopeIdCompTime: + case ScopeIdNoAsync: case ScopeIdRuntime: case ScopeIdTypeOf: case ScopeIdExpr: diff --git a/src/ir.cpp b/src/ir.cpp index 110778fb9a..6a387e12e0 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -4978,6 +4978,7 @@ static void ir_count_defers(IrBuilderSrc *irb, Scope *inner_scope, Scope *outer_ case ScopeIdLoop: case ScopeIdSuspend: case ScopeIdCompTime: + case ScopeIdNoAsync: case ScopeIdRuntime: case ScopeIdTypeOf: case ScopeIdExpr: @@ -5033,6 +5034,7 @@ static bool ir_gen_defers_for_block(IrBuilderSrc *irb, Scope *inner_scope, Scope case ScopeIdLoop: case ScopeIdSuspend: case ScopeIdCompTime: + case ScopeIdNoAsync: case ScopeIdRuntime: case ScopeIdTypeOf: case ScopeIdExpr: @@ -7307,6 +7309,31 @@ static IrInstSrc *ir_gen_builtin_fn_call(IrBuilderSrc *irb, Scope *scope, AstNod zig_unreachable(); } +static bool is_noasync_scope(Scope *scope) { + for (;;) { + switch (scope->id) { + case ScopeIdNoAsync: + return true; + case ScopeIdDefer: + case ScopeIdDeferExpr: + case ScopeIdDecls: + case ScopeIdFnDef: + case ScopeIdCompTime: + case ScopeIdVarDecl: + case ScopeIdCImport: + case ScopeIdSuspend: + return false; + case ScopeIdExpr: + case ScopeIdTypeOf: + case ScopeIdBlock: + case ScopeIdLoop: + case ScopeIdRuntime: + scope = scope->parent; + continue; + } + } +} + static IrInstSrc *ir_gen_fn_call(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, ResultLoc *result_loc) { @@ -7315,8 +7342,19 @@ static IrInstSrc *ir_gen_fn_call(IrBuilderSrc *irb, Scope *scope, AstNode *node, if (node->data.fn_call_expr.modifier == CallModifierBuiltin) return ir_gen_builtin_fn_call(irb, scope, node, lval, result_loc); + bool is_noasync = is_noasync_scope(scope); + CallModifier modifier = node->data.fn_call_expr.modifier; + if (is_noasync) { + if (modifier == CallModifierAsync) { + add_node_error(irb->codegen, node, + buf_sprintf("async call in noasync scope")); + return irb->codegen->invalid_inst_src; + } + modifier = CallModifierNoAsync; + } + AstNode *fn_ref_node = node->data.fn_call_expr.fn_ref_expr; - return ir_gen_fn_call_with_args(irb, scope, node, fn_ref_node, node->data.fn_call_expr.modifier, + return ir_gen_fn_call_with_args(irb, scope, node, fn_ref_node, modifier, nullptr, node->data.fn_call_expr.params.items, node->data.fn_call_expr.params.length, lval, result_loc); } @@ -9170,6 +9208,14 @@ static IrInstSrc *ir_gen_comptime(IrBuilderSrc *irb, Scope *parent_scope, AstNod return ir_gen_node_extra(irb, node->data.comptime_expr.expr, child_scope, lval, nullptr); } +static IrInstSrc *ir_gen_noasync(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node, LVal lval) { + assert(node->type == NodeTypeNoAsync); + + Scope *child_scope = create_noasync_scope(irb->codegen, node, parent_scope); + // purposefully pass null for result_loc and let EndExpr handle it + return ir_gen_node_extra(irb, node->data.comptime_expr.expr, child_scope, lval, nullptr); +} + static IrInstSrc *ir_gen_return_from_block(IrBuilderSrc *irb, Scope *break_scope, AstNode *node, ScopeBlock *block_scope) { IrInstSrc *is_comptime; if (ir_should_inline(irb->exec, break_scope)) { @@ -9763,7 +9809,7 @@ static IrInstSrc *ir_gen_await_expr(IrBuilderSrc *irb, Scope *scope, AstNode *no { assert(node->type == NodeTypeAwaitExpr); - bool is_noasync = node->data.await_expr.noasync_token != nullptr; + bool is_noasync = is_noasync_scope(scope); AstNode *expr_node = node->data.await_expr.expr; if (expr_node->type == NodeTypeFnCallExpr && expr_node->data.fn_call_expr.modifier == CallModifierBuiltin) { @@ -9938,6 +9984,8 @@ static IrInstSrc *ir_gen_node_raw(IrBuilderSrc *irb, AstNode *node, Scope *scope return ir_gen_switch_expr(irb, scope, node, lval, result_loc); case NodeTypeCompTime: return ir_expr_wrap(irb, scope, ir_gen_comptime(irb, scope, node, lval), result_loc); + case NodeTypeNoAsync: + return ir_expr_wrap(irb, scope, ir_gen_noasync(irb, scope, node, lval), result_loc); case NodeTypeErrorType: return ir_lval_wrap(irb, scope, ir_gen_error_type(irb, scope, node), lval, result_loc); case NodeTypeBreak: diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 717793baaa..7021f81554 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2,6 +2,15 @@ const tests = @import("tests.zig"); const std = @import("std"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.addTest("combination of noasync and async", + \\export fn entry() void { + \\ noasync async foo(); + \\} + \\fn foo() void {} + , &[_][]const u8{ + "tmp.zig:2:13: error: async call in noasync scope", + }); + cases.addTest("@TypeOf with no arguments", \\export fn entry() void { \\ _ = @TypeOf(); From 03c1431f9c0b651f3f1853f11112edc07555883d Mon Sep 17 00:00:00 2001 From: Vexu Date: Mon, 9 Mar 2020 15:51:51 +0200 Subject: [PATCH 3/4] disallow resume and suspend in noasync scopes --- lib/std/zig/ast.zig | 1 - lib/std/zig/parse.zig | 16 ++++++++++++++-- src/ir.cpp | 44 ++++++++++++++++++++----------------------- src/parser.cpp | 9 +++++++++ 4 files changed, 43 insertions(+), 27 deletions(-) diff --git a/lib/std/zig/ast.zig b/lib/std/zig/ast.zig index 711806b810..5411519667 100644 --- a/lib/std/zig/ast.zig +++ b/lib/std/zig/ast.zig @@ -1081,7 +1081,6 @@ pub const Node = struct { pub const Noasync = struct { base: Node = Node{ .id = .Noasync }, - doc_comments: ?*DocComment, noasync_token: TokenIndex, expr: *Node, diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index 5392c18dfe..9a574c6231 100644 --- a/lib/std/zig/parse.zig +++ b/lib/std/zig/parse.zig @@ -462,6 +462,7 @@ fn parseContainerField(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*No /// Statement /// <- KEYWORD_comptime? VarDecl /// / KEYWORD_comptime BlockExprStatement +/// / KEYWORD_noasync BlockExprStatement /// / KEYWORD_suspend (SEMICOLON / BlockExprStatement) /// / KEYWORD_defer BlockExprStatement /// / KEYWORD_errdefer BlockExprStatement @@ -493,6 +494,19 @@ fn parseStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) Error!?*No return &node.base; } + if (eatToken(it, .Keyword_noasync)) |noasync_token| { + const block_expr = try expectNode(arena, it, tree, parseBlockExprStatement, .{ + .ExpectedBlockOrAssignment = .{ .token = it.index }, + }); + + const node = try arena.create(Node.Noasync); + node.* = .{ + .noasync_token = noasync_token, + .expr = block_expr, + }; + return &node.base; + } + if (eatToken(it, .Keyword_suspend)) |suspend_token| { const semicolon = eatToken(it, .Semicolon); @@ -898,7 +912,6 @@ fn parsePrimaryExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node }); const node = try arena.create(Node.Noasync); node.* = .{ - .doc_comments = null, .noasync_token = token, .expr = expr_node, }; @@ -1280,7 +1293,6 @@ fn parsePrimaryTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*N const expr = (try parseTypeExpr(arena, it, tree)) orelse return null; const node = try arena.create(Node.Noasync); node.* = .{ - .doc_comments = null, .noasync_token = token, .expr = expr, }; diff --git a/src/ir.cpp b/src/ir.cpp index 6a387e12e0..50ace888cd 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7309,29 +7309,16 @@ static IrInstSrc *ir_gen_builtin_fn_call(IrBuilderSrc *irb, Scope *scope, AstNod zig_unreachable(); } -static bool is_noasync_scope(Scope *scope) { - for (;;) { - switch (scope->id) { - case ScopeIdNoAsync: - return true; - case ScopeIdDefer: - case ScopeIdDeferExpr: - case ScopeIdDecls: - case ScopeIdFnDef: - case ScopeIdCompTime: - case ScopeIdVarDecl: - case ScopeIdCImport: - case ScopeIdSuspend: - return false; - case ScopeIdExpr: - case ScopeIdTypeOf: - case ScopeIdBlock: - case ScopeIdLoop: - case ScopeIdRuntime: - scope = scope->parent; - continue; - } +static ScopeNoAsync *get_scope_noasync(Scope *scope) { + while (scope) { + if (scope->id == ScopeIdNoAsync) + return (ScopeNoAsync *)scope; + if (scope->id == ScopeIdFnDef) + return nullptr; + + scope = scope->parent; } + return nullptr; } static IrInstSrc *ir_gen_fn_call(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, @@ -7342,7 +7329,7 @@ static IrInstSrc *ir_gen_fn_call(IrBuilderSrc *irb, Scope *scope, AstNode *node, if (node->data.fn_call_expr.modifier == CallModifierBuiltin) return ir_gen_builtin_fn_call(irb, scope, node, lval, result_loc); - bool is_noasync = is_noasync_scope(scope); + bool is_noasync = get_scope_noasync(scope) != nullptr; CallModifier modifier = node->data.fn_call_expr.modifier; if (is_noasync) { if (modifier == CallModifierAsync) { @@ -9796,6 +9783,10 @@ static IrInstSrc *ir_gen_fn_proto(IrBuilderSrc *irb, Scope *parent_scope, AstNod static IrInstSrc *ir_gen_resume(IrBuilderSrc *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeResume); + if (get_scope_noasync(scope) != nullptr) { + add_node_error(irb->codegen, node, buf_sprintf("resume in noasync scope")); + return irb->codegen->invalid_inst_src; + } IrInstSrc *target_inst = ir_gen_node_extra(irb, node->data.resume_expr.expr, scope, LValPtr, nullptr); if (target_inst == irb->codegen->invalid_inst_src) @@ -9809,7 +9800,7 @@ static IrInstSrc *ir_gen_await_expr(IrBuilderSrc *irb, Scope *scope, AstNode *no { assert(node->type == NodeTypeAwaitExpr); - bool is_noasync = is_noasync_scope(scope); + bool is_noasync = get_scope_noasync(scope) != nullptr; AstNode *expr_node = node->data.await_expr.expr; if (expr_node->type == NodeTypeFnCallExpr && expr_node->data.fn_call_expr.modifier == CallModifierBuiltin) { @@ -9855,6 +9846,11 @@ static IrInstSrc *ir_gen_suspend(IrBuilderSrc *irb, Scope *parent_scope, AstNode add_node_error(irb->codegen, node, buf_sprintf("suspend outside function definition")); return irb->codegen->invalid_inst_src; } + if (get_scope_noasync(parent_scope) != nullptr) { + add_node_error(irb->codegen, node, buf_sprintf("suspend in noasync scope")); + return irb->codegen->invalid_inst_src; + } + ScopeSuspend *existing_suspend_scope = get_scope_suspend(parent_scope); if (existing_suspend_scope) { if (!existing_suspend_scope->reported_err) { diff --git a/src/parser.cpp b/src/parser.cpp index 7ff24db184..cdd254962e 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -876,6 +876,7 @@ static AstNode *ast_parse_container_field(ParseContext *pc) { // Statement // <- KEYWORD_comptime? VarDecl // / KEYWORD_comptime BlockExprStatement +// / KEYWORD_noasync BlockExprStatement // / KEYWORD_suspend (SEMICOLON / BlockExprStatement) // / KEYWORD_defer BlockExprStatement // / KEYWORD_errdefer BlockExprStatement @@ -899,6 +900,14 @@ static AstNode *ast_parse_statement(ParseContext *pc) { return res; } + Token *noasync = eat_token_if(pc, TokenIdKeywordNoAsync); + if (noasync != nullptr) { + AstNode *statement = ast_expect(pc, ast_parse_block_expr_statement); + AstNode *res = ast_create_node(pc, NodeTypeNoAsync, noasync); + res->data.noasync_expr.expr = statement; + return res; + } + Token *suspend = eat_token_if(pc, TokenIdKeywordSuspend); if (suspend != nullptr) { AstNode *statement = nullptr; From 3fd2cd43678d52ca2f737d991edaddfd8b73c2a4 Mon Sep 17 00:00:00 2001 From: Vexu Date: Mon, 9 Mar 2020 18:38:54 +0200 Subject: [PATCH 4/4] add LemonBoy's test --- src/codegen.cpp | 2 +- test/compile_errors.zig | 10 ++++++++-- test/stage1/behavior/async_fn.zig | 16 ++++++++++++++++ 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 52ac2e7936..a67b8dc00b 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -969,7 +969,7 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) { case PanicMsgIdResumedFnPendingAwait: return buf_create_from_str("resumed an async function which can only be awaited"); case PanicMsgIdBadNoAsyncCall: - return buf_create_from_str("async function called with noasync suspended"); + return buf_create_from_str("async function called in noasync scope suspended"); case PanicMsgIdResumeNotSuspendedFn: return buf_create_from_str("resumed a non-suspended function"); case PanicMsgIdBadSentinel: diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 7021f81554..4434920224 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -4,11 +4,17 @@ const std = @import("std"); pub fn addCases(cases: *tests.CompileErrorContext) void { cases.addTest("combination of noasync and async", \\export fn entry() void { - \\ noasync async foo(); + \\ noasync { + \\ const bar = async foo(); + \\ suspend; + \\ resume bar; + \\ } \\} \\fn foo() void {} , &[_][]const u8{ - "tmp.zig:2:13: error: async call in noasync scope", + "tmp.zig:3:21: error: async call in noasync scope", + "tmp.zig:4:9: error: suspend in noasync scope", + "tmp.zig:5:9: error: resume in noasync scope", }); cases.addTest("@TypeOf with no arguments", diff --git a/test/stage1/behavior/async_fn.zig b/test/stage1/behavior/async_fn.zig index 6583adec66..6b99187563 100644 --- a/test/stage1/behavior/async_fn.zig +++ b/test/stage1/behavior/async_fn.zig @@ -1531,3 +1531,19 @@ test "noasync await" { S.doTheTest(); expect(S.finished); } + +test "noasync on function calls" { + const S0 = struct { + b: i32 = 42, + }; + const S1 = struct { + fn c() S0 { + return S0{}; + } + fn d() !S0 { + return S0{}; + } + }; + expectEqual(@as(i32, 42), noasync S1.c().b); + expectEqual(@as(i32, 42), (try noasync S1.d()).b); +}