authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-07-30 13:42:26-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2018-07-30 13:42:26-04:00
log5d4a02c350a18a70cf1f92f6638b5d26689c16b4
tree30c9da2e8dc9a0aaef2ac59c9694291ec0f5c9ab
parent608ff52dc3ea356b23fb6ae92fbca9fbb18c7892
parentcfe03c764de0d2edfbad74a71d7c18f0fd68b506
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #1307 from ziglang/cancel-semantics

improved coroutine cancel semantics

10 files changed, 486 insertions(+), 142 deletions(-)

doc/langref.html.in+7-7
...@@ -4665,24 +4665,24 @@ async fn testSuspendBlock() void {...@@ -4665,24 +4665,24 @@ async fn testSuspendBlock() void {
4665 block, while the old thread continued executing the suspend block.4665 block, while the old thread continued executing the suspend block.
4666 </p>4666 </p>
4667 <p>4667 <p>
4668 However, if you use labeled <code>break</code> on the suspend block, the coroutine4668 However, the coroutine can be directly resumed from the suspend block, in which case it
4669 never returns to its resumer and continues executing.4669 never returns to its resumer and continues executing.
4670 </p>4670 </p>
4671 {#code_begin|test#}4671 {#code_begin|test#}
4672const std = @import("std");4672const std = @import("std");
4673const assert = std.debug.assert;4673const assert = std.debug.assert;
46744674
4675test "break from suspend" {4675test "resume from suspend" {
4676 var buf: [500]u8 = undefined;4676 var buf: [500]u8 = undefined;
4677 var a = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator;4677 var a = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator;
4678 var my_result: i32 = 1;4678 var my_result: i32 = 1;
4679 const p = try async<a> testBreakFromSuspend(&my_result);4679 const p = try async<a> testResumeFromSuspend(&my_result);
4680 cancel p;4680 cancel p;
4681 std.debug.assert(my_result == 2);4681 std.debug.assert(my_result == 2);
4682}4682}
4683async fn testBreakFromSuspend(my_result: *i32) void {4683async fn testResumeFromSuspend(my_result: *i32) void {
4684 s: suspend |p| {4684 suspend |p| {
4685 break :s;4685 resume p;
4686 }4686 }
4687 my_result.* += 1;4687 my_result.* += 1;
4688 suspend;4688 suspend;
...@@ -7336,7 +7336,7 @@ Defer(body) = ("defer" | "deferror") body...@@ -7336,7 +7336,7 @@ Defer(body) = ("defer" | "deferror") body
73367336
7337IfExpression(body) = "if" "(" Expression ")" body option("else" BlockExpression(body))7337IfExpression(body) = "if" "(" Expression ")" body option("else" BlockExpression(body))
73387338
7339SuspendExpression(body) = option(Symbol ":") "suspend" option(("|" Symbol "|" body))7339SuspendExpression(body) = "suspend" option(("|" Symbol "|" body))
73407340
7341IfErrorExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|") body "else" "|" Symbol "|" BlockExpression(body)7341IfErrorExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|") body "else" "|" Symbol "|" BlockExpression(body)
73427342
src/all_types.hpp+2-4
...@@ -60,7 +60,7 @@ struct IrExecutable {...@@ -60,7 +60,7 @@ struct IrExecutable {
60 ZigList<Tld *> tld_list;60 ZigList<Tld *> tld_list;
6161
62 IrInstruction *coro_handle;62 IrInstruction *coro_handle;
63 IrInstruction *coro_awaiter_field_ptr; // this one is shared and in the promise63 IrInstruction *atomic_state_field_ptr; // this one is shared and in the promise
64 IrInstruction *coro_result_ptr_field_ptr;64 IrInstruction *coro_result_ptr_field_ptr;
65 IrInstruction *coro_result_field_ptr;65 IrInstruction *coro_result_field_ptr;
66 IrInstruction *await_handle_var_ptr; // this one is where we put the one we extracted from the promise66 IrInstruction *await_handle_var_ptr; // this one is where we put the one we extracted from the promise
...@@ -898,7 +898,6 @@ struct AstNodeAwaitExpr {...@@ -898,7 +898,6 @@ struct AstNodeAwaitExpr {
898};898};
899899
900struct AstNodeSuspend {900struct AstNodeSuspend {
901 Buf *name;
902 AstNode *block;901 AstNode *block;
903 AstNode *promise_symbol;902 AstNode *promise_symbol;
904};903};
...@@ -1929,7 +1928,6 @@ struct ScopeLoop {...@@ -1929,7 +1928,6 @@ struct ScopeLoop {
1929struct ScopeSuspend {1928struct ScopeSuspend {
1930 Scope base;1929 Scope base;
19311930
1932 Buf *name;
1933 IrBasicBlock *resume_block;1931 IrBasicBlock *resume_block;
1934 bool reported_err;1932 bool reported_err;
1935};1933};
...@@ -3245,7 +3243,7 @@ static const size_t stack_trace_ptr_count = 30;...@@ -3245,7 +3243,7 @@ static const size_t stack_trace_ptr_count = 30;
3245#define RESULT_FIELD_NAME "result"3243#define RESULT_FIELD_NAME "result"
3246#define ASYNC_ALLOC_FIELD_NAME "allocFn"3244#define ASYNC_ALLOC_FIELD_NAME "allocFn"
3247#define ASYNC_FREE_FIELD_NAME "freeFn"3245#define ASYNC_FREE_FIELD_NAME "freeFn"
3248#define AWAITER_HANDLE_FIELD_NAME "awaiter_handle"3246#define ATOMIC_STATE_FIELD_NAME "atomic_state"
3249// these point to data belonging to the awaiter3247// these point to data belonging to the awaiter
3250#define ERR_RET_TRACE_PTR_FIELD_NAME "err_ret_trace_ptr"3248#define ERR_RET_TRACE_PTR_FIELD_NAME "err_ret_trace_ptr"
3251#define RESULT_PTR_FIELD_NAME "result_ptr"3249#define RESULT_PTR_FIELD_NAME "result_ptr"
src/analyze.cpp+9-5
...@@ -161,7 +161,6 @@ ScopeSuspend *create_suspend_scope(AstNode *node, Scope *parent) {...@@ -161,7 +161,6 @@ ScopeSuspend *create_suspend_scope(AstNode *node, Scope *parent) {
161 assert(node->type == NodeTypeSuspend);161 assert(node->type == NodeTypeSuspend);
162 ScopeSuspend *scope = allocate<ScopeSuspend>(1);162 ScopeSuspend *scope = allocate<ScopeSuspend>(1);
163 init_scope(&scope->base, ScopeIdSuspend, node, parent);163 init_scope(&scope->base, ScopeIdSuspend, node, parent);
164 scope->name = node->data.suspend.name;
165 return scope;164 return scope;
166}165}
167166
...@@ -519,11 +518,11 @@ TypeTableEntry *get_promise_frame_type(CodeGen *g, TypeTableEntry *return_type)...@@ -519,11 +518,11 @@ TypeTableEntry *get_promise_frame_type(CodeGen *g, TypeTableEntry *return_type)
519 return return_type->promise_frame_parent;518 return return_type->promise_frame_parent;
520 }519 }
521520
522 TypeTableEntry *awaiter_handle_type = get_optional_type(g, g->builtin_types.entry_promise);521 TypeTableEntry *atomic_state_type = g->builtin_types.entry_usize;
523 TypeTableEntry *result_ptr_type = get_pointer_to_type(g, return_type, false);522 TypeTableEntry *result_ptr_type = get_pointer_to_type(g, return_type, false);
524523
525 ZigList<const char *> field_names = {};524 ZigList<const char *> field_names = {};
526 field_names.append(AWAITER_HANDLE_FIELD_NAME);525 field_names.append(ATOMIC_STATE_FIELD_NAME);
527 field_names.append(RESULT_FIELD_NAME);526 field_names.append(RESULT_FIELD_NAME);
528 field_names.append(RESULT_PTR_FIELD_NAME);527 field_names.append(RESULT_PTR_FIELD_NAME);
529 if (g->have_err_ret_tracing) {528 if (g->have_err_ret_tracing) {
...@@ -533,7 +532,7 @@ TypeTableEntry *get_promise_frame_type(CodeGen *g, TypeTableEntry *return_type)...@@ -533,7 +532,7 @@ TypeTableEntry *get_promise_frame_type(CodeGen *g, TypeTableEntry *return_type)
533 }532 }
534533
535 ZigList<TypeTableEntry *> field_types = {};534 ZigList<TypeTableEntry *> field_types = {};
536 field_types.append(awaiter_handle_type);535 field_types.append(atomic_state_type);
537 field_types.append(return_type);536 field_types.append(return_type);
538 field_types.append(result_ptr_type);537 field_types.append(result_ptr_type);
539 if (g->have_err_ret_tracing) {538 if (g->have_err_ret_tracing) {
...@@ -6228,7 +6227,12 @@ uint32_t get_abi_alignment(CodeGen *g, TypeTableEntry *type_entry) {...@@ -6228,7 +6227,12 @@ uint32_t get_abi_alignment(CodeGen *g, TypeTableEntry *type_entry) {
6228 } else if (type_entry->id == TypeTableEntryIdOpaque) {6227 } else if (type_entry->id == TypeTableEntryIdOpaque) {
6229 return 1;6228 return 1;
6230 } else {6229 } else {
6231 return LLVMABIAlignmentOfType(g->target_data_ref, type_entry->type_ref);6230 uint32_t llvm_alignment = LLVMABIAlignmentOfType(g->target_data_ref, type_entry->type_ref);
6231 // promises have at least alignment 8 so that we can have 3 extra bits when doing atomicrmw
6232 if (type_entry->id == TypeTableEntryIdPromise && llvm_alignment < 8) {
6233 return 8;
6234 }
6235 return llvm_alignment;
6232 }6236 }
6233}6237}
62346238
src/ir.cpp+357-97
...@@ -3097,20 +3097,47 @@ static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode...@@ -3097,20 +3097,47 @@ static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode
3097 return return_inst;3097 return return_inst;
3098 }3098 }
30993099
3100 ir_build_store_ptr(irb, scope, node, irb->exec->coro_result_field_ptr, return_value);3100 IrBasicBlock *suspended_block = ir_create_basic_block(irb, scope, "Suspended");
3101 IrInstruction *promise_type_val = ir_build_const_type(irb, scope, node,3101 IrBasicBlock *not_suspended_block = ir_create_basic_block(irb, scope, "NotSuspended");
3102 get_optional_type(irb->codegen, irb->codegen->builtin_types.entry_promise));3102 IrBasicBlock *store_awaiter_block = ir_create_basic_block(irb, scope, "StoreAwaiter");
3103 // TODO replace replacement_value with @intToPtr(?promise, 0x1) when it doesn't crash zig3103 IrBasicBlock *check_canceled_block = ir_create_basic_block(irb, scope, "CheckCanceled");
3104 IrInstruction *replacement_value = irb->exec->coro_handle;3104
3105 IrInstruction *maybe_await_handle = ir_build_atomic_rmw(irb, scope, node,3105 IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, scope, node, 0x7); // 0b111
3106 promise_type_val, irb->exec->coro_awaiter_field_ptr, nullptr, replacement_value, nullptr,3106 IrInstruction *ptr_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000
3107 AtomicRmwOp_xchg, AtomicOrderSeqCst);3107 IrInstruction *is_canceled_mask = ir_build_const_usize(irb, scope, node, 0x1); // 0b001
3108 ir_build_store_ptr(irb, scope, node, irb->exec->await_handle_var_ptr, maybe_await_handle);3108 IrInstruction *is_suspended_mask = ir_build_const_usize(irb, scope, node, 0x2); // 0b010
3109 IrInstruction *is_non_null = ir_build_test_nonnull(irb, scope, node, maybe_await_handle);3109 IrInstruction *promise_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_promise);
3110 IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, false);3110 IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, false);
3111 return ir_build_cond_br(irb, scope, node, is_non_null, irb->exec->coro_normal_final, irb->exec->coro_early_final,3111 IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0);
3112 is_comptime);3112
3113 // the above blocks are rendered by ir_gen after the rest of codegen3113 ir_build_store_ptr(irb, scope, node, irb->exec->coro_result_field_ptr, return_value);
3114 IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize);
3115 IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node,
3116 usize_type_val, irb->exec->atomic_state_field_ptr, nullptr, ptr_mask, nullptr,
3117 AtomicRmwOp_or, AtomicOrderSeqCst);
3118
3119 IrInstruction *is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false);
3120 IrInstruction *is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false);
3121 ir_build_cond_br(irb, scope, node, is_suspended_bool, suspended_block, not_suspended_block, is_comptime);
3122
3123 ir_set_cursor_at_end_and_append_block(irb, suspended_block);
3124 ir_build_unreachable(irb, scope, node);
3125
3126 ir_set_cursor_at_end_and_append_block(irb, not_suspended_block);
3127 IrInstruction *await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false);
3128 // if we ever add null checking safety to the ptrtoint instruction, it needs to be disabled here
3129 IrInstruction *have_await_handle = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false);
3130 ir_build_cond_br(irb, scope, node, have_await_handle, store_awaiter_block, check_canceled_block, is_comptime);
3131
3132 ir_set_cursor_at_end_and_append_block(irb, store_awaiter_block);
3133 IrInstruction *await_handle = ir_build_int_to_ptr(irb, scope, node, promise_type_val, await_handle_addr);
3134 ir_build_store_ptr(irb, scope, node, irb->exec->await_handle_var_ptr, await_handle);
3135 ir_build_br(irb, scope, node, irb->exec->coro_normal_final, is_comptime);
3136
3137 ir_set_cursor_at_end_and_append_block(irb, check_canceled_block);
3138 IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false);
3139 IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false);
3140 return ir_build_cond_br(irb, scope, node, is_canceled_bool, irb->exec->coro_final_cleanup_block, irb->exec->coro_early_final, is_comptime);
3114}3141}
31153142
3116static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) {3143static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) {
...@@ -6159,15 +6186,6 @@ static IrInstruction *ir_gen_return_from_block(IrBuilder *irb, Scope *break_scop...@@ -6159,15 +6186,6 @@ static IrInstruction *ir_gen_return_from_block(IrBuilder *irb, Scope *break_scop
6159 return ir_build_br(irb, break_scope, node, dest_block, is_comptime);6186 return ir_build_br(irb, break_scope, node, dest_block, is_comptime);
6160}6187}
61616188
6162static IrInstruction *ir_gen_break_from_suspend(IrBuilder *irb, Scope *break_scope, AstNode *node, ScopeSuspend *suspend_scope) {
6163 IrInstruction *is_comptime = ir_build_const_bool(irb, break_scope, node, false);
6164
6165 IrBasicBlock *dest_block = suspend_scope->resume_block;
6166 ir_gen_defers_for_block(irb, break_scope, dest_block->scope, false);
6167
6168 return ir_build_br(irb, break_scope, node, dest_block, is_comptime);
6169}
6170
6171static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode *node) {6189static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode *node) {
6172 assert(node->type == NodeTypeBreak);6190 assert(node->type == NodeTypeBreak);
61736191
...@@ -6208,12 +6226,8 @@ static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode *...@@ -6208,12 +6226,8 @@ static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode *
6208 return ir_gen_return_from_block(irb, break_scope, node, this_block_scope);6226 return ir_gen_return_from_block(irb, break_scope, node, this_block_scope);
6209 }6227 }
6210 } else if (search_scope->id == ScopeIdSuspend) {6228 } else if (search_scope->id == ScopeIdSuspend) {
6211 ScopeSuspend *this_suspend_scope = (ScopeSuspend *)search_scope;6229 add_node_error(irb->codegen, node, buf_sprintf("cannot break out of suspend block"));
6212 if (node->data.break_expr.name != nullptr &&6230 return irb->codegen->invalid_instruction;
6213 (this_suspend_scope->name != nullptr && buf_eql_buf(node->data.break_expr.name, this_suspend_scope->name)))
6214 {
6215 return ir_gen_break_from_suspend(irb, break_scope, node, this_suspend_scope);
6216 }
6217 }6231 }
6218 search_scope = search_scope->parent;6232 search_scope = search_scope->parent;
6219 }6233 }
...@@ -6649,30 +6663,150 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo...@@ -6649,30 +6663,150 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo
6649 async_allocator_type_value, is_var_args);6663 async_allocator_type_value, is_var_args);
6650}6664}
66516665
6652static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *parent_scope, AstNode *node) {6666static IrInstruction *ir_gen_cancel_target(IrBuilder *irb, Scope *scope, AstNode *node,
6667 IrInstruction *target_inst, bool cancel_non_suspended, bool cancel_awaited)
6668{
6669 IrBasicBlock *done_block = ir_create_basic_block(irb, scope, "CancelDone");
6670 IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, scope, "NotCanceled");
6671 IrBasicBlock *pre_return_block = ir_create_basic_block(irb, scope, "PreReturn");
6672 IrBasicBlock *post_return_block = ir_create_basic_block(irb, scope, "PostReturn");
6673 IrBasicBlock *do_cancel_block = ir_create_basic_block(irb, scope, "DoCancel");
6674
6675 IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0);
6676 IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize);
6677 IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, false);
6678 IrInstruction *is_canceled_mask = ir_build_const_usize(irb, scope, node, 0x1); // 0b001
6679 IrInstruction *promise_T_type_val = ir_build_const_type(irb, scope, node,
6680 get_promise_type(irb->codegen, irb->codegen->builtin_types.entry_void));
6681 IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, scope, node, 0x7); // 0b111
6682 IrInstruction *ptr_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000
6683 IrInstruction *await_mask = ir_build_const_usize(irb, scope, node, 0x4); // 0b100
6684 IrInstruction *is_suspended_mask = ir_build_const_usize(irb, scope, node, 0x2); // 0b010
6685
6686 // TODO relies on Zig not re-ordering fields
6687 IrInstruction *casted_target_inst = ir_build_ptr_cast(irb, scope, node, promise_T_type_val, target_inst);
6688 IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, casted_target_inst);
6689 Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME);
6690 IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr,
6691 atomic_state_field_name);
6692
6693 // set the is_canceled bit
6694 IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node,
6695 usize_type_val, atomic_state_ptr, nullptr, is_canceled_mask, nullptr,
6696 AtomicRmwOp_or, AtomicOrderSeqCst);
6697
6698 IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false);
6699 IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false);
6700 ir_build_cond_br(irb, scope, node, is_canceled_bool, done_block, not_canceled_block, is_comptime);
6701
6702 ir_set_cursor_at_end_and_append_block(irb, not_canceled_block);
6703 IrInstruction *awaiter_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false);
6704 IrInstruction *is_returned_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpEq, awaiter_addr, ptr_mask, false);
6705 ir_build_cond_br(irb, scope, node, is_returned_bool, post_return_block, pre_return_block, is_comptime);
6706
6707 ir_set_cursor_at_end_and_append_block(irb, post_return_block);
6708 if (cancel_awaited) {
6709 ir_build_br(irb, scope, node, do_cancel_block, is_comptime);
6710 } else {
6711 IrInstruction *is_awaited_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, await_mask, false);
6712 IrInstruction *is_awaited_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_awaited_value, zero, false);
6713 ir_build_cond_br(irb, scope, node, is_awaited_bool, done_block, do_cancel_block, is_comptime);
6714 }
6715
6716 ir_set_cursor_at_end_and_append_block(irb, pre_return_block);
6717 if (cancel_awaited) {
6718 if (cancel_non_suspended) {
6719 ir_build_br(irb, scope, node, do_cancel_block, is_comptime);
6720 } else {
6721 IrInstruction *is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false);
6722 IrInstruction *is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false);
6723 ir_build_cond_br(irb, scope, node, is_suspended_bool, do_cancel_block, done_block, is_comptime);
6724 }
6725 } else {
6726 ir_build_br(irb, scope, node, done_block, is_comptime);
6727 }
6728
6729 ir_set_cursor_at_end_and_append_block(irb, do_cancel_block);
6730 ir_build_cancel(irb, scope, node, target_inst);
6731 ir_build_br(irb, scope, node, done_block, is_comptime);
6732
6733 ir_set_cursor_at_end_and_append_block(irb, done_block);
6734 return ir_build_const_void(irb, scope, node);
6735}
6736
6737static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *scope, AstNode *node) {
6653 assert(node->type == NodeTypeCancel);6738 assert(node->type == NodeTypeCancel);
66546739
6655 IrInstruction *target_inst = ir_gen_node(irb, node->data.cancel_expr.expr, parent_scope);6740 IrInstruction *target_inst = ir_gen_node(irb, node->data.cancel_expr.expr, scope);
6656 if (target_inst == irb->codegen->invalid_instruction)6741 if (target_inst == irb->codegen->invalid_instruction)
6657 return irb->codegen->invalid_instruction;6742 return irb->codegen->invalid_instruction;
66586743
6659 return ir_build_cancel(irb, parent_scope, node, target_inst);6744 return ir_gen_cancel_target(irb, scope, node, target_inst, false, true);
6745}
6746
6747static IrInstruction *ir_gen_resume_target(IrBuilder *irb, Scope *scope, AstNode *node,
6748 IrInstruction *target_inst)
6749{
6750 IrBasicBlock *done_block = ir_create_basic_block(irb, scope, "ResumeDone");
6751 IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, scope, "NotCanceled");
6752 IrBasicBlock *suspended_block = ir_create_basic_block(irb, scope, "IsSuspended");
6753 IrBasicBlock *not_suspended_block = ir_create_basic_block(irb, scope, "IsNotSuspended");
6754
6755 IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0);
6756 IrInstruction *is_canceled_mask = ir_build_const_usize(irb, scope, node, 0x1); // 0b001
6757 IrInstruction *is_suspended_mask = ir_build_const_usize(irb, scope, node, 0x2); // 0b010
6758 IrInstruction *and_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, is_suspended_mask);
6759 IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, false);
6760 IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize);
6761 IrInstruction *promise_T_type_val = ir_build_const_type(irb, scope, node,
6762 get_promise_type(irb->codegen, irb->codegen->builtin_types.entry_void));
6763
6764 // TODO relies on Zig not re-ordering fields
6765 IrInstruction *casted_target_inst = ir_build_ptr_cast(irb, scope, node, promise_T_type_val, target_inst);
6766 IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, casted_target_inst);
6767 Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME);
6768 IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr,
6769 atomic_state_field_name);
6770
6771 // clear the is_suspended bit
6772 IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node,
6773 usize_type_val, atomic_state_ptr, nullptr, and_mask, nullptr,
6774 AtomicRmwOp_and, AtomicOrderSeqCst);
6775
6776 IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false);
6777 IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false);
6778 ir_build_cond_br(irb, scope, node, is_canceled_bool, done_block, not_canceled_block, is_comptime);
6779
6780 ir_set_cursor_at_end_and_append_block(irb, not_canceled_block);
6781 IrInstruction *is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false);
6782 IrInstruction *is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false);
6783 ir_build_cond_br(irb, scope, node, is_suspended_bool, suspended_block, not_suspended_block, is_comptime);
6784
6785 ir_set_cursor_at_end_and_append_block(irb, not_suspended_block);
6786 ir_build_unreachable(irb, scope, node);
6787
6788 ir_set_cursor_at_end_and_append_block(irb, suspended_block);
6789 ir_build_coro_resume(irb, scope, node, target_inst);
6790 ir_build_br(irb, scope, node, done_block, is_comptime);
6791
6792 ir_set_cursor_at_end_and_append_block(irb, done_block);
6793 return ir_build_const_void(irb, scope, node);
6660}6794}
66616795
6662static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *parent_scope, AstNode *node) {6796static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *scope, AstNode *node) {
6663 assert(node->type == NodeTypeResume);6797 assert(node->type == NodeTypeResume);
66646798
6665 IrInstruction *target_inst = ir_gen_node(irb, node->data.resume_expr.expr, parent_scope);6799 IrInstruction *target_inst = ir_gen_node(irb, node->data.resume_expr.expr, scope);
6666 if (target_inst == irb->codegen->invalid_instruction)6800 if (target_inst == irb->codegen->invalid_instruction)
6667 return irb->codegen->invalid_instruction;6801 return irb->codegen->invalid_instruction;
66686802
6669 return ir_build_coro_resume(irb, parent_scope, node, target_inst);6803 return ir_gen_resume_target(irb, scope, node, target_inst);
6670}6804}
66716805
6672static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, AstNode *node) {6806static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *node) {
6673 assert(node->type == NodeTypeAwaitExpr);6807 assert(node->type == NodeTypeAwaitExpr);
66746808
6675 IrInstruction *target_inst = ir_gen_node(irb, node->data.await_expr.expr, parent_scope);6809 IrInstruction *target_inst = ir_gen_node(irb, node->data.await_expr.expr, scope);
6676 if (target_inst == irb->codegen->invalid_instruction)6810 if (target_inst == irb->codegen->invalid_instruction)
6677 return irb->codegen->invalid_instruction;6811 return irb->codegen->invalid_instruction;
66786812
...@@ -6686,7 +6820,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast...@@ -6686,7 +6820,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast
6686 return irb->codegen->invalid_instruction;6820 return irb->codegen->invalid_instruction;
6687 }6821 }
66886822
6689 ScopeDeferExpr *scope_defer_expr = get_scope_defer_expr(parent_scope);6823 ScopeDeferExpr *scope_defer_expr = get_scope_defer_expr(scope);
6690 if (scope_defer_expr) {6824 if (scope_defer_expr) {
6691 if (!scope_defer_expr->reported_err) {6825 if (!scope_defer_expr->reported_err) {
6692 add_node_error(irb->codegen, node, buf_sprintf("cannot await inside defer expression"));6826 add_node_error(irb->codegen, node, buf_sprintf("cannot await inside defer expression"));
...@@ -6697,81 +6831,157 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast...@@ -6697,81 +6831,157 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast
66976831
6698 Scope *outer_scope = irb->exec->begin_scope;6832 Scope *outer_scope = irb->exec->begin_scope;
66996833
6700 IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, parent_scope, node, target_inst);6834 IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, target_inst);
6701 Buf *result_ptr_field_name = buf_create_from_str(RESULT_PTR_FIELD_NAME);6835 Buf *result_ptr_field_name = buf_create_from_str(RESULT_PTR_FIELD_NAME);
6702 IrInstruction *result_ptr_field_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, result_ptr_field_name);6836 IrInstruction *result_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_ptr_field_name);
67036837
6704 if (irb->codegen->have_err_ret_tracing) {6838 if (irb->codegen->have_err_ret_tracing) {
6705 IrInstruction *err_ret_trace_ptr = ir_build_error_return_trace(irb, parent_scope, node, IrInstructionErrorReturnTrace::NonNull);6839 IrInstruction *err_ret_trace_ptr = ir_build_error_return_trace(irb, scope, node, IrInstructionErrorReturnTrace::NonNull);
6706 Buf *err_ret_trace_ptr_field_name = buf_create_from_str(ERR_RET_TRACE_PTR_FIELD_NAME);6840 Buf *err_ret_trace_ptr_field_name = buf_create_from_str(ERR_RET_TRACE_PTR_FIELD_NAME);
6707 IrInstruction *err_ret_trace_ptr_field_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, err_ret_trace_ptr_field_name);6841 IrInstruction *err_ret_trace_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, err_ret_trace_ptr_field_name);
6708 ir_build_store_ptr(irb, parent_scope, node, err_ret_trace_ptr_field_ptr, err_ret_trace_ptr);6842 ir_build_store_ptr(irb, scope, node, err_ret_trace_ptr_field_ptr, err_ret_trace_ptr);
6709 }6843 }
67106844
6711 Buf *awaiter_handle_field_name = buf_create_from_str(AWAITER_HANDLE_FIELD_NAME);6845 IrBasicBlock *already_awaited_block = ir_create_basic_block(irb, scope, "AlreadyAwaited");
6712 IrInstruction *awaiter_field_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr,6846 IrBasicBlock *not_awaited_block = ir_create_basic_block(irb, scope, "NotAwaited");
6713 awaiter_handle_field_name);6847 IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, scope, "NotCanceled");
67146848 IrBasicBlock *yes_suspend_block = ir_create_basic_block(irb, scope, "YesSuspend");
6715 IrInstruction *const_bool_false = ir_build_const_bool(irb, parent_scope, node, false);6849 IrBasicBlock *no_suspend_block = ir_create_basic_block(irb, scope, "NoSuspend");
6716 VariableTableEntry *result_var = ir_create_var(irb, node, parent_scope, nullptr,6850 IrBasicBlock *merge_block = ir_create_basic_block(irb, scope, "MergeSuspend");
6851 IrBasicBlock *cleanup_block = ir_create_basic_block(irb, scope, "SuspendCleanup");
6852 IrBasicBlock *resume_block = ir_create_basic_block(irb, scope, "SuspendResume");
6853 IrBasicBlock *cancel_target_block = ir_create_basic_block(irb, scope, "CancelTarget");
6854 IrBasicBlock *do_cancel_block = ir_create_basic_block(irb, scope, "DoCancel");
6855 IrBasicBlock *do_defers_block = ir_create_basic_block(irb, scope, "DoDefers");
6856 IrBasicBlock *destroy_block = ir_create_basic_block(irb, scope, "DestroyBlock");
6857 IrBasicBlock *my_suspended_block = ir_create_basic_block(irb, scope, "AlreadySuspended");
6858 IrBasicBlock *my_not_suspended_block = ir_create_basic_block(irb, scope, "NotAlreadySuspended");
6859 IrBasicBlock *do_suspend_block = ir_create_basic_block(irb, scope, "DoSuspend");
6860
6861 Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME);
6862 IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr,
6863 atomic_state_field_name);
6864
6865 IrInstruction *promise_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_promise);
6866 IrInstruction *const_bool_false = ir_build_const_bool(irb, scope, node, false);
6867 IrInstruction *undefined_value = ir_build_const_undefined(irb, scope, node);
6868 IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize);
6869 IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0);
6870 IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, scope, node, 0x7); // 0b111
6871 IrInstruction *ptr_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000
6872 IrInstruction *await_mask = ir_build_const_usize(irb, scope, node, 0x4); // 0b100
6873 IrInstruction *is_canceled_mask = ir_build_const_usize(irb, scope, node, 0x1); // 0b001
6874 IrInstruction *is_suspended_mask = ir_build_const_usize(irb, scope, node, 0x2); // 0b010
6875
6876 VariableTableEntry *result_var = ir_create_var(irb, node, scope, nullptr,
6717 false, false, true, const_bool_false);6877 false, false, true, const_bool_false);
6718 IrInstruction *undefined_value = ir_build_const_undefined(irb, parent_scope, node);6878 IrInstruction *target_promise_type = ir_build_typeof(irb, scope, node, target_inst);
6719 IrInstruction *target_promise_type = ir_build_typeof(irb, parent_scope, node, target_inst);6879 IrInstruction *promise_result_type = ir_build_promise_result_type(irb, scope, node, target_promise_type);
6720 IrInstruction *promise_result_type = ir_build_promise_result_type(irb, parent_scope, node, target_promise_type);6880 ir_build_await_bookkeeping(irb, scope, node, promise_result_type);
6721 ir_build_await_bookkeeping(irb, parent_scope, node, promise_result_type);6881 ir_build_var_decl(irb, scope, node, result_var, promise_result_type, nullptr, undefined_value);
6722 ir_build_var_decl(irb, parent_scope, node, result_var, promise_result_type, nullptr, undefined_value);6882 IrInstruction *my_result_var_ptr = ir_build_var_ptr(irb, scope, node, result_var);
6723 IrInstruction *my_result_var_ptr = ir_build_var_ptr(irb, parent_scope, node, result_var);6883 ir_build_store_ptr(irb, scope, node, result_ptr_field_ptr, my_result_var_ptr);
6724 ir_build_store_ptr(irb, parent_scope, node, result_ptr_field_ptr, my_result_var_ptr);6884 IrInstruction *save_token = ir_build_coro_save(irb, scope, node, irb->exec->coro_handle);
6725 IrInstruction *save_token = ir_build_coro_save(irb, parent_scope, node, irb->exec->coro_handle);6885
6726 IrInstruction *promise_type_val = ir_build_const_type(irb, parent_scope, node,6886 IrInstruction *coro_handle_addr = ir_build_ptr_to_int(irb, scope, node, irb->exec->coro_handle);
6727 get_optional_type(irb->codegen, irb->codegen->builtin_types.entry_promise));6887 IrInstruction *mask_bits = ir_build_bin_op(irb, scope, node, IrBinOpBinOr, coro_handle_addr, await_mask, false);
6728 IrInstruction *maybe_await_handle = ir_build_atomic_rmw(irb, parent_scope, node, 6888 IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node,
6729 promise_type_val, awaiter_field_ptr, nullptr, irb->exec->coro_handle, nullptr,6889 usize_type_val, atomic_state_ptr, nullptr, mask_bits, nullptr,
6730 AtomicRmwOp_xchg, AtomicOrderSeqCst);6890 AtomicRmwOp_or, AtomicOrderSeqCst);
6731 IrInstruction *is_non_null = ir_build_test_nonnull(irb, parent_scope, node, maybe_await_handle);6891
6732 IrBasicBlock *yes_suspend_block = ir_create_basic_block(irb, parent_scope, "YesSuspend");6892 IrInstruction *is_awaited_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, await_mask, false);
6733 IrBasicBlock *no_suspend_block = ir_create_basic_block(irb, parent_scope, "NoSuspend");6893 IrInstruction *is_awaited_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_awaited_value, zero, false);
6734 IrBasicBlock *merge_block = ir_create_basic_block(irb, parent_scope, "MergeSuspend");6894 ir_build_cond_br(irb, scope, node, is_awaited_bool, already_awaited_block, not_awaited_block, const_bool_false);
6735 ir_build_cond_br(irb, parent_scope, node, is_non_null, no_suspend_block, yes_suspend_block, const_bool_false);6895
6896 ir_set_cursor_at_end_and_append_block(irb, already_awaited_block);
6897 ir_build_unreachable(irb, scope, node);
6898
6899 ir_set_cursor_at_end_and_append_block(irb, not_awaited_block);
6900 IrInstruction *await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false);
6901 IrInstruction *is_non_null = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false);
6902 IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false);
6903 IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false);
6904 ir_build_cond_br(irb, scope, node, is_canceled_bool, cancel_target_block, not_canceled_block, const_bool_false);
6905
6906 ir_set_cursor_at_end_and_append_block(irb, not_canceled_block);
6907 ir_build_cond_br(irb, scope, node, is_non_null, no_suspend_block, yes_suspend_block, const_bool_false);
6908
6909 ir_set_cursor_at_end_and_append_block(irb, cancel_target_block);
6910 ir_build_cancel(irb, scope, node, target_inst);
6911 ir_mark_gen(ir_build_br(irb, scope, node, cleanup_block, const_bool_false));
67366912
6737 ir_set_cursor_at_end_and_append_block(irb, no_suspend_block);6913 ir_set_cursor_at_end_and_append_block(irb, no_suspend_block);
6738 if (irb->codegen->have_err_ret_tracing) {6914 if (irb->codegen->have_err_ret_tracing) {
6739 Buf *err_ret_trace_field_name = buf_create_from_str(ERR_RET_TRACE_FIELD_NAME);6915 Buf *err_ret_trace_field_name = buf_create_from_str(ERR_RET_TRACE_FIELD_NAME);
6740 IrInstruction *src_err_ret_trace_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, err_ret_trace_field_name);6916 IrInstruction *src_err_ret_trace_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, err_ret_trace_field_name);
6741 IrInstruction *dest_err_ret_trace_ptr = ir_build_error_return_trace(irb, parent_scope, node, IrInstructionErrorReturnTrace::NonNull);6917 IrInstruction *dest_err_ret_trace_ptr = ir_build_error_return_trace(irb, scope, node, IrInstructionErrorReturnTrace::NonNull);
6742 ir_build_merge_err_ret_traces(irb, parent_scope, node, coro_promise_ptr, src_err_ret_trace_ptr, dest_err_ret_trace_ptr);6918 ir_build_merge_err_ret_traces(irb, scope, node, coro_promise_ptr, src_err_ret_trace_ptr, dest_err_ret_trace_ptr);
6743 }6919 }
6744 Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME);6920 Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME);
6745 IrInstruction *promise_result_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, result_field_name);6921 IrInstruction *promise_result_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_field_name);
6746 // If the type of the result handle_is_ptr then this does not actually perform a load. But we need it to,6922 // If the type of the result handle_is_ptr then this does not actually perform a load. But we need it to,
6747 // because we're about to destroy the memory. So we store it into our result variable.6923 // because we're about to destroy the memory. So we store it into our result variable.
6748 IrInstruction *no_suspend_result = ir_build_load_ptr(irb, parent_scope, node, promise_result_ptr);6924 IrInstruction *no_suspend_result = ir_build_load_ptr(irb, scope, node, promise_result_ptr);
6749 ir_build_store_ptr(irb, parent_scope, node, my_result_var_ptr, no_suspend_result);6925 ir_build_store_ptr(irb, scope, node, my_result_var_ptr, no_suspend_result);
6750 ir_build_cancel(irb, parent_scope, node, target_inst);6926 ir_build_cancel(irb, scope, node, target_inst);
6751 ir_build_br(irb, parent_scope, node, merge_block, const_bool_false);6927 ir_build_br(irb, scope, node, merge_block, const_bool_false);
6928
67526929
6753 ir_set_cursor_at_end_and_append_block(irb, yes_suspend_block);6930 ir_set_cursor_at_end_and_append_block(irb, yes_suspend_block);
6754 IrInstruction *suspend_code = ir_build_coro_suspend(irb, parent_scope, node, save_token, const_bool_false);6931 IrInstruction *my_prev_atomic_value = ir_build_atomic_rmw(irb, scope, node,
6755 IrBasicBlock *cleanup_block = ir_create_basic_block(irb, parent_scope, "SuspendCleanup");6932 usize_type_val, irb->exec->atomic_state_field_ptr, nullptr, is_suspended_mask, nullptr,
6756 IrBasicBlock *resume_block = ir_create_basic_block(irb, parent_scope, "SuspendResume");6933 AtomicRmwOp_or, AtomicOrderSeqCst);
6934 IrInstruction *my_is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, my_prev_atomic_value, is_suspended_mask, false);
6935 IrInstruction *my_is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, my_is_suspended_value, zero, false);
6936 ir_build_cond_br(irb, scope, node, my_is_suspended_bool, my_suspended_block, my_not_suspended_block, const_bool_false);
6937
6938 ir_set_cursor_at_end_and_append_block(irb, my_suspended_block);
6939 ir_build_unreachable(irb, scope, node);
6940
6941 ir_set_cursor_at_end_and_append_block(irb, my_not_suspended_block);
6942 IrInstruction *my_is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, my_prev_atomic_value, is_canceled_mask, false);
6943 IrInstruction *my_is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, my_is_canceled_value, zero, false);
6944 ir_build_cond_br(irb, scope, node, my_is_canceled_bool, cleanup_block, do_suspend_block, const_bool_false);
6945
6946 ir_set_cursor_at_end_and_append_block(irb, do_suspend_block);
6947 IrInstruction *suspend_code = ir_build_coro_suspend(irb, scope, node, save_token, const_bool_false);
67576948
6758 IrInstructionSwitchBrCase *cases = allocate<IrInstructionSwitchBrCase>(2);6949 IrInstructionSwitchBrCase *cases = allocate<IrInstructionSwitchBrCase>(2);
6759 cases[0].value = ir_build_const_u8(irb, parent_scope, node, 0);6950 cases[0].value = ir_build_const_u8(irb, scope, node, 0);
6760 cases[0].block = resume_block;6951 cases[0].block = resume_block;
6761 cases[1].value = ir_build_const_u8(irb, parent_scope, node, 1);6952 cases[1].value = ir_build_const_u8(irb, scope, node, 1);
6762 cases[1].block = cleanup_block;6953 cases[1].block = destroy_block;
6763 ir_build_switch_br(irb, parent_scope, node, suspend_code, irb->exec->coro_suspend_block,6954 ir_build_switch_br(irb, scope, node, suspend_code, irb->exec->coro_suspend_block,
6764 2, cases, const_bool_false, nullptr);6955 2, cases, const_bool_false, nullptr);
67656956
6957 ir_set_cursor_at_end_and_append_block(irb, destroy_block);
6958 ir_gen_cancel_target(irb, scope, node, target_inst, false, true);
6959 ir_mark_gen(ir_build_br(irb, scope, node, cleanup_block, const_bool_false));
6960
6766 ir_set_cursor_at_end_and_append_block(irb, cleanup_block);6961 ir_set_cursor_at_end_and_append_block(irb, cleanup_block);
6767 ir_gen_defers_for_block(irb, parent_scope, outer_scope, true);6962 IrInstruction *my_mask_bits = ir_build_bin_op(irb, scope, node, IrBinOpBinOr, ptr_mask, is_canceled_mask, false);
6768 ir_mark_gen(ir_build_br(irb, parent_scope, node, irb->exec->coro_final_cleanup_block, const_bool_false));6963 IrInstruction *b_my_prev_atomic_value = ir_build_atomic_rmw(irb, scope, node,
6964 usize_type_val, irb->exec->atomic_state_field_ptr, nullptr, my_mask_bits, nullptr,
6965 AtomicRmwOp_or, AtomicOrderSeqCst);
6966 IrInstruction *my_await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, b_my_prev_atomic_value, ptr_mask, false);
6967 IrInstruction *dont_have_my_await_handle = ir_build_bin_op(irb, scope, node, IrBinOpCmpEq, my_await_handle_addr, zero, false);
6968 IrInstruction *dont_destroy_ourselves = ir_build_bin_op(irb, scope, node, IrBinOpBoolAnd, dont_have_my_await_handle, is_canceled_bool, false);
6969 ir_build_cond_br(irb, scope, node, dont_have_my_await_handle, do_defers_block, do_cancel_block, const_bool_false);
6970
6971 ir_set_cursor_at_end_and_append_block(irb, do_cancel_block);
6972 IrInstruction *my_await_handle = ir_build_int_to_ptr(irb, scope, node, promise_type_val, my_await_handle_addr);
6973 ir_gen_cancel_target(irb, scope, node, my_await_handle, true, false);
6974 ir_mark_gen(ir_build_br(irb, scope, node, do_defers_block, const_bool_false));
6975
6976 ir_set_cursor_at_end_and_append_block(irb, do_defers_block);
6977 ir_gen_defers_for_block(irb, scope, outer_scope, true);
6978 ir_mark_gen(ir_build_cond_br(irb, scope, node, dont_destroy_ourselves, irb->exec->coro_early_final, irb->exec->coro_final_cleanup_block, const_bool_false));
67696979
6770 ir_set_cursor_at_end_and_append_block(irb, resume_block);6980 ir_set_cursor_at_end_and_append_block(irb, resume_block);
6771 ir_build_br(irb, parent_scope, node, merge_block, const_bool_false);6981 ir_build_br(irb, scope, node, merge_block, const_bool_false);
67726982
6773 ir_set_cursor_at_end_and_append_block(irb, merge_block);6983 ir_set_cursor_at_end_and_append_block(irb, merge_block);
6774 return ir_build_load_ptr(irb, parent_scope, node, my_result_var_ptr);6984 return ir_build_load_ptr(irb, scope, node, my_result_var_ptr);
6775}6985}
67766986
6777static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNode *node) {6987static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNode *node) {
...@@ -6810,9 +7020,52 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod...@@ -6810,9 +7020,52 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod
68107020
6811 IrBasicBlock *cleanup_block = ir_create_basic_block(irb, parent_scope, "SuspendCleanup");7021 IrBasicBlock *cleanup_block = ir_create_basic_block(irb, parent_scope, "SuspendCleanup");
6812 IrBasicBlock *resume_block = ir_create_basic_block(irb, parent_scope, "SuspendResume");7022 IrBasicBlock *resume_block = ir_create_basic_block(irb, parent_scope, "SuspendResume");
68137023 IrBasicBlock *suspended_block = ir_create_basic_block(irb, parent_scope, "AlreadySuspended");
6814 IrInstruction *suspend_code;7024 IrBasicBlock *canceled_block = ir_create_basic_block(irb, parent_scope, "IsCanceled");
7025 IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, parent_scope, "NotCanceled");
7026 IrBasicBlock *not_suspended_block = ir_create_basic_block(irb, parent_scope, "NotAlreadySuspended");
7027 IrBasicBlock *cancel_awaiter_block = ir_create_basic_block(irb, parent_scope, "CancelAwaiter");
7028
7029 IrInstruction *promise_type_val = ir_build_const_type(irb, parent_scope, node, irb->codegen->builtin_types.entry_promise);
7030 IrInstruction *const_bool_true = ir_build_const_bool(irb, parent_scope, node, true);
6815 IrInstruction *const_bool_false = ir_build_const_bool(irb, parent_scope, node, false);7031 IrInstruction *const_bool_false = ir_build_const_bool(irb, parent_scope, node, false);
7032 IrInstruction *usize_type_val = ir_build_const_type(irb, parent_scope, node, irb->codegen->builtin_types.entry_usize);
7033 IrInstruction *is_canceled_mask = ir_build_const_usize(irb, parent_scope, node, 0x1); // 0b001
7034 IrInstruction *is_suspended_mask = ir_build_const_usize(irb, parent_scope, node, 0x2); // 0b010
7035 IrInstruction *zero = ir_build_const_usize(irb, parent_scope, node, 0);
7036 IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, parent_scope, node, 0x7); // 0b111
7037 IrInstruction *ptr_mask = ir_build_un_op(irb, parent_scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000
7038
7039 IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, parent_scope, node,
7040 usize_type_val, irb->exec->atomic_state_field_ptr, nullptr, is_suspended_mask, nullptr,
7041 AtomicRmwOp_or, AtomicOrderSeqCst);
7042
7043 IrInstruction *is_canceled_value = ir_build_bin_op(irb, parent_scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false);
7044 IrInstruction *is_canceled_bool = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false);
7045 ir_build_cond_br(irb, parent_scope, node, is_canceled_bool, canceled_block, not_canceled_block, const_bool_false);
7046
7047 ir_set_cursor_at_end_and_append_block(irb, canceled_block);
7048 IrInstruction *await_handle_addr = ir_build_bin_op(irb, parent_scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false);
7049 IrInstruction *have_await_handle = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false);
7050 IrBasicBlock *post_canceled_block = irb->current_basic_block;
7051 ir_build_cond_br(irb, parent_scope, node, have_await_handle, cancel_awaiter_block, cleanup_block, const_bool_false);
7052
7053 ir_set_cursor_at_end_and_append_block(irb, cancel_awaiter_block);
7054 IrInstruction *await_handle = ir_build_int_to_ptr(irb, parent_scope, node, promise_type_val, await_handle_addr);
7055 ir_gen_cancel_target(irb, parent_scope, node, await_handle, true, false);
7056 IrBasicBlock *post_cancel_awaiter_block = irb->current_basic_block;
7057 ir_build_br(irb, parent_scope, node, cleanup_block, const_bool_false);
7058
7059 ir_set_cursor_at_end_and_append_block(irb, not_canceled_block);
7060 IrInstruction *is_suspended_value = ir_build_bin_op(irb, parent_scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false);
7061 IrInstruction *is_suspended_bool = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false);
7062 ir_build_cond_br(irb, parent_scope, node, is_suspended_bool, suspended_block, not_suspended_block, const_bool_false);
7063
7064 ir_set_cursor_at_end_and_append_block(irb, suspended_block);
7065 ir_build_unreachable(irb, parent_scope, node);
7066
7067 ir_set_cursor_at_end_and_append_block(irb, not_suspended_block);
7068 IrInstruction *suspend_code;
6816 if (node->data.suspend.block == nullptr) {7069 if (node->data.suspend.block == nullptr) {
6817 suspend_code = ir_build_coro_suspend(irb, parent_scope, node, nullptr, const_bool_false);7070 suspend_code = ir_build_coro_suspend(irb, parent_scope, node, nullptr, const_bool_false);
6818 } else {7071 } else {
...@@ -6840,13 +7093,20 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod...@@ -6840,13 +7093,20 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod
6840 cases[0].value = ir_mark_gen(ir_build_const_u8(irb, parent_scope, node, 0));7093 cases[0].value = ir_mark_gen(ir_build_const_u8(irb, parent_scope, node, 0));
6841 cases[0].block = resume_block;7094 cases[0].block = resume_block;
6842 cases[1].value = ir_mark_gen(ir_build_const_u8(irb, parent_scope, node, 1));7095 cases[1].value = ir_mark_gen(ir_build_const_u8(irb, parent_scope, node, 1));
6843 cases[1].block = cleanup_block;7096 cases[1].block = canceled_block;
6844 ir_mark_gen(ir_build_switch_br(irb, parent_scope, node, suspend_code, irb->exec->coro_suspend_block,7097 ir_mark_gen(ir_build_switch_br(irb, parent_scope, node, suspend_code, irb->exec->coro_suspend_block,
6845 2, cases, const_bool_false, nullptr));7098 2, cases, const_bool_false, nullptr));
68467099
6847 ir_set_cursor_at_end_and_append_block(irb, cleanup_block);7100 ir_set_cursor_at_end_and_append_block(irb, cleanup_block);
7101 IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2);
7102 IrInstruction **incoming_values = allocate<IrInstruction *>(2);
7103 incoming_blocks[0] = post_canceled_block;
7104 incoming_values[0] = const_bool_true;
7105 incoming_blocks[1] = post_cancel_awaiter_block;
7106 incoming_values[1] = const_bool_false;
7107 IrInstruction *destroy_ourselves = ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values);
6848 ir_gen_defers_for_block(irb, parent_scope, outer_scope, true);7108 ir_gen_defers_for_block(irb, parent_scope, outer_scope, true);
6849 ir_mark_gen(ir_build_br(irb, parent_scope, node, irb->exec->coro_final_cleanup_block, const_bool_false));7109 ir_mark_gen(ir_build_cond_br(irb, parent_scope, node, destroy_ourselves, irb->exec->coro_final_cleanup_block, irb->exec->coro_early_final, const_bool_false));
68507110
6851 ir_set_cursor_at_end_and_append_block(irb, resume_block);7111 ir_set_cursor_at_end_and_append_block(irb, resume_block);
6852 return ir_mark_gen(ir_build_const_void(irb, parent_scope, node));7112 return ir_mark_gen(ir_build_const_void(irb, parent_scope, node));
...@@ -7087,10 +7347,11 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec...@@ -7087,10 +7347,11 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
7087 IrInstruction *coro_mem_ptr = ir_build_ptr_cast(irb, coro_scope, node, u8_ptr_type, maybe_coro_mem_ptr);7347 IrInstruction *coro_mem_ptr = ir_build_ptr_cast(irb, coro_scope, node, u8_ptr_type, maybe_coro_mem_ptr);
7088 irb->exec->coro_handle = ir_build_coro_begin(irb, coro_scope, node, coro_id, coro_mem_ptr);7348 irb->exec->coro_handle = ir_build_coro_begin(irb, coro_scope, node, coro_id, coro_mem_ptr);
70897349
7090 Buf *awaiter_handle_field_name = buf_create_from_str(AWAITER_HANDLE_FIELD_NAME);7350 Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME);
7091 irb->exec->coro_awaiter_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr,7351 irb->exec->atomic_state_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr,
7092 awaiter_handle_field_name);7352 atomic_state_field_name);
7093 ir_build_store_ptr(irb, scope, node, irb->exec->coro_awaiter_field_ptr, null_value);7353 IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0);
7354 ir_build_store_ptr(irb, scope, node, irb->exec->atomic_state_field_ptr, zero);
7094 Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME);7355 Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME);
7095 irb->exec->coro_result_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_field_name);7356 irb->exec->coro_result_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_field_name);
7096 result_ptr_field_name = buf_create_from_str(RESULT_PTR_FIELD_NAME);7357 result_ptr_field_name = buf_create_from_str(RESULT_PTR_FIELD_NAME);
...@@ -7108,7 +7369,6 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec...@@ -7108,7 +7369,6 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
7108 // coordinate with builtin.zig7369 // coordinate with builtin.zig
7109 Buf *index_name = buf_create_from_str("index");7370 Buf *index_name = buf_create_from_str("index");
7110 IrInstruction *index_ptr = ir_build_field_ptr(irb, scope, node, err_ret_trace_ptr, index_name);7371 IrInstruction *index_ptr = ir_build_field_ptr(irb, scope, node, err_ret_trace_ptr, index_name);
7111 IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0);
7112 ir_build_store_ptr(irb, scope, node, index_ptr, zero);7372 ir_build_store_ptr(irb, scope, node, index_ptr, zero);
71137373
7114 Buf *instruction_addresses_name = buf_create_from_str("instruction_addresses");7374 Buf *instruction_addresses_name = buf_create_from_str("instruction_addresses");
...@@ -7231,7 +7491,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec...@@ -7231,7 +7491,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
7231 ir_build_cond_br(irb, scope, node, resume_awaiter, resume_block, irb->exec->coro_suspend_block, const_bool_false);7491 ir_build_cond_br(irb, scope, node, resume_awaiter, resume_block, irb->exec->coro_suspend_block, const_bool_false);
72327492
7233 ir_set_cursor_at_end_and_append_block(irb, resume_block);7493 ir_set_cursor_at_end_and_append_block(irb, resume_block);
7234 ir_build_coro_resume(irb, scope, node, awaiter_handle);7494 ir_gen_resume_target(irb, scope, node, awaiter_handle);
7235 ir_build_br(irb, scope, node, irb->exec->coro_suspend_block, const_bool_false);7495 ir_build_br(irb, scope, node, irb->exec->coro_suspend_block, const_bool_false);
7236 }7496 }
72377497
src/parser.cpp+2-23
...@@ -648,30 +648,12 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, size_t *token_index, bool m...@@ -648,30 +648,12 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, size_t *token_index, bool m
648}648}
649649
650/*650/*
651SuspendExpression(body) = option(Symbol ":") "suspend" option(("|" Symbol "|" body))651SuspendExpression(body) = "suspend" option(("|" Symbol "|" body))
652*/652*/
653static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, bool mandatory) {653static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, bool mandatory) {
654 size_t orig_token_index = *token_index;654 size_t orig_token_index = *token_index;
655655
656 Token *name_token = nullptr;656 Token *suspend_token = &pc->tokens->at(*token_index);
657 Token *token = &pc->tokens->at(*token_index);
658 if (token->id == TokenIdSymbol) {
659 *token_index += 1;
660 Token *colon_token = &pc->tokens->at(*token_index);
661 if (colon_token->id == TokenIdColon) {
662 *token_index += 1;
663 name_token = token;
664 token = &pc->tokens->at(*token_index);
665 } else if (mandatory) {
666 ast_expect_token(pc, colon_token, TokenIdColon);
667 zig_unreachable();
668 } else {
669 *token_index = orig_token_index;
670 return nullptr;
671 }
672 }
673
674 Token *suspend_token = token;
675 if (suspend_token->id == TokenIdKeywordSuspend) {657 if (suspend_token->id == TokenIdKeywordSuspend) {
676 *token_index += 1;658 *token_index += 1;
677 } else if (mandatory) {659 } else if (mandatory) {
...@@ -693,9 +675,6 @@ static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, b...@@ -693,9 +675,6 @@ static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, b
693 }675 }
694676
695 AstNode *node = ast_create_node(pc, NodeTypeSuspend, suspend_token);677 AstNode *node = ast_create_node(pc, NodeTypeSuspend, suspend_token);
696 if (name_token != nullptr) {
697 node->data.suspend.name = token_buf(name_token);
698 }
699 node->data.suspend.promise_symbol = ast_parse_symbol(pc, token_index);678 node->data.suspend.promise_symbol = ast_parse_symbol(pc, token_index);
700 ast_eat_token(pc, token_index, TokenIdBinOr);679 ast_eat_token(pc, token_index, TokenIdBinOr);
701 node->data.suspend.block = ast_parse_block(pc, token_index, true);680 node->data.suspend.block = ast_parse_block(pc, token_index, true);
std/debug/index.zig+12-2
...@@ -27,7 +27,7 @@ pub fn warn(comptime fmt: []const u8, args: ...) void {...@@ -27,7 +27,7 @@ pub fn warn(comptime fmt: []const u8, args: ...) void {
27 const stderr = getStderrStream() catch return;27 const stderr = getStderrStream() catch return;
28 stderr.print(fmt, args) catch return;28 stderr.print(fmt, args) catch return;
29}29}
30fn getStderrStream() !*io.OutStream(io.FileOutStream.Error) {30pub fn getStderrStream() !*io.OutStream(io.FileOutStream.Error) {
31 if (stderr_stream) |st| {31 if (stderr_stream) |st| {
32 return st;32 return st;
33 } else {33 } else {
...@@ -172,6 +172,16 @@ pub fn writeStackTrace(stack_trace: *const builtin.StackTrace, out_stream: var,...@@ -172,6 +172,16 @@ pub fn writeStackTrace(stack_trace: *const builtin.StackTrace, out_stream: var,
172 }172 }
173}173}
174174
175pub inline fn getReturnAddress(frame_count: usize) usize {
176 var fp = @ptrToInt(@frameAddress());
177 var i: usize = 0;
178 while (fp != 0 and i < frame_count) {
179 fp = @intToPtr(*const usize, fp).*;
180 i += 1;
181 }
182 return @intToPtr(*const usize, fp + @sizeOf(usize)).*;
183}
184
175pub fn writeCurrentStackTrace(out_stream: var, allocator: *mem.Allocator, debug_info: *ElfStackTrace, tty_color: bool, start_addr: ?usize) !void {185pub fn writeCurrentStackTrace(out_stream: var, allocator: *mem.Allocator, debug_info: *ElfStackTrace, tty_color: bool, start_addr: ?usize) !void {
176 const AddressState = union(enum) {186 const AddressState = union(enum) {
177 NotLookingForStartAddress,187 NotLookingForStartAddress,
...@@ -205,7 +215,7 @@ pub fn writeCurrentStackTrace(out_stream: var, allocator: *mem.Allocator, debug_...@@ -205,7 +215,7 @@ pub fn writeCurrentStackTrace(out_stream: var, allocator: *mem.Allocator, debug_
205 }215 }
206}216}
207217
208fn printSourceAtAddress(debug_info: *ElfStackTrace, out_stream: var, address: usize, tty_color: bool) !void {218pub fn printSourceAtAddress(debug_info: *ElfStackTrace, out_stream: var, address: usize, tty_color: bool) !void {
209 switch (builtin.os) {219 switch (builtin.os) {
210 builtin.Os.windows => return error.UnsupportedDebugInfo,220 builtin.Os.windows => return error.UnsupportedDebugInfo,
211 builtin.Os.macosx => {221 builtin.Os.macosx => {
std/event/loop.zig+2-2
...@@ -55,7 +55,7 @@ pub const Loop = struct {...@@ -55,7 +55,7 @@ pub const Loop = struct {
55 /// After initialization, call run().55 /// After initialization, call run().
56 /// TODO copy elision / named return values so that the threads referencing *Loop56 /// TODO copy elision / named return values so that the threads referencing *Loop
57 /// have the correct pointer value.57 /// have the correct pointer value.
58 fn initSingleThreaded(self: *Loop, allocator: *mem.Allocator) !void {58 pub fn initSingleThreaded(self: *Loop, allocator: *mem.Allocator) !void {
59 return self.initInternal(allocator, 1);59 return self.initInternal(allocator, 1);
60 }60 }
6161
...@@ -64,7 +64,7 @@ pub const Loop = struct {...@@ -64,7 +64,7 @@ pub const Loop = struct {
64 /// After initialization, call run().64 /// After initialization, call run().
65 /// TODO copy elision / named return values so that the threads referencing *Loop65 /// TODO copy elision / named return values so that the threads referencing *Loop
66 /// have the correct pointer value.66 /// have the correct pointer value.
67 fn initMultiThreaded(self: *Loop, allocator: *mem.Allocator) !void {67 pub fn initMultiThreaded(self: *Loop, allocator: *mem.Allocator) !void {
68 const core_count = try std.os.cpuCount(allocator);68 const core_count = try std.os.cpuCount(allocator);
69 return self.initInternal(allocator, core_count);69 return self.initInternal(allocator, core_count);
70 }70 }
test/behavior.zig+1
...@@ -16,6 +16,7 @@ comptime {...@@ -16,6 +16,7 @@ comptime {
16 _ = @import("cases/bugs/828.zig");16 _ = @import("cases/bugs/828.zig");
17 _ = @import("cases/bugs/920.zig");17 _ = @import("cases/bugs/920.zig");
18 _ = @import("cases/byval_arg_var.zig");18 _ = @import("cases/byval_arg_var.zig");
19 _ = @import("cases/cancel.zig");
19 _ = @import("cases/cast.zig");20 _ = @import("cases/cast.zig");
20 _ = @import("cases/const_slice_child.zig");21 _ = @import("cases/const_slice_child.zig");
21 _ = @import("cases/coroutine_await_struct.zig");22 _ = @import("cases/coroutine_await_struct.zig");
test/cases/cancel.zig created+92
...@@ -0,0 +1,92 @@
1const std = @import("std");
2
3var defer_f1: bool = false;
4var defer_f2: bool = false;
5var defer_f3: bool = false;
6
7test "cancel forwards" {
8 var da = std.heap.DirectAllocator.init();
9 defer da.deinit();
10
11 const p = async<&da.allocator> f1() catch unreachable;
12 cancel p;
13 std.debug.assert(defer_f1);
14 std.debug.assert(defer_f2);
15 std.debug.assert(defer_f3);
16}
17
18async fn f1() void {
19 defer {
20 defer_f1 = true;
21 }
22 await (async f2() catch unreachable);
23}
24
25async fn f2() void {
26 defer {
27 defer_f2 = true;
28 }
29 await (async f3() catch unreachable);
30}
31
32async fn f3() void {
33 defer {
34 defer_f3 = true;
35 }
36 suspend;
37}
38
39var defer_b1: bool = false;
40var defer_b2: bool = false;
41var defer_b3: bool = false;
42var defer_b4: bool = false;
43
44test "cancel backwards" {
45 var da = std.heap.DirectAllocator.init();
46 defer da.deinit();
47
48 const p = async<&da.allocator> b1() catch unreachable;
49 cancel p;
50 std.debug.assert(defer_b1);
51 std.debug.assert(defer_b2);
52 std.debug.assert(defer_b3);
53 std.debug.assert(defer_b4);
54}
55
56async fn b1() void {
57 defer {
58 defer_b1 = true;
59 }
60 await (async b2() catch unreachable);
61}
62
63var b4_handle: promise = undefined;
64
65async fn b2() void {
66 const b3_handle = async b3() catch unreachable;
67 resume b4_handle;
68 cancel b4_handle;
69 defer {
70 defer_b2 = true;
71 }
72 const value = await b3_handle;
73 @panic("unreachable");
74}
75
76async fn b3() i32 {
77 defer {
78 defer_b3 = true;
79 }
80 await (async b4() catch unreachable);
81 return 1234;
82}
83
84async fn b4() void {
85 defer {
86 defer_b4 = true;
87 }
88 suspend |p| {
89 b4_handle = p;
90 }
91 suspend;
92}
test/cases/coroutines.zig+2-2
...@@ -244,8 +244,8 @@ test "break from suspend" {...@@ -244,8 +244,8 @@ test "break from suspend" {
244 std.debug.assert(my_result == 2);244 std.debug.assert(my_result == 2);
245}245}
246async fn testBreakFromSuspend(my_result: *i32) void {246async fn testBreakFromSuspend(my_result: *i32) void {
247 s: suspend |p| {247 suspend |p| {
248 break :s;248 resume p;
249 }249 }
250 my_result.* += 1;250 my_result.* += 1;
251 suspend;251 suspend;