Commit 7f7d1fbe5a

Andrew Kelley <andrew@ziglang.org>
2020-02-16 07:32:15
Implement noasync awaits
Note that there is not yet runtime safety for this. See #3157
1 parent cb3a818
Changed files (6)
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 {
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;
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);
     }
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<IrInstSrcAwait>(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<IrInstGenAwait>(&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);
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;
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);
 }