From 7f7d1fbe5ad16fd41eeaeb20e3e71a39bdd3f991 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 16 Feb 2020 01:32:15 -0500 Subject: [PATCH] Implement noasync awaits Note that there is not yet runtime safety for this. See #3157 --- src/all_types.hpp | 3 +++ src/analyze.cpp | 8 ++++---- src/codegen.cpp | 4 +++- src/ir.cpp | 15 ++++++++++----- src/parser.cpp | 4 ++++ test/stage1/behavior/async_fn.zig | 4 ++++ 6 files changed, 28 insertions(+), 10 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index b514f4ecbc..95f291b343 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1139,6 +1139,7 @@ struct AstNodeErrorType { }; struct AstNodeAwaitExpr { + Token *noasync_token; AstNode *expr; }; @@ -4500,6 +4501,7 @@ struct IrInstSrcAwait { IrInstSrc *frame; ResultLoc *result_loc; + bool is_noasync; }; struct IrInstGenAwait { @@ -4508,6 +4510,7 @@ struct IrInstGenAwait { IrInstGen *frame; IrInstGen *result_loc; ZigFn *target_fn; + bool is_noasync; }; struct IrInstSrcResume { diff --git a/src/analyze.cpp b/src/analyze.cpp index fc1a805d82..98366bc87d 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -4710,8 +4710,7 @@ static void analyze_fn_async(CodeGen *g, ZigFn *fn, bool resolve_frame) { } for (size_t i = 0; i < fn->await_list.length; i += 1) { IrInstGenAwait *await = fn->await_list.at(i); - // TODO If this is a noasync await, it doesn't count - // https://github.com/ziglang/zig/issues/3157 + if (await->is_noasync) continue; switch (analyze_callee_async(g, fn, await->target_fn, await->base.base.source_node, must_not_be_async, CallModifierNone)) { @@ -6315,8 +6314,9 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) { // The funtion call result of foo() must be spilled. for (size_t i = 0; i < fn->await_list.length; i += 1) { IrInstGenAwait *await = fn->await_list.at(i); - // TODO If this is a noasync await, it doesn't suspend - // https://github.com/ziglang/zig/issues/3157 + if (await->is_noasync) { + continue; + } if (await->base.value->special != ConstValSpecialRuntime) { // Known at comptime. No spill, no suspend. continue; diff --git a/src/codegen.cpp b/src/codegen.cpp index 0f6988f986..a0bc56f093 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6188,7 +6188,9 @@ static LLVMValueRef ir_render_await(CodeGen *g, IrExecutableGen *executable, IrI LLVMValueRef result_loc = (instruction->result_loc == nullptr) ? nullptr : ir_llvm_value(g, instruction->result_loc); - if (instruction->target_fn != nullptr && !fn_is_async(instruction->target_fn)) { + if (instruction->is_noasync || + (instruction->target_fn != nullptr && !fn_is_async(instruction->target_fn))) + { return gen_await_early_return(g, &instruction->base, target_frame_ptr, result_type, ptr_result_type, result_loc, true); } diff --git a/src/ir.cpp b/src/ir.cpp index 323fa4ba6f..c87424b25e 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -4956,11 +4956,12 @@ static IrInstGen *ir_build_suspend_finish_gen(IrAnalyze *ira, IrInst *source_ins } static IrInstSrc *ir_build_await_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *frame, ResultLoc *result_loc) + IrInstSrc *frame, ResultLoc *result_loc, bool is_noasync) { IrInstSrcAwait *instruction = ir_build_instruction(irb, scope, source_node); instruction->frame = frame; instruction->result_loc = result_loc; + instruction->is_noasync = is_noasync; ir_ref_instruction(frame, irb->current_basic_block); @@ -4968,13 +4969,14 @@ static IrInstSrc *ir_build_await_src(IrBuilderSrc *irb, Scope *scope, AstNode *s } static IrInstGenAwait *ir_build_await_gen(IrAnalyze *ira, IrInst *source_instruction, - IrInstGen *frame, ZigType *result_type, IrInstGen *result_loc) + IrInstGen *frame, ZigType *result_type, IrInstGen *result_loc, bool is_noasync) { IrInstGenAwait *instruction = ir_build_inst_gen(&ira->new_irb, source_instruction->scope, source_instruction->source_node); instruction->base.value->type = result_type; instruction->frame = frame; instruction->result_loc = result_loc; + instruction->is_noasync = is_noasync; ir_ref_inst_gen(frame, ira->new_irb.current_basic_block); if (result_loc != nullptr) ir_ref_inst_gen(result_loc, ira->new_irb.current_basic_block); @@ -9953,6 +9955,8 @@ 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; + AstNode *expr_node = node->data.await_expr.expr; if (expr_node->type == NodeTypeFnCallExpr && expr_node->data.fn_call_expr.modifier == CallModifierBuiltin) { AstNode *fn_ref_expr = expr_node->data.fn_call_expr.fn_ref_expr; @@ -9985,7 +9989,7 @@ static IrInstSrc *ir_gen_await_expr(IrBuilderSrc *irb, Scope *scope, AstNode *no if (target_inst == irb->codegen->invalid_inst_src) return irb->codegen->invalid_inst_src; - IrInstSrc *await_inst = ir_build_await_src(irb, scope, node, target_inst, result_loc); + IrInstSrc *await_inst = ir_build_await_src(irb, scope, node, target_inst, result_loc, is_noasync); return ir_lval_wrap(irb, scope, await_inst, lval, result_loc); } @@ -29505,7 +29509,7 @@ static IrInstGen *ir_analyze_instruction_await(IrAnalyze *ira, IrInstSrcAwait *i ir_assert(fn_entry != nullptr, &instruction->base.base); // If it's not @Frame(func) then it's definitely a suspend point - if (target_fn == nullptr) { + if (target_fn == nullptr && !instruction->is_noasync) { if (fn_entry->inferred_async_node == nullptr) { fn_entry->inferred_async_node = instruction->base.base.source_node; } @@ -29528,7 +29532,8 @@ static IrInstGen *ir_analyze_instruction_await(IrAnalyze *ira, IrInstSrcAwait *i result_loc = nullptr; } - IrInstGenAwait *result = ir_build_await_gen(ira, &instruction->base.base, frame, result_type, result_loc); + IrInstGenAwait *result = ir_build_await_gen(ira, &instruction->base.base, frame, result_type, result_loc, + instruction->is_noasync); result->target_fn = target_fn; fn_entry->await_list.append(result); return ir_finish_anal(ira, &result->base); diff --git a/src/parser.cpp b/src/parser.cpp index ef2121e20d..c8c53e81b7 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2596,10 +2596,14 @@ 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; diff --git a/test/stage1/behavior/async_fn.zig b/test/stage1/behavior/async_fn.zig index 029a4edb7c..b04d4e2b5d 100644 --- a/test/stage1/behavior/async_fn.zig +++ b/test/stage1/behavior/async_fn.zig @@ -1513,9 +1513,12 @@ test "take address of temporary async frame" { test "noasync await" { const S = struct { + var finished = false; + fn doTheTest() void { var frame = async foo(false); expect(noasync await frame == 42); + finished = true; } fn foo(want_suspend: bool) i32 { @@ -1526,4 +1529,5 @@ test "noasync await" { } }; S.doTheTest(); + expect(S.finished); }