authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-08-02 19:27:27-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-08-02 19:27:27-04:00
log0920bb087279bdfa317fc88cef877d40ece18239
treeed7b5802757557ad499a70b64f0c84895c98fcf7
parent5bd330e76c4b9bfc341dcba1732a8ac9277e133d
signature Commit is signed but in an unrecognized format.

implement async functions returning structs


3 files changed, 65 insertions(+), 8 deletions(-)

BRANCH_TODO+3-1
......@@ -1,3 +1,4 @@
1 * struct types as the return type of an async function. make sure it works with return result locations.
12 * compile error for error: expected anyframe->T, found 'anyframe'
23 * compile error for error: expected anyframe->T, found 'i32'
34 * await of a non async function
......@@ -15,5 +16,6 @@
1516 * peer type resolution of *@Frame(func) and anyframe
1617 * peer type resolution of *@Frame(func) and anyframe->T when the return type matches
1718 * returning a value from within a suspend block
18 * struct types as the return type of an async function. make sure it works with return result locations.
1919 * make resuming inside a suspend block, with nothing after it, a must-tail call.
20 * make sure there are safety tests for all the new safety features (search the new PanicFnId enum values)
21 * error return tracing
src/codegen.cpp+20-7
......@@ -503,7 +503,7 @@ static LLVMValueRef make_fn_llvm_value(CodeGen *g, ZigFn *fn) {
503503 // nothing to do
504504 } else if (type_is_nonnull_ptr(return_type)) {
505505 addLLVMAttr(llvm_fn, 0, "nonnull");
506 } else if (want_first_arg_sret(g, &fn_type->data.fn.fn_type_id)) {
506 } else if (!is_async && want_first_arg_sret(g, &fn_type->data.fn.fn_type_id)) {
507507 // Sret pointers must not be address 0
508508 addLLVMArgAttr(llvm_fn, 0, "nonnull");
509509 addLLVMArgAttr(llvm_fn, 0, "sret");
......@@ -3241,8 +3241,13 @@ static LLVMValueRef ir_render_var_ptr(CodeGen *g, IrExecutable *executable, IrIn
32413241static LLVMValueRef ir_render_return_ptr(CodeGen *g, IrExecutable *executable,
32423242 IrInstructionReturnPtr *instruction)
32433243{
3244 src_assert(g->cur_ret_ptr != nullptr || !type_has_bits(instruction->base.value.type),
3245 instruction->base.source_node);
3244 if (!type_has_bits(instruction->base.value.type))
3245 return nullptr;
3246 src_assert(g->cur_ret_ptr != nullptr, instruction->base.source_node);
3247 if (fn_is_async(g->cur_fn)) {
3248 LLVMValueRef ptr_ptr = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr, coro_arg_start, "");
3249 return LLVMBuildLoad(g->builder, ptr_ptr, "");
3250 }
32463251 return g->cur_ret_ptr;
32473252}
32483253
......@@ -3506,6 +3511,12 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
35063511 if (ret_has_bits) {
35073512 ret_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, coro_arg_start + 1, "");
35083513 }
3514
3515 // Use the result location which is inside the frame if this is an async call.
3516 if (ret_has_bits) {
3517 LLVMValueRef ret_ptr_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, coro_arg_start, "");
3518 LLVMBuildStore(g->builder, ret_ptr, ret_ptr_ptr);
3519 }
35093520 } else if (callee_is_async) {
35103521 frame_result_loc = ir_llvm_value(g, instruction->frame_result_loc);
35113522 awaiter_init_val = LLVMBuildPtrToInt(g->builder, g->cur_ret_ptr,
......@@ -3513,6 +3524,12 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
35133524 if (ret_has_bits) {
35143525 ret_ptr = result_loc;
35153526 }
3527
3528 // Use the call instruction's result location.
3529 if (ret_has_bits) {
3530 LLVMValueRef ret_ptr_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, coro_arg_start, "");
3531 LLVMBuildStore(g->builder, result_loc, ret_ptr_ptr);
3532 }
35163533 }
35173534 if (instruction->is_async || callee_is_async) {
35183535 assert(frame_result_loc != nullptr);
......@@ -3525,10 +3542,6 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
35253542 LLVMValueRef awaiter_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, coro_awaiter_index, "");
35263543 LLVMBuildStore(g->builder, awaiter_init_val, awaiter_ptr);
35273544
3528 if (ret_has_bits) {
3529 LLVMValueRef ret_ptr_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, coro_arg_start, "");
3530 LLVMBuildStore(g->builder, ret_ptr, ret_ptr_ptr);
3531 }
35323545 }
35333546 if (!instruction->is_async && !callee_is_async) {
35343547 if (first_arg_ret) {
test/stage1/behavior/coroutines.zig+42
......@@ -1,6 +1,7 @@
11const std = @import("std");
22const builtin = @import("builtin");
33const expect = std.testing.expect;
4const expectEqual = std.testing.expectEqual;
45
56var global_x: i32 = 1;
67
......@@ -357,3 +358,44 @@ test "heap allocated async function frame" {
357358 };
358359 try S.doTheTest();
359360}
361
362test "async function call return value" {
363 const S = struct {
364 var frame: anyframe = undefined;
365 var pt = Point{.x = 10, .y = 11 };
366
367 fn doTheTest() void {
368 expectEqual(pt.x, 10);
369 expectEqual(pt.y, 11);
370 _ = async first();
371 expectEqual(pt.x, 10);
372 expectEqual(pt.y, 11);
373 resume frame;
374 expectEqual(pt.x, 1);
375 expectEqual(pt.y, 2);
376 }
377
378 fn first() void {
379 pt = second(1, 2);
380 }
381
382 fn second(x: i32, y: i32) Point {
383 return other(x, y);
384 }
385
386 fn other(x: i32, y: i32) Point {
387 frame = @frame();
388 suspend;
389 return Point{
390 .x = x,
391 .y = y,
392 };
393 }
394
395 const Point = struct {
396 x: i32,
397 y: i32,
398 };
399 };
400 S.doTheTest();
401}