authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-07-03 21:36:16-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-07-04 11:51:02-04:00
log8c39cdc89f2ae7fc25c3856e7c4c6b4662ac8a80
treeb47fb874d9e0d351ede0e227a77d98a88dc285a7
parent1d1868862863e8ce52bd77c100e02ae9e27ec274

fix await on early return when return type is struct

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 frame

4 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);
66806683
...@@ -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));
66976700
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);
67016703
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}
67116707
6712static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNode *node) {6708static 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 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const assert = std.debug.assert;
4
5const Foo = struct {
6 x: i32,
7};
8
9var await_a_promise: promise = undefined;
10var await_final_result = Foo{ .x = 0 };
11
12test "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}
24async 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}
31async 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
41var await_points = []u8{0} ** "abcdefghi".len;
42var await_seq_index: usize = 0;
43
44fn 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();
117117
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}
124async fn early_amain() void {124async 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');