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,3 +1,4 @@
1 * struct types as the return type of an async function. make sure it works with return result locations.
1 * compile error for error: expected anyframe->T, found 'anyframe'2 * compile error for error: expected anyframe->T, found 'anyframe'
2 * compile error for error: expected anyframe->T, found 'i32'3 * compile error for error: expected anyframe->T, found 'i32'
3 * await of a non async function4 * await of a non async function
...@@ -15,5 +16,6 @@...@@ -15,5 +16,6 @@
15 * peer type resolution of *@Frame(func) and anyframe16 * peer type resolution of *@Frame(func) and anyframe
16 * peer type resolution of *@Frame(func) and anyframe->T when the return type matches17 * peer type resolution of *@Frame(func) and anyframe->T when the return type matches
17 * returning a value from within a suspend block18 * 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.
19 * make resuming inside a suspend block, with nothing after it, a must-tail call.19 * 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) {...@@ -503,7 +503,7 @@ static LLVMValueRef make_fn_llvm_value(CodeGen *g, ZigFn *fn) {
503 // nothing to do503 // nothing to do
504 } else if (type_is_nonnull_ptr(return_type)) {504 } else if (type_is_nonnull_ptr(return_type)) {
505 addLLVMAttr(llvm_fn, 0, "nonnull");505 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)) {
507 // Sret pointers must not be address 0507 // Sret pointers must not be address 0
508 addLLVMArgAttr(llvm_fn, 0, "nonnull");508 addLLVMArgAttr(llvm_fn, 0, "nonnull");
509 addLLVMArgAttr(llvm_fn, 0, "sret");509 addLLVMArgAttr(llvm_fn, 0, "sret");
...@@ -3241,8 +3241,13 @@ static LLVMValueRef ir_render_var_ptr(CodeGen *g, IrExecutable *executable, IrIn...@@ -3241,8 +3241,13 @@ static LLVMValueRef ir_render_var_ptr(CodeGen *g, IrExecutable *executable, IrIn
3241static LLVMValueRef ir_render_return_ptr(CodeGen *g, IrExecutable *executable,3241static LLVMValueRef ir_render_return_ptr(CodeGen *g, IrExecutable *executable,
3242 IrInstructionReturnPtr *instruction)3242 IrInstructionReturnPtr *instruction)
3243{3243{
3244 src_assert(g->cur_ret_ptr != nullptr || !type_has_bits(instruction->base.value.type),3244 if (!type_has_bits(instruction->base.value.type))
3245 instruction->base.source_node);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 }
3246 return g->cur_ret_ptr;3251 return g->cur_ret_ptr;
3247}3252}
32483253
...@@ -3506,6 +3511,12 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr...@@ -3506,6 +3511,12 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
3506 if (ret_has_bits) {3511 if (ret_has_bits) {
3507 ret_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, coro_arg_start + 1, "");3512 ret_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, coro_arg_start + 1, "");
3508 }3513 }
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 }
3509 } else if (callee_is_async) {3520 } else if (callee_is_async) {
3510 frame_result_loc = ir_llvm_value(g, instruction->frame_result_loc);3521 frame_result_loc = ir_llvm_value(g, instruction->frame_result_loc);
3511 awaiter_init_val = LLVMBuildPtrToInt(g->builder, g->cur_ret_ptr,3522 awaiter_init_val = LLVMBuildPtrToInt(g->builder, g->cur_ret_ptr,
...@@ -3513,6 +3524,12 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr...@@ -3513,6 +3524,12 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
3513 if (ret_has_bits) {3524 if (ret_has_bits) {
3514 ret_ptr = result_loc;3525 ret_ptr = result_loc;
3515 }3526 }
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 }
3516 }3533 }
3517 if (instruction->is_async || callee_is_async) {3534 if (instruction->is_async || callee_is_async) {
3518 assert(frame_result_loc != nullptr);3535 assert(frame_result_loc != nullptr);
...@@ -3525,10 +3542,6 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr...@@ -3525,10 +3542,6 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
3525 LLVMValueRef awaiter_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, coro_awaiter_index, "");3542 LLVMValueRef awaiter_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, coro_awaiter_index, "");
3526 LLVMBuildStore(g->builder, awaiter_init_val, awaiter_ptr);3543 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 }
3532 }3545 }
3533 if (!instruction->is_async && !callee_is_async) {3546 if (!instruction->is_async && !callee_is_async) {
3534 if (first_arg_ret) {3547 if (first_arg_ret) {
test/stage1/behavior/coroutines.zig+42
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const expect = std.testing.expect;3const expect = std.testing.expect;
4const expectEqual = std.testing.expectEqual;
45
5var global_x: i32 = 1;6var global_x: i32 = 1;
67
...@@ -357,3 +358,44 @@ test "heap allocated async function frame" {...@@ -357,3 +358,44 @@ test "heap allocated async function frame" {
357 };358 };
358 try S.doTheTest();359 try S.doTheTest();
359}360}
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}