| author | |
| committer | |
| log | 8c39cdc89f2ae7fc25c3856e7c4c6b4662ac8a80 |
| tree | b47fb874d9e0d351ede0e227a77d98a88dc285a7 |
| parent | 1d1868862863e8ce52bd77c100e02ae9e27ec274 |
previously, await on an early return would try to access the
destroyed coroutine frame; now it copies the result into a
temporary variable before destroying the coroutine frame4 files changed, 54 insertions(+), 10 deletions(-)
src/ir.cpp+4-8| ... | ... | @@ -6674,7 +6674,10 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast |
| 6674 | 6674 | } |
| 6675 | 6675 | Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME); |
| 6676 | 6676 | IrInstruction *promise_result_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, result_field_name); |
| 6677 | // If the type of the result handle_is_ptr then this does not actually perform a load. But we need it to, | |
| 6678 | // because we're about to destroy the memory. So we store it into our result variable. | |
| 6677 | 6679 | IrInstruction *no_suspend_result = ir_build_load_ptr(irb, parent_scope, node, promise_result_ptr); |
| 6680 | ir_build_store_ptr(irb, parent_scope, node, my_result_var_ptr, no_suspend_result); | |
| 6678 | 6681 | ir_build_cancel(irb, parent_scope, node, target_inst); |
| 6679 | 6682 | ir_build_br(irb, parent_scope, node, merge_block, const_bool_false); |
| 6680 | 6683 | |
| ... | ... | @@ -6696,17 +6699,10 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast |
| 6696 | 6699 | ir_mark_gen(ir_build_br(irb, parent_scope, node, irb->exec->coro_final_cleanup_block, const_bool_false)); |
| 6697 | 6700 | |
| 6698 | 6701 | ir_set_cursor_at_end_and_append_block(irb, resume_block); |
| 6699 | IrInstruction *yes_suspend_result = ir_build_load_ptr(irb, parent_scope, node, my_result_var_ptr); | |
| 6700 | 6702 | ir_build_br(irb, parent_scope, node, merge_block, const_bool_false); |
| 6701 | 6703 | |
| 6702 | 6704 | ir_set_cursor_at_end_and_append_block(irb, merge_block); |
| 6703 | IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2); | |
| 6704 | IrInstruction **incoming_values = allocate<IrInstruction *>(2); | |
| 6705 | incoming_blocks[0] = resume_block; | |
| 6706 | incoming_values[0] = yes_suspend_result; | |
| 6707 | incoming_blocks[1] = no_suspend_block; | |
| 6708 | incoming_values[1] = no_suspend_result; | |
| 6709 | return ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values); | |
| 6705 | return ir_build_load_ptr(irb, parent_scope, node, my_result_var_ptr); | |
| 6710 | 6706 | } |
| 6711 | 6707 | |
| 6712 | 6708 | static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNode *node) { |
test/behavior.zig+1| ... | ... | @@ -18,6 +18,7 @@ comptime { |
| 18 | 18 | _ = @import("cases/cast.zig"); |
| 19 | 19 | _ = @import("cases/const_slice_child.zig"); |
| 20 | 20 | _ = @import("cases/coroutines.zig"); |
| 21 | _ = @import("cases/coroutine_await_struct.zig"); | |
| 21 | 22 | _ = @import("cases/defer.zig"); |
| 22 | 23 | _ = @import("cases/enum.zig"); |
| 23 | 24 | _ = @import("cases/enum_with_members.zig"); |
test/cases/coroutine_await_struct.zig created+47| ... | ... | @@ -0,0 +1,47 @@ |
| 1 | const std = @import("std"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | const assert = std.debug.assert; | |
| 4 | ||
| 5 | const Foo = struct { | |
| 6 | x: i32, | |
| 7 | }; | |
| 8 | ||
| 9 | var await_a_promise: promise = undefined; | |
| 10 | var await_final_result = Foo{ .x = 0 }; | |
| 11 | ||
| 12 | test "coroutine await struct" { | |
| 13 | var da = std.heap.DirectAllocator.init(); | |
| 14 | defer da.deinit(); | |
| 15 | ||
| 16 | await_seq('a'); | |
| 17 | const p = async<&da.allocator> await_amain() catch unreachable; | |
| 18 | await_seq('f'); | |
| 19 | resume await_a_promise; | |
| 20 | await_seq('i'); | |
| 21 | assert(await_final_result.x == 1234); | |
| 22 | assert(std.mem.eql(u8, await_points, "abcdefghi")); | |
| 23 | } | |
| 24 | async fn await_amain() void { | |
| 25 | await_seq('b'); | |
| 26 | const p = async await_another() catch unreachable; | |
| 27 | await_seq('e'); | |
| 28 | await_final_result = await p; | |
| 29 | await_seq('h'); | |
| 30 | } | |
| 31 | async fn await_another() Foo { | |
| 32 | await_seq('c'); | |
| 33 | suspend |p| { | |
| 34 | await_seq('d'); | |
| 35 | await_a_promise = p; | |
| 36 | } | |
| 37 | await_seq('g'); | |
| 38 | return Foo{ .x = 1234 }; | |
| 39 | } | |
| 40 | ||
| 41 | var await_points = []u8{0} ** "abcdefghi".len; | |
| 42 | var await_seq_index: usize = 0; | |
| 43 | ||
| 44 | fn await_seq(c: u8) void { | |
| 45 | await_points[await_seq_index] = c; | |
| 46 | await_seq_index += 1; | |
| 47 | } |
test/cases/coroutines.zig+2-2| ... | ... | @@ -116,14 +116,14 @@ test "coroutine await early return" { |
| 116 | 116 | defer da.deinit(); |
| 117 | 117 | |
| 118 | 118 | early_seq('a'); |
| 119 | const p = async<&da.allocator> early_amain() catch unreachable; | |
| 119 | const p = async<&da.allocator> early_amain() catch @panic("out of memory"); | |
| 120 | 120 | early_seq('f'); |
| 121 | 121 | assert(early_final_result == 1234); |
| 122 | 122 | assert(std.mem.eql(u8, early_points, "abcdef")); |
| 123 | 123 | } |
| 124 | 124 | async fn early_amain() void { |
| 125 | 125 | early_seq('b'); |
| 126 | const p = async early_another() catch unreachable; | |
| 126 | const p = async early_another() catch @panic("out of memory"); | |
| 127 | 127 | early_seq('d'); |
| 128 | 128 | early_final_result = await p; |
| 129 | 129 | early_seq('e'); |