authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-08-08 11:37:49-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-08-08 11:37:49-04:00
log34bfdf193aee4cb4fc931c6cc4ee82ef0a3a506f
tree37d3c11a0b2cc5a2520428c912e53c2b868ffe85
parente11cafbd4f11fa5eae0cbdf03854291834b4cd77
signaturelock-open Commit is signed but in an unrecognized format.

cancel, defer, errdefer all working as intended now


7 files changed, 243 insertions(+), 62 deletions(-)

BRANCH_TODO+1-2
......@@ -2,8 +2,7 @@
22 * compile error for error: expected anyframe->T, found 'i32'
33 * await of a non async function
44 * async call on a non async function
5 * cancel
6 * defer and errdefer
5 * a test where an async function destroys its own frame in a defer
76 * implicit cast of normal function to async function should be allowed when it is inferred to be async
87 * revive std.event.Loop
98 * @typeInfo for @Frame(func)
src/all_types.hpp+7
......@@ -2363,6 +2363,7 @@ enum IrInstructionId {
23632363 IrInstructionIdAwaitSrc,
23642364 IrInstructionIdAwaitGen,
23652365 IrInstructionIdCoroResume,
2366 IrInstructionIdTestCancelRequested,
23662367};
23672368
23682369struct IrInstruction {
......@@ -3636,6 +3637,12 @@ struct IrInstructionCoroResume {
36363637 IrInstruction *frame;
36373638};
36383639
3640struct IrInstructionTestCancelRequested {
3641 IrInstruction base;
3642
3643 bool use_return_begin_prev_value;
3644};
3645
36393646enum ResultLocId {
36403647 ResultLocIdInvalid,
36413648 ResultLocIdNone,
src/codegen.cpp+14
......@@ -5557,6 +5557,18 @@ static LLVMValueRef ir_render_frame_size(CodeGen *g, IrExecutable *executable,
55575557 return gen_frame_size(g, fn_val);
55585558}
55595559
5560static LLVMValueRef ir_render_test_cancel_requested(CodeGen *g, IrExecutable *executable,
5561 IrInstructionTestCancelRequested *instruction)
5562{
5563 if (!fn_is_async(g->cur_fn))
5564 return LLVMConstInt(LLVMInt1Type(), 0, false);
5565 if (instruction->use_return_begin_prev_value) {
5566 return LLVMBuildTrunc(g->builder, g->cur_async_prev_val, LLVMInt1Type(), "");
5567 } else {
5568 zig_panic("TODO");
5569 }
5570}
5571
55605572static void set_debug_location(CodeGen *g, IrInstruction *instruction) {
55615573 AstNode *source_node = instruction->source_node;
55625574 Scope *scope = instruction->scope;
......@@ -5810,6 +5822,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
58105822 return ir_render_frame_size(g, executable, (IrInstructionFrameSizeGen *)instruction);
58115823 case IrInstructionIdAwaitGen:
58125824 return ir_render_await(g, executable, (IrInstructionAwaitGen *)instruction);
5825 case IrInstructionIdTestCancelRequested:
5826 return ir_render_test_cancel_requested(g, executable, (IrInstructionTestCancelRequested *)instruction);
58135827 }
58145828 zig_unreachable();
58155829}
src/ir.cpp+122-35
......@@ -26,6 +26,7 @@ struct IrBuilder {
2626 CodeGen *codegen;
2727 IrExecutable *exec;
2828 IrBasicBlock *current_basic_block;
29 AstNode *main_block_node;
2930};
3031
3132struct IrAnalyze {
......@@ -1061,6 +1062,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroResume *) {
10611062 return IrInstructionIdCoroResume;
10621063}
10631064
1065static constexpr IrInstructionId ir_instruction_id(IrInstructionTestCancelRequested *) {
1066 return IrInstructionIdTestCancelRequested;
1067}
1068
10641069template<typename T>
10651070static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
10661071 T *special_instruction = allocate<T>(1);
......@@ -3320,6 +3325,16 @@ static IrInstruction *ir_build_coro_resume(IrBuilder *irb, Scope *scope, AstNode
33203325 return &instruction->base;
33213326}
33223327
3328static IrInstruction *ir_build_test_cancel_requested(IrBuilder *irb, Scope *scope, AstNode *source_node,
3329 bool use_return_begin_prev_value)
3330{
3331 IrInstructionTestCancelRequested *instruction = ir_build_instruction<IrInstructionTestCancelRequested>(irb, scope, source_node);
3332 instruction->base.value.type = irb->codegen->builtin_types.entry_bool;
3333 instruction->use_return_begin_prev_value = use_return_begin_prev_value;
3334
3335 return &instruction->base;
3336}
3337
33233338static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {
33243339 results[ReturnKindUnconditional] = 0;
33253340 results[ReturnKindError] = 0;
......@@ -3494,45 +3509,62 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,
34943509 size_t defer_counts[2];
34953510 ir_count_defers(irb, scope, outer_scope, defer_counts);
34963511 bool have_err_defers = defer_counts[ReturnKindError] > 0;
3497 if (have_err_defers || irb->codegen->have_err_ret_tracing) {
3498 IrBasicBlock *err_block = ir_create_basic_block(irb, scope, "ErrRetErr");
3499 IrBasicBlock *ok_block = ir_create_basic_block(irb, scope, "ErrRetOk");
3512 if (!have_err_defers && !irb->codegen->have_err_ret_tracing) {
3513 // only generate unconditional defers
3514 ir_gen_defers_for_block(irb, scope, outer_scope, false);
3515 IrInstruction *result = ir_build_return(irb, scope, node, return_value);
3516 result_loc_ret->base.source_instruction = result;
3517 return result;
3518 }
3519 bool should_inline = ir_should_inline(irb->exec, scope);
3520 bool need_test_cancel = !should_inline && have_err_defers;
35003521
3501 IrInstruction *is_err = ir_build_test_err_src(irb, scope, node, return_value, false, true);
3522 IrBasicBlock *err_block = ir_create_basic_block(irb, scope, "ErrRetErr");
3523 IrBasicBlock *normal_defers_block = ir_create_basic_block(irb, scope, "Defers");
3524 IrBasicBlock *ok_block = need_test_cancel ?
3525 ir_create_basic_block(irb, scope, "ErrRetOk") : normal_defers_block;
3526 IrBasicBlock *all_defers_block = have_err_defers ? ir_create_basic_block(irb, scope, "ErrDefers") : normal_defers_block;
35023527
3503 bool should_inline = ir_should_inline(irb->exec, scope);
3504 IrInstruction *is_comptime;
3505 if (should_inline) {
3506 is_comptime = ir_build_const_bool(irb, scope, node, true);
3507 } else {
3508 is_comptime = ir_build_test_comptime(irb, scope, node, is_err);
3509 }
3528 IrInstruction *is_err = ir_build_test_err_src(irb, scope, node, return_value, false, true);
35103529
3511 ir_mark_gen(ir_build_cond_br(irb, scope, node, is_err, err_block, ok_block, is_comptime));
3512 IrBasicBlock *ret_stmt_block = ir_create_basic_block(irb, scope, "RetStmt");
3530 IrInstruction *force_comptime = ir_build_const_bool(irb, scope, node, should_inline);
3531 IrInstruction *err_is_comptime;
3532 if (should_inline) {
3533 err_is_comptime = force_comptime;
3534 } else {
3535 err_is_comptime = ir_build_test_comptime(irb, scope, node, is_err);
3536 }
35133537
3514 ir_set_cursor_at_end_and_append_block(irb, err_block);
3515 if (irb->codegen->have_err_ret_tracing && !should_inline) {
3516 ir_build_save_err_ret_addr(irb, scope, node);
3517 }
3518 ir_gen_defers_for_block(irb, scope, outer_scope, true);
3519 ir_build_br(irb, scope, node, ret_stmt_block, is_comptime);
3538 ir_mark_gen(ir_build_cond_br(irb, scope, node, is_err, err_block, ok_block, err_is_comptime));
3539 IrBasicBlock *ret_stmt_block = ir_create_basic_block(irb, scope, "RetStmt");
35203540
3541 ir_set_cursor_at_end_and_append_block(irb, err_block);
3542 if (irb->codegen->have_err_ret_tracing && !should_inline) {
3543 ir_build_save_err_ret_addr(irb, scope, node);
3544 }
3545 ir_build_br(irb, scope, node, all_defers_block, err_is_comptime);
3546
3547 if (need_test_cancel) {
35213548 ir_set_cursor_at_end_and_append_block(irb, ok_block);
3522 ir_gen_defers_for_block(irb, scope, outer_scope, false);
3523 ir_build_br(irb, scope, node, ret_stmt_block, is_comptime);
3549 IrInstruction *is_canceled = ir_build_test_cancel_requested(irb, scope, node, true);
3550 ir_mark_gen(ir_build_cond_br(irb, scope, node, is_canceled,
3551 all_defers_block, normal_defers_block, force_comptime));
3552 }
35243553
3525 ir_set_cursor_at_end_and_append_block(irb, ret_stmt_block);
3526 IrInstruction *result = ir_build_return(irb, scope, node, return_value);
3527 result_loc_ret->base.source_instruction = result;
3528 return result;
3529 } else {
3530 // generate unconditional defers
3531 ir_gen_defers_for_block(irb, scope, outer_scope, false);
3532 IrInstruction *result = ir_build_return(irb, scope, node, return_value);
3533 result_loc_ret->base.source_instruction = result;
3534 return result;
3554 if (all_defers_block != normal_defers_block) {
3555 ir_set_cursor_at_end_and_append_block(irb, all_defers_block);
3556 ir_gen_defers_for_block(irb, scope, outer_scope, true);
3557 ir_build_br(irb, scope, node, ret_stmt_block, force_comptime);
35353558 }
3559
3560 ir_set_cursor_at_end_and_append_block(irb, normal_defers_block);
3561 ir_gen_defers_for_block(irb, scope, outer_scope, false);
3562 ir_build_br(irb, scope, node, ret_stmt_block, force_comptime);
3563
3564 ir_set_cursor_at_end_and_append_block(irb, ret_stmt_block);
3565 IrInstruction *result = ir_build_return(irb, scope, node, return_value);
3566 result_loc_ret->base.source_instruction = result;
3567 return result;
35363568 }
35373569 case ReturnKindError:
35383570 {
......@@ -3765,18 +3797,59 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode
37653797 incoming_values.append(else_expr_result);
37663798 }
37673799
3768 if (block_node->data.block.name != nullptr) {
3800 bool is_return_from_fn = block_node == irb->main_block_node;
3801 if (!is_return_from_fn) {
37693802 ir_gen_defers_for_block(irb, child_scope, outer_block_scope, false);
3803 }
3804
3805 IrInstruction *result;
3806 if (block_node->data.block.name != nullptr) {
37703807 ir_mark_gen(ir_build_br(irb, parent_scope, block_node, scope_block->end_block, scope_block->is_comptime));
37713808 ir_set_cursor_at_end_and_append_block(irb, scope_block->end_block);
37723809 IrInstruction *phi = ir_build_phi(irb, parent_scope, block_node, incoming_blocks.length,
37733810 incoming_blocks.items, incoming_values.items, scope_block->peer_parent);
3774 return ir_expr_wrap(irb, parent_scope, phi, result_loc);
3811 result = ir_expr_wrap(irb, parent_scope, phi, result_loc);
37753812 } else {
3776 ir_gen_defers_for_block(irb, child_scope, outer_block_scope, false);
37773813 IrInstruction *void_inst = ir_mark_gen(ir_build_const_void(irb, child_scope, block_node));
3778 return ir_lval_wrap(irb, parent_scope, void_inst, lval, result_loc);
3814 result = ir_lval_wrap(irb, parent_scope, void_inst, lval, result_loc);
37793815 }
3816 if (!is_return_from_fn)
3817 return result;
3818
3819 // no need for save_err_ret_addr because this cannot return error
3820 // but if it is a canceled async function we do need to run the errdefers
3821
3822 ir_mark_gen(ir_build_add_implicit_return_type(irb, child_scope, block_node, result));
3823 result = ir_mark_gen(ir_build_return_begin(irb, child_scope, block_node, result));
3824
3825 size_t defer_counts[2];
3826 ir_count_defers(irb, child_scope, outer_block_scope, defer_counts);
3827 bool have_err_defers = defer_counts[ReturnKindError] > 0;
3828 if (!have_err_defers) {
3829 // only generate unconditional defers
3830 ir_gen_defers_for_block(irb, child_scope, outer_block_scope, false);
3831 return ir_mark_gen(ir_build_return(irb, child_scope, result->source_node, result));
3832 }
3833 IrInstruction *is_canceled = ir_build_test_cancel_requested(irb, child_scope, block_node, true);
3834 IrBasicBlock *all_defers_block = ir_create_basic_block(irb, child_scope, "ErrDefers");
3835 IrBasicBlock *normal_defers_block = ir_create_basic_block(irb, child_scope, "Defers");
3836 IrBasicBlock *ret_stmt_block = ir_create_basic_block(irb, child_scope, "RetStmt");
3837 bool should_inline = ir_should_inline(irb->exec, child_scope);
3838 IrInstruction *errdefers_is_comptime = ir_build_const_bool(irb, child_scope, block_node,
3839 should_inline || !have_err_defers);
3840 ir_mark_gen(ir_build_cond_br(irb, child_scope, block_node, is_canceled,
3841 all_defers_block, normal_defers_block, errdefers_is_comptime));
3842
3843 ir_set_cursor_at_end_and_append_block(irb, all_defers_block);
3844 ir_gen_defers_for_block(irb, child_scope, outer_block_scope, true);
3845 ir_build_br(irb, child_scope, block_node, ret_stmt_block, errdefers_is_comptime);
3846
3847 ir_set_cursor_at_end_and_append_block(irb, normal_defers_block);
3848 ir_gen_defers_for_block(irb, child_scope, outer_block_scope, false);
3849 ir_build_br(irb, child_scope, block_node, ret_stmt_block, errdefers_is_comptime);
3850
3851 ir_set_cursor_at_end_and_append_block(irb, ret_stmt_block);
3852 return ir_mark_gen(ir_build_return(irb, child_scope, result->source_node, result));
37803853}
37813854
37823855static IrInstruction *ir_gen_bin_op_id(IrBuilder *irb, Scope *scope, AstNode *node, IrBinOp op_id) {
......@@ -8111,6 +8184,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
81118184
81128185 irb->codegen = codegen;
81138186 irb->exec = ir_executable;
8187 irb->main_block_node = node;
81148188
81158189 IrBasicBlock *entry_block = ir_create_basic_block(irb, scope, "Entry");
81168190 ir_set_cursor_at_end_and_append_block(irb, entry_block);
......@@ -24603,6 +24677,16 @@ static IrInstruction *ir_analyze_instruction_coro_resume(IrAnalyze *ira, IrInstr
2460324677 return ir_build_coro_resume(&ira->new_irb, instruction->base.scope, instruction->base.source_node, casted_frame);
2460424678}
2460524679
24680static IrInstruction *ir_analyze_instruction_test_cancel_requested(IrAnalyze *ira,
24681 IrInstructionTestCancelRequested *instruction)
24682{
24683 if (ir_should_inline(ira->new_irb.exec, instruction->base.scope)) {
24684 return ir_const_bool(ira, &instruction->base, false);
24685 }
24686 return ir_build_test_cancel_requested(&ira->new_irb, instruction->base.scope, instruction->base.source_node,
24687 instruction->use_return_begin_prev_value);
24688}
24689
2460624690static IrInstruction *ir_analyze_instruction_base(IrAnalyze *ira, IrInstruction *instruction) {
2460724691 switch (instruction->id) {
2460824692 case IrInstructionIdInvalid:
......@@ -24900,6 +24984,8 @@ static IrInstruction *ir_analyze_instruction_base(IrAnalyze *ira, IrInstruction
2490024984 return ir_analyze_instruction_coro_resume(ira, (IrInstructionCoroResume *)instruction);
2490124985 case IrInstructionIdAwaitSrc:
2490224986 return ir_analyze_instruction_await(ira, (IrInstructionAwaitSrc *)instruction);
24987 case IrInstructionIdTestCancelRequested:
24988 return ir_analyze_instruction_test_cancel_requested(ira, (IrInstructionTestCancelRequested *)instruction);
2490324989 }
2490424990 zig_unreachable();
2490524991}
......@@ -25134,6 +25220,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
2513425220 case IrInstructionIdHasDecl:
2513525221 case IrInstructionIdAllocaSrc:
2513625222 case IrInstructionIdAllocaGen:
25223 case IrInstructionIdTestCancelRequested:
2513725224 return false;
2513825225
2513925226 case IrInstructionIdAsm:
src/ir_print.cpp+8
......@@ -1550,6 +1550,11 @@ static void ir_print_await_gen(IrPrint *irp, IrInstructionAwaitGen *instruction)
15501550 fprintf(irp->f, ")");
15511551}
15521552
1553static void ir_print_test_cancel_requested(IrPrint *irp, IrInstructionTestCancelRequested *instruction) {
1554 const char *arg = instruction->use_return_begin_prev_value ? "UseReturnBeginPrevValue" : "AdditionalCheck";
1555 fprintf(irp->f, "@testCancelRequested(%s)", arg);
1556}
1557
15531558static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
15541559 ir_print_prefix(irp, instruction);
15551560 switch (instruction->id) {
......@@ -2032,6 +2037,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
20322037 case IrInstructionIdAwaitGen:
20332038 ir_print_await_gen(irp, (IrInstructionAwaitGen *)instruction);
20342039 break;
2040 case IrInstructionIdTestCancelRequested:
2041 ir_print_test_cancel_requested(irp, (IrInstructionTestCancelRequested *)instruction);
2042 break;
20352043 }
20362044 fprintf(irp->f, "\n");
20372045}
test/stage1/behavior/cancel.zig+7-2
......@@ -48,8 +48,9 @@ var defer_b3: bool = false;
4848var defer_b4: bool = false;
4949
5050test "cancel backwards" {
51 _ = async b1();
51 var b1_frame = async b1();
5252 resume b4_handle;
53 _ = async awaitAFrame(&b1_frame);
5354 expect(defer_b1);
5455 expect(defer_b2);
5556 expect(defer_b3);
......@@ -63,7 +64,7 @@ async fn b1() void {
6364 b2();
6465}
6566
66var b4_handle: anyframe = undefined;
67var b4_handle: anyframe->void = undefined;
6768
6869async fn b2() void {
6970 const b3_handle = async b3();
......@@ -93,6 +94,10 @@ async fn b4() void {
9394 suspend;
9495}
9596
97fn awaitAFrame(f: anyframe->void) void {
98 await f;
99}
100
96101test "cancel on a non-pointer" {
97102 const S = struct {
98103 fn doTheTest() void {
test/stage1/behavior/coroutines.zig+84-23
......@@ -134,29 +134,44 @@ test "@frameSize" {
134134}
135135
136136test "coroutine suspend, resume" {
137 seq('a');
138 const p = async testAsyncSeq();
139 seq('c');
140 resume p;
141 seq('f');
142 // `cancel` is now a suspend point so it cannot be done here
143 seq('g');
137 const S = struct {
138 var frame: anyframe = undefined;
144139
145 expect(std.mem.eql(u8, points, "abcdefg"));
146}
147async fn testAsyncSeq() void {
148 defer seq('e');
140 fn doTheTest() void {
141 _ = async amain();
142 seq('d');
143 resume frame;
144 seq('h');
149145
150 seq('b');
151 suspend;
152 seq('d');
153}
154var points = [_]u8{0} ** "abcdefg".len;
155var index: usize = 0;
146 expect(std.mem.eql(u8, points, "abcdefgh"));
147 }
148
149 fn amain() void {
150 seq('a');
151 var f = async testAsyncSeq();
152 seq('c');
153 cancel f;
154 seq('g');
155 }
156
157 fn testAsyncSeq() void {
158 defer seq('f');
156159
157fn seq(c: u8) void {
158 points[index] = c;
159 index += 1;
160 seq('b');
161 suspend {
162 frame = @frame();
163 }
164 seq('e');
165 }
166 var points = [_]u8{'x'} ** "abcdefgh".len;
167 var index: usize = 0;
168
169 fn seq(c: u8) void {
170 points[index] = c;
171 index += 1;
172 }
173 };
174 S.doTheTest();
160175}
161176
162177test "coroutine suspend with block" {
......@@ -267,12 +282,19 @@ test "async fn pointer in a struct field" {
267282 };
268283 var foo = Foo{ .bar = simpleAsyncFn2 };
269284 var bytes: [64]u8 = undefined;
270 const p = @asyncCall(&bytes, {}, foo.bar, &data);
271 comptime expect(@typeOf(p) == anyframe->void);
285 const f = @asyncCall(&bytes, {}, foo.bar, &data);
286 comptime expect(@typeOf(f) == anyframe->void);
272287 expect(data == 2);
273 resume p;
288 resume f;
289 expect(data == 2);
290 _ = async doTheAwait(f);
274291 expect(data == 4);
275292}
293
294fn doTheAwait(f: anyframe->void) void {
295 await f;
296}
297
276298async fn simpleAsyncFn2(y: *i32) void {
277299 defer y.* += 2;
278300 y.* += 1;
......@@ -507,3 +529,42 @@ test "call async function which has struct return type" {
507529 };
508530 S.doTheTest();
509531}
532
533test "errdefers in scope get run when canceling async fn call" {
534 const S = struct {
535 var frame: anyframe = undefined;
536 var x: u32 = 0;
537
538 fn doTheTest() void {
539 x = 9;
540 _ = async cancelIt();
541 resume frame;
542 expect(x == 6);
543
544 x = 9;
545 _ = async awaitIt();
546 resume frame;
547 expect(x == 11);
548 }
549
550 fn cancelIt() void {
551 var f = async func();
552 cancel f;
553 }
554
555 fn awaitIt() void {
556 var f = async func();
557 await f;
558 }
559
560 fn func() void {
561 defer x += 1;
562 errdefer x /= 2;
563 defer x += 1;
564 suspend {
565 frame = @frame();
566 }
567 }
568 };
569 S.doTheTest();
570}