Commit 0920bb0872

Andrew Kelley <andrew@ziglang.org>
2019-08-03 01:27:27
implement async functions returning structs
1 parent 5bd330e
Changed files (3)
src
test
stage1
src/codegen.cpp
@@ -503,7 +503,7 @@ static LLVMValueRef make_fn_llvm_value(CodeGen *g, ZigFn *fn) {
         // nothing to do
     } else if (type_is_nonnull_ptr(return_type)) {
         addLLVMAttr(llvm_fn, 0, "nonnull");
-    } else if (want_first_arg_sret(g, &fn_type->data.fn.fn_type_id)) {
+    } else if (!is_async && want_first_arg_sret(g, &fn_type->data.fn.fn_type_id)) {
         // Sret pointers must not be address 0
         addLLVMArgAttr(llvm_fn, 0, "nonnull");
         addLLVMArgAttr(llvm_fn, 0, "sret");
@@ -3241,8 +3241,13 @@ static LLVMValueRef ir_render_var_ptr(CodeGen *g, IrExecutable *executable, IrIn
 static LLVMValueRef ir_render_return_ptr(CodeGen *g, IrExecutable *executable,
         IrInstructionReturnPtr *instruction)
 {
-    src_assert(g->cur_ret_ptr != nullptr || !type_has_bits(instruction->base.value.type),
-            instruction->base.source_node);
+    if (!type_has_bits(instruction->base.value.type))
+        return nullptr;
+    src_assert(g->cur_ret_ptr != nullptr, instruction->base.source_node);
+    if (fn_is_async(g->cur_fn)) {
+        LLVMValueRef ptr_ptr = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr, coro_arg_start, "");
+        return LLVMBuildLoad(g->builder, ptr_ptr, "");
+    }
     return g->cur_ret_ptr;
 }
 
@@ -3506,6 +3511,12 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
         if (ret_has_bits) {
             ret_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, coro_arg_start + 1, "");
         }
+
+        // Use the result location which is inside the frame if this is an async call.
+        if (ret_has_bits) {
+            LLVMValueRef ret_ptr_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, coro_arg_start, "");
+            LLVMBuildStore(g->builder, ret_ptr, ret_ptr_ptr);
+        }
     } else if (callee_is_async) {
         frame_result_loc = ir_llvm_value(g, instruction->frame_result_loc);
         awaiter_init_val = LLVMBuildPtrToInt(g->builder, g->cur_ret_ptr,
@@ -3513,6 +3524,12 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
         if (ret_has_bits) {
             ret_ptr = result_loc;
         }
+
+        // Use the call instruction's result location.
+        if (ret_has_bits) {
+            LLVMValueRef ret_ptr_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, coro_arg_start, "");
+            LLVMBuildStore(g->builder, result_loc, ret_ptr_ptr);
+        }
     }
     if (instruction->is_async || callee_is_async) {
         assert(frame_result_loc != nullptr);
@@ -3525,10 +3542,6 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
         LLVMValueRef awaiter_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, coro_awaiter_index, "");
         LLVMBuildStore(g->builder, awaiter_init_val, awaiter_ptr);
 
-        if (ret_has_bits) {
-            LLVMValueRef ret_ptr_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, coro_arg_start, "");
-            LLVMBuildStore(g->builder, ret_ptr, ret_ptr_ptr);
-        }
     }
     if (!instruction->is_async && !callee_is_async) {
         if (first_arg_ret) {
test/stage1/behavior/coroutines.zig
@@ -1,6 +1,7 @@
 const std = @import("std");
 const builtin = @import("builtin");
 const expect = std.testing.expect;
+const expectEqual = std.testing.expectEqual;
 
 var global_x: i32 = 1;
 
@@ -357,3 +358,44 @@ test "heap allocated async function frame" {
     };
     try S.doTheTest();
 }
+
+test "async function call return value" {
+    const S = struct {
+        var frame: anyframe = undefined;
+        var pt = Point{.x = 10, .y = 11 };
+
+        fn doTheTest() void {
+            expectEqual(pt.x, 10);
+            expectEqual(pt.y, 11);
+            _ = async first();
+            expectEqual(pt.x, 10);
+            expectEqual(pt.y, 11);
+            resume frame;
+            expectEqual(pt.x, 1);
+            expectEqual(pt.y, 2);
+        }
+
+        fn first() void {
+            pt = second(1, 2);
+        }
+
+        fn second(x: i32, y: i32) Point {
+            return other(x, y);
+        }
+
+        fn other(x: i32, y: i32) Point {
+            frame = @frame();
+            suspend;
+            return Point{
+                .x = x,
+                .y = y,
+            };
+        }
+
+        const Point = struct {
+            x: i32,
+            y: i32,
+        };
+    };
+    S.doTheTest();
+}
BRANCH_TODO
@@ -1,3 +1,4 @@
+ * struct types as the return type of an async function. make sure it works with return result locations.
  * compile error for error: expected anyframe->T, found 'anyframe'
  * compile error for error: expected anyframe->T, found 'i32'
  * await of a non async function
@@ -15,5 +16,6 @@
  * peer type resolution of *@Frame(func) and anyframe
  * peer type resolution of *@Frame(func) and anyframe->T when the return type matches
  * returning a value from within a suspend block
- * struct types as the return type of an async function. make sure it works with return result locations.
  * make resuming inside a suspend block, with nothing after it, a must-tail call.
+ * make sure there are safety tests for all the new safety features (search the new PanicFnId enum values)
+ * error return tracing