| 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,7 +6674,10 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast |
| 6674 | } | 6674 | } |
| 6675 | Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME); | 6675 | Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME); |
| 6676 | IrInstruction *promise_result_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, result_field_name); | 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 | IrInstruction *no_suspend_result = ir_build_load_ptr(irb, parent_scope, node, promise_result_ptr); | 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 | ir_build_cancel(irb, parent_scope, node, target_inst); | 6681 | ir_build_cancel(irb, parent_scope, node, target_inst); |
| 6679 | ir_build_br(irb, parent_scope, node, merge_block, const_bool_false); | 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,17 +6699,10 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast |
| 6696 | ir_mark_gen(ir_build_br(irb, parent_scope, node, irb->exec->coro_final_cleanup_block, const_bool_false)); | 6699 | ir_mark_gen(ir_build_br(irb, parent_scope, node, irb->exec->coro_final_cleanup_block, const_bool_false)); |
| 6697 | 6700 | ||
| 6698 | ir_set_cursor_at_end_and_append_block(irb, resume_block); | 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 | ir_build_br(irb, parent_scope, node, merge_block, const_bool_false); | 6702 | ir_build_br(irb, parent_scope, node, merge_block, const_bool_false); |
| 6701 | 6703 | ||
| 6702 | ir_set_cursor_at_end_and_append_block(irb, merge_block); | 6704 | ir_set_cursor_at_end_and_append_block(irb, merge_block); |
| 6703 | IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2); | 6705 | return ir_build_load_ptr(irb, parent_scope, node, my_result_var_ptr); |
| 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); | ||
| 6710 | } | 6706 | } |
| 6711 | 6707 | ||
| 6712 | static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNode *node) { | 6708 | static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNode *node) { |
test/behavior.zig+1| ... | @@ -18,6 +18,7 @@ comptime { | ... | @@ -18,6 +18,7 @@ comptime { |
| 18 | _ = @import("cases/cast.zig"); | 18 | _ = @import("cases/cast.zig"); |
| 19 | _ = @import("cases/const_slice_child.zig"); | 19 | _ = @import("cases/const_slice_child.zig"); |
| 20 | _ = @import("cases/coroutines.zig"); | 20 | _ = @import("cases/coroutines.zig"); |
| 21 | _ = @import("cases/coroutine_await_struct.zig"); | ||
| 21 | _ = @import("cases/defer.zig"); | 22 | _ = @import("cases/defer.zig"); |
| 22 | _ = @import("cases/enum.zig"); | 23 | _ = @import("cases/enum.zig"); |
| 23 | _ = @import("cases/enum_with_members.zig"); | 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,14 +116,14 @@ test "coroutine await early return" { |
| 116 | defer da.deinit(); | 116 | defer da.deinit(); |
| 117 | 117 | ||
| 118 | early_seq('a'); | 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 | early_seq('f'); | 120 | early_seq('f'); |
| 121 | assert(early_final_result == 1234); | 121 | assert(early_final_result == 1234); |
| 122 | assert(std.mem.eql(u8, early_points, "abcdef")); | 122 | assert(std.mem.eql(u8, early_points, "abcdef")); |
| 123 | } | 123 | } |
| 124 | async fn early_amain() void { | 124 | async fn early_amain() void { |
| 125 | early_seq('b'); | 125 | early_seq('b'); |
| 126 | const p = async early_another() catch unreachable; | 126 | const p = async early_another() catch @panic("out of memory"); |
| 127 | early_seq('d'); | 127 | early_seq('d'); |
| 128 | early_final_result = await p; | 128 | early_final_result = await p; |
| 129 | early_seq('e'); | 129 | early_seq('e'); |