Skip to content

Commit

Permalink
fix await on early return when return type is struct
Browse files Browse the repository at this point in the history
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
andrewrk committed Jul 4, 2018

Verified

This commit was signed with the committer’s verified signature.
nwsharp Nathan Sharp
1 parent 1d18688 commit 8c39cdc
Showing 4 changed files with 54 additions and 10 deletions.
12 changes: 4 additions & 8 deletions src/ir.cpp
Original file line number Diff line number Diff line change
@@ -6674,7 +6674,10 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast
}
Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME);
IrInstruction *promise_result_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, result_field_name);
// If the type of the result handle_is_ptr then this does not actually perform a load. But we need it to,
// because we're about to destroy the memory. So we store it into our result variable.
IrInstruction *no_suspend_result = ir_build_load_ptr(irb, parent_scope, node, promise_result_ptr);
ir_build_store_ptr(irb, parent_scope, node, my_result_var_ptr, no_suspend_result);
ir_build_cancel(irb, parent_scope, node, target_inst);
ir_build_br(irb, parent_scope, node, merge_block, const_bool_false);

@@ -6696,17 +6699,10 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast
ir_mark_gen(ir_build_br(irb, parent_scope, node, irb->exec->coro_final_cleanup_block, const_bool_false));

ir_set_cursor_at_end_and_append_block(irb, resume_block);
IrInstruction *yes_suspend_result = ir_build_load_ptr(irb, parent_scope, node, my_result_var_ptr);
ir_build_br(irb, parent_scope, node, merge_block, const_bool_false);

ir_set_cursor_at_end_and_append_block(irb, merge_block);
IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2);
IrInstruction **incoming_values = allocate<IrInstruction *>(2);
incoming_blocks[0] = resume_block;
incoming_values[0] = yes_suspend_result;
incoming_blocks[1] = no_suspend_block;
incoming_values[1] = no_suspend_result;
return ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values);
return ir_build_load_ptr(irb, parent_scope, node, my_result_var_ptr);
}

static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNode *node) {
1 change: 1 addition & 0 deletions test/behavior.zig
Original file line number Diff line number Diff line change
@@ -18,6 +18,7 @@ comptime {
_ = @import("cases/cast.zig");
_ = @import("cases/const_slice_child.zig");
_ = @import("cases/coroutines.zig");
_ = @import("cases/coroutine_await_struct.zig");
_ = @import("cases/defer.zig");
_ = @import("cases/enum.zig");
_ = @import("cases/enum_with_members.zig");
47 changes: 47 additions & 0 deletions test/cases/coroutine_await_struct.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
const std = @import("std");
const builtin = @import("builtin");
const assert = std.debug.assert;

const Foo = struct {
x: i32,
};

var await_a_promise: promise = undefined;
var await_final_result = Foo{ .x = 0 };

test "coroutine await struct" {
var da = std.heap.DirectAllocator.init();
defer da.deinit();

await_seq('a');
const p = async<&da.allocator> await_amain() catch unreachable;
await_seq('f');
resume await_a_promise;
await_seq('i');
assert(await_final_result.x == 1234);
assert(std.mem.eql(u8, await_points, "abcdefghi"));
}
async fn await_amain() void {
await_seq('b');
const p = async await_another() catch unreachable;
await_seq('e');
await_final_result = await p;
await_seq('h');
}
async fn await_another() Foo {
await_seq('c');
suspend |p| {
await_seq('d');
await_a_promise = p;
}
await_seq('g');
return Foo{ .x = 1234 };
}

var await_points = []u8{0} ** "abcdefghi".len;
var await_seq_index: usize = 0;

fn await_seq(c: u8) void {
await_points[await_seq_index] = c;
await_seq_index += 1;
}
4 changes: 2 additions & 2 deletions test/cases/coroutines.zig
Original file line number Diff line number Diff line change
@@ -116,14 +116,14 @@ test "coroutine await early return" {
defer da.deinit();

early_seq('a');
const p = async<&da.allocator> early_amain() catch unreachable;
const p = async<&da.allocator> early_amain() catch @panic("out of memory");
early_seq('f');
assert(early_final_result == 1234);
assert(std.mem.eql(u8, early_points, "abcdef"));
}
async fn early_amain() void {
early_seq('b');
const p = async early_another() catch unreachable;
const p = async early_another() catch @panic("out of memory");
early_seq('d');
early_final_result = await p;
early_seq('e');

0 comments on commit 8c39cdc

Please sign in to comment.