| author | |
| committer | |
| log | 056c4e2c988c0a2ff6f1be8fe18a0a056d848271 |
| tree | 86d830fbc801536ec06d7f5d7e609ff01e32288a |
| parent | 0f879d02a4c4b1de0e28c2863c1e5f3760eb5b19 |
| signature | Commit is signed but in an unrecognized format. |
9 files changed, 384 insertions(+), 146 deletions(-)
BRANCH_TODO+5-1| ... | ... | @@ -1,4 +1,5 @@ |
| 1 | * await | |
| 1 | * compile error for error: expected anyframe->T, found 'anyframe' | |
| 2 | * compile error for error: expected anyframe->T, found 'i32' | |
| 2 | 3 | * await of a non async function |
| 3 | 4 | * await in single-threaded mode |
| 4 | 5 | * async call on a non async function |
| ... | ... | @@ -13,3 +14,6 @@ |
| 13 | 14 | * @typeInfo for @Frame(func) |
| 14 | 15 | * peer type resolution of *@Frame(func) and anyframe |
| 15 | 16 | * peer type resolution of *@Frame(func) and anyframe->T when the return type matches |
| 17 | * returning a value from within a suspend block | |
| 18 | * struct types as the return type of an async function. make sure it works with return result locations. | |
| 19 | * make resuming inside a suspend block, with nothing after it, a must-tail call. |
src/all_types.hpp+9-1| ... | ... | @@ -1550,6 +1550,8 @@ enum PanicMsgId { |
| 1550 | 1550 | PanicMsgIdFloatToInt, |
| 1551 | 1551 | PanicMsgIdPtrCastNull, |
| 1552 | 1552 | PanicMsgIdBadResume, |
| 1553 | PanicMsgIdBadAwait, | |
| 1554 | PanicMsgIdBadReturn, | |
| 1553 | 1555 | |
| 1554 | 1556 | PanicMsgIdCount, |
| 1555 | 1557 | }; |
| ... | ... | @@ -1795,7 +1797,6 @@ struct CodeGen { |
| 1795 | 1797 | ZigType *entry_arg_tuple; |
| 1796 | 1798 | ZigType *entry_enum_literal; |
| 1797 | 1799 | ZigType *entry_any_frame; |
| 1798 | ZigType *entry_async_fn; | |
| 1799 | 1800 | } builtin_types; |
| 1800 | 1801 | |
| 1801 | 1802 | ZigType *align_amt_type; |
| ... | ... | @@ -2348,6 +2349,7 @@ enum IrInstructionId { |
| 2348 | 2349 | IrInstructionIdUnionInitNamedField, |
| 2349 | 2350 | IrInstructionIdSuspendBegin, |
| 2350 | 2351 | IrInstructionIdSuspendBr, |
| 2352 | IrInstructionIdAwait, | |
| 2351 | 2353 | IrInstructionIdCoroResume, |
| 2352 | 2354 | }; |
| 2353 | 2355 | |
| ... | ... | @@ -3600,6 +3602,12 @@ struct IrInstructionSuspendBr { |
| 3600 | 3602 | IrBasicBlock *resume_block; |
| 3601 | 3603 | }; |
| 3602 | 3604 | |
| 3605 | struct IrInstructionAwait { | |
| 3606 | IrInstruction base; | |
| 3607 | ||
| 3608 | IrInstruction *frame; | |
| 3609 | }; | |
| 3610 | ||
| 3603 | 3611 | struct IrInstructionCoroResume { |
| 3604 | 3612 | IrInstruction base; |
| 3605 | 3613 |
src/analyze.cpp+21-4| ... | ... | @@ -3807,6 +3807,9 @@ static void add_async_error_notes(CodeGen *g, ErrorMsg *msg, ZigFn *fn) { |
| 3807 | 3807 | } else if (fn->inferred_async_node->type == NodeTypeSuspend) { |
| 3808 | 3808 | add_error_note(g, msg, fn->inferred_async_node, |
| 3809 | 3809 | buf_sprintf("suspends here")); |
| 3810 | } else if (fn->inferred_async_node->type == NodeTypeAwaitExpr) { | |
| 3811 | add_error_note(g, msg, fn->inferred_async_node, | |
| 3812 | buf_sprintf("await is a suspend point")); | |
| 3810 | 3813 | } else { |
| 3811 | 3814 | zig_unreachable(); |
| 3812 | 3815 | } |
| ... | ... | @@ -7361,7 +7364,7 @@ static void resolve_llvm_types_fn_type(CodeGen *g, ZigType *fn_type) { |
| 7361 | 7364 | param_di_types.append(get_llvm_di_type(g, gen_type)); |
| 7362 | 7365 | } |
| 7363 | 7366 | if (is_async) { |
| 7364 | fn_type->data.fn.gen_param_info = allocate<FnGenParamInfo>(1); | |
| 7367 | fn_type->data.fn.gen_param_info = allocate<FnGenParamInfo>(2); | |
| 7365 | 7368 | |
| 7366 | 7369 | ZigType *frame_type = get_any_frame_type(g, fn_type_id->return_type); |
| 7367 | 7370 | gen_param_types.append(get_llvm_type(g, frame_type)); |
| ... | ... | @@ -7370,6 +7373,13 @@ static void resolve_llvm_types_fn_type(CodeGen *g, ZigType *fn_type) { |
| 7370 | 7373 | fn_type->data.fn.gen_param_info[0].src_index = 0; |
| 7371 | 7374 | fn_type->data.fn.gen_param_info[0].gen_index = 0; |
| 7372 | 7375 | fn_type->data.fn.gen_param_info[0].type = frame_type; |
| 7376 | ||
| 7377 | gen_param_types.append(get_llvm_type(g, g->builtin_types.entry_usize)); | |
| 7378 | param_di_types.append(get_llvm_di_type(g, g->builtin_types.entry_usize)); | |
| 7379 | ||
| 7380 | fn_type->data.fn.gen_param_info[1].src_index = 1; | |
| 7381 | fn_type->data.fn.gen_param_info[1].gen_index = 1; | |
| 7382 | fn_type->data.fn.gen_param_info[1].type = g->builtin_types.entry_usize; | |
| 7373 | 7383 | } else { |
| 7374 | 7384 | fn_type->data.fn.gen_param_info = allocate<FnGenParamInfo>(fn_type_id->param_count); |
| 7375 | 7385 | for (size_t i = 0; i < fn_type_id->param_count; i += 1) { |
| ... | ... | @@ -7434,15 +7444,21 @@ void resolve_llvm_types_fn(CodeGen *g, ZigFn *fn) { |
| 7434 | 7444 | |
| 7435 | 7445 | ZigType *gen_return_type = g->builtin_types.entry_void; |
| 7436 | 7446 | ZigList<ZigLLVMDIType *> param_di_types = {}; |
| 7447 | ZigList<LLVMTypeRef> gen_param_types = {}; | |
| 7437 | 7448 | // first "parameter" is return value |
| 7438 | 7449 | param_di_types.append(get_llvm_di_type(g, gen_return_type)); |
| 7439 | 7450 | |
| 7440 | 7451 | ZigType *frame_type = get_coro_frame_type(g, fn); |
| 7441 | 7452 | ZigType *ptr_type = get_pointer_to_type(g, frame_type, false); |
| 7442 | LLVMTypeRef gen_param_type = get_llvm_type(g, ptr_type); | |
| 7453 | gen_param_types.append(get_llvm_type(g, ptr_type)); | |
| 7443 | 7454 | param_di_types.append(get_llvm_di_type(g, ptr_type)); |
| 7444 | 7455 | |
| 7445 | fn->raw_type_ref = LLVMFunctionType(get_llvm_type(g, gen_return_type), &gen_param_type, 1, false); | |
| 7456 | // this parameter is used to pass the result pointer when await completes | |
| 7457 | gen_param_types.append(get_llvm_type(g, g->builtin_types.entry_usize)); | |
| 7458 | param_di_types.append(get_llvm_di_type(g, g->builtin_types.entry_usize)); | |
| 7459 | ||
| 7460 | fn->raw_type_ref = LLVMFunctionType(get_llvm_type(g, gen_return_type), | |
| 7461 | gen_param_types.items, gen_param_types.length, false); | |
| 7446 | 7462 | fn->raw_di_type = ZigLLVMCreateSubroutineType(g->dbuilder, param_di_types.items, (int)param_di_types.length, 0); |
| 7447 | 7463 | } |
| 7448 | 7464 | |
| ... | ... | @@ -7493,7 +7509,8 @@ static void resolve_llvm_types_any_frame(CodeGen *g, ZigType *any_frame_type, Re |
| 7493 | 7509 | 8*g->pointer_size_bytes, 8*g->builtin_types.entry_usize->abi_align, buf_ptr(&any_frame_type->name)); |
| 7494 | 7510 | |
| 7495 | 7511 | LLVMTypeRef llvm_void = LLVMVoidType(); |
| 7496 | LLVMTypeRef fn_type = LLVMFunctionType(llvm_void, &any_frame_type->llvm_type, 1, false); | |
| 7512 | LLVMTypeRef arg_types[] = {any_frame_type->llvm_type, g->builtin_types.entry_usize->llvm_type}; | |
| 7513 | LLVMTypeRef fn_type = LLVMFunctionType(llvm_void, arg_types, 2, false); | |
| 7497 | 7514 | LLVMTypeRef usize_type_ref = get_llvm_type(g, g->builtin_types.entry_usize); |
| 7498 | 7515 | ZigLLVMDIType *usize_di_type = get_llvm_di_type(g, g->builtin_types.entry_usize); |
| 7499 | 7516 | ZigLLVMDIScope *compile_unit_scope = ZigLLVMCompileUnitToScope(g->compile_unit); |
src/ast_render.cpp+3-1| ... | ... | @@ -1149,9 +1149,11 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { |
| 1149 | 1149 | } |
| 1150 | 1150 | case NodeTypeSuspend: |
| 1151 | 1151 | { |
| 1152 | fprintf(ar->f, "suspend"); | |
| 1153 | 1152 | if (node->data.suspend.block != nullptr) { |
| 1153 | fprintf(ar->f, "suspend "); | |
| 1154 | 1154 | render_node_grouped(ar, node->data.suspend.block); |
| 1155 | } else { | |
| 1156 | fprintf(ar->f, "suspend\n"); | |
| 1155 | 1157 | } |
| 1156 | 1158 | break; |
| 1157 | 1159 | } |
src/codegen.cpp+154-8| ... | ... | @@ -873,6 +873,10 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) { |
| 873 | 873 | return buf_create_from_str("cast causes pointer to be null"); |
| 874 | 874 | case PanicMsgIdBadResume: |
| 875 | 875 | return buf_create_from_str("invalid resume of async function"); |
| 876 | case PanicMsgIdBadAwait: | |
| 877 | return buf_create_from_str("async function awaited twice"); | |
| 878 | case PanicMsgIdBadReturn: | |
| 879 | return buf_create_from_str("async function returned twice"); | |
| 876 | 880 | } |
| 877 | 881 | zig_unreachable(); |
| 878 | 882 | } |
| ... | ... | @@ -1991,14 +1995,66 @@ static LLVMValueRef ir_render_save_err_ret_addr(CodeGen *g, IrExecutable *execut |
| 1991 | 1995 | |
| 1992 | 1996 | static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrInstructionReturn *return_instruction) { |
| 1993 | 1997 | if (fn_is_async(g->cur_fn)) { |
| 1998 | LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; | |
| 1999 | LLVMValueRef locals_ptr = g->cur_ret_ptr; | |
| 2000 | bool ret_type_has_bits = return_instruction->value != nullptr && | |
| 2001 | type_has_bits(return_instruction->value->value.type); | |
| 2002 | ZigType *ret_type = ret_type_has_bits ? return_instruction->value->value.type : nullptr; | |
| 2003 | ||
| 1994 | 2004 | if (ir_want_runtime_safety(g, &return_instruction->base)) { |
| 1995 | LLVMValueRef locals_ptr = g->cur_ret_ptr; | |
| 1996 | 2005 | LLVMValueRef resume_index_ptr = LLVMBuildStructGEP(g->builder, locals_ptr, coro_fn_ptr_index, ""); |
| 1997 | 2006 | LLVMValueRef new_resume_fn = g->cur_fn->resume_blocks.last()->split_llvm_fn; |
| 1998 | 2007 | LLVMBuildStore(g->builder, new_resume_fn, resume_index_ptr); |
| 1999 | 2008 | } |
| 2000 | 2009 | |
| 2010 | LLVMValueRef awaiter_ptr = LLVMBuildStructGEP(g->builder, locals_ptr, coro_awaiter_index, ""); | |
| 2011 | LLVMValueRef result_ptr_as_usize; | |
| 2012 | if (ret_type_has_bits) { | |
| 2013 | LLVMValueRef result_ptr_ptr = LLVMBuildStructGEP(g->builder, locals_ptr, coro_arg_start, ""); | |
| 2014 | LLVMValueRef result_ptr = LLVMBuildLoad(g->builder, result_ptr_ptr, ""); | |
| 2015 | if (!handle_is_ptr(ret_type)) { | |
| 2016 | // It's a scalar, so it didn't get written to the result ptr. Do that now. | |
| 2017 | LLVMBuildStore(g->builder, ir_llvm_value(g, return_instruction->value), result_ptr); | |
| 2018 | } | |
| 2019 | result_ptr_as_usize = LLVMBuildPtrToInt(g->builder, result_ptr, usize_type_ref, ""); | |
| 2020 | } else { | |
| 2021 | result_ptr_as_usize = LLVMGetUndef(usize_type_ref); | |
| 2022 | } | |
| 2023 | LLVMValueRef zero = LLVMConstNull(usize_type_ref); | |
| 2024 | LLVMValueRef all_ones = LLVMConstAllOnes(usize_type_ref); | |
| 2025 | LLVMValueRef prev_val = LLVMBuildAtomicRMW(g->builder, LLVMAtomicRMWBinOpXchg, awaiter_ptr, | |
| 2026 | all_ones, LLVMAtomicOrderingSequentiallyConsistent, g->is_single_threaded); | |
| 2027 | ||
| 2028 | LLVMBasicBlockRef bad_return_block = LLVMAppendBasicBlock(g->cur_fn_val, "BadReturn"); | |
| 2029 | LLVMBasicBlockRef early_return_block = LLVMAppendBasicBlock(g->cur_fn_val, "EarlyReturn"); | |
| 2030 | LLVMBasicBlockRef resume_them_block = LLVMAppendBasicBlock(g->cur_fn_val, "ResumeThem"); | |
| 2031 | ||
| 2032 | LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, prev_val, resume_them_block, 2); | |
| 2033 | ||
| 2034 | LLVMAddCase(switch_instr, zero, early_return_block); | |
| 2035 | LLVMAddCase(switch_instr, all_ones, bad_return_block); | |
| 2036 | ||
| 2037 | // Something has gone horribly wrong, and this is an invalid second return. | |
| 2038 | LLVMPositionBuilderAtEnd(g->builder, bad_return_block); | |
| 2039 | gen_assertion(g, PanicMsgIdBadReturn, &return_instruction->base); | |
| 2040 | ||
| 2041 | // The caller will deal with fetching the result - we're done. | |
| 2042 | LLVMPositionBuilderAtEnd(g->builder, early_return_block); | |
| 2001 | 2043 | LLVMBuildRetVoid(g->builder); |
| 2044 | ||
| 2045 | // We need to resume the caller by tail calling them. | |
| 2046 | LLVMPositionBuilderAtEnd(g->builder, resume_them_block); | |
| 2047 | ZigType *any_frame_type = get_any_frame_type(g, ret_type); | |
| 2048 | LLVMValueRef their_frame_ptr = LLVMBuildIntToPtr(g->builder, prev_val, | |
| 2049 | get_llvm_type(g, any_frame_type), ""); | |
| 2050 | LLVMValueRef fn_ptr_ptr = LLVMBuildStructGEP(g->builder, their_frame_ptr, coro_fn_ptr_index, ""); | |
| 2051 | LLVMValueRef awaiter_fn = LLVMBuildLoad(g->builder, fn_ptr_ptr, ""); | |
| 2052 | LLVMValueRef args[] = {their_frame_ptr, result_ptr_as_usize}; | |
| 2053 | LLVMValueRef call_inst = ZigLLVMBuildCall(g->builder, awaiter_fn, args, 2, LLVMFastCallConv, | |
| 2054 | ZigLLVM_FnInlineAuto, ""); | |
| 2055 | ZigLLVMSetTailCall(call_inst); | |
| 2056 | LLVMBuildRetVoid(g->builder); | |
| 2057 | ||
| 2002 | 2058 | return nullptr; |
| 2003 | 2059 | } |
| 2004 | 2060 | if (want_first_arg_sret(g, &g->cur_fn->type_entry->data.fn.fn_type_id)) { |
| ... | ... | @@ -3514,14 +3570,17 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr |
| 3514 | 3570 | } |
| 3515 | 3571 | } |
| 3516 | 3572 | if (instruction->is_async) { |
| 3517 | ZigLLVMBuildCall(g->builder, fn_val, &frame_result_loc, 1, llvm_cc, fn_inline, ""); | |
| 3573 | LLVMValueRef args[] = {frame_result_loc, LLVMGetUndef(g->builtin_types.entry_usize->llvm_type)}; | |
| 3574 | ZigLLVMBuildCall(g->builder, fn_val, args, 2, llvm_cc, fn_inline, ""); | |
| 3518 | 3575 | return nullptr; |
| 3519 | 3576 | } else if (callee_is_async) { |
| 3577 | ZigType *ptr_result_type = get_pointer_to_type(g, src_return_type, true); | |
| 3520 | 3578 | LLVMValueRef split_llvm_fn = make_fn_llvm_value(g, g->cur_fn); |
| 3521 | 3579 | LLVMValueRef fn_ptr_ptr = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr, coro_fn_ptr_index, ""); |
| 3522 | 3580 | LLVMBuildStore(g->builder, split_llvm_fn, fn_ptr_ptr); |
| 3523 | 3581 | |
| 3524 | LLVMValueRef call_inst = ZigLLVMBuildCall(g->builder, fn_val, &frame_result_loc, 1, llvm_cc, fn_inline, ""); | |
| 3582 | LLVMValueRef args[] = {frame_result_loc, LLVMGetUndef(g->builtin_types.entry_usize->llvm_type)}; | |
| 3583 | LLVMValueRef call_inst = ZigLLVMBuildCall(g->builder, fn_val, args, 2, llvm_cc, fn_inline, ""); | |
| 3525 | 3584 | ZigLLVMSetTailCall(call_inst); |
| 3526 | 3585 | LLVMBuildRetVoid(g->builder); |
| 3527 | 3586 | |
| ... | ... | @@ -3530,7 +3589,15 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr |
| 3530 | 3589 | LLVMBasicBlockRef call_bb = LLVMAppendBasicBlock(split_llvm_fn, "CallResume"); |
| 3531 | 3590 | LLVMPositionBuilderAtEnd(g->builder, call_bb); |
| 3532 | 3591 | render_async_var_decls(g, instruction->base.scope); |
| 3533 | return nullptr; | |
| 3592 | ||
| 3593 | if (type_has_bits(src_return_type)) { | |
| 3594 | LLVMValueRef spilled_result_ptr = LLVMGetParam(g->cur_fn_val, 1); | |
| 3595 | LLVMValueRef casted_spilled_result_ptr = LLVMBuildIntToPtr(g->builder, spilled_result_ptr, | |
| 3596 | get_llvm_type(g, ptr_result_type), ""); | |
| 3597 | return get_handle_value(g, casted_spilled_result_ptr, src_return_type, ptr_result_type); | |
| 3598 | } else { | |
| 3599 | return nullptr; | |
| 3600 | } | |
| 3534 | 3601 | } |
| 3535 | 3602 | |
| 3536 | 3603 | if (instruction->new_stack == nullptr) { |
| ... | ... | @@ -4829,7 +4896,7 @@ static LLVMValueRef ir_render_atomic_rmw(CodeGen *g, IrExecutable *executable, |
| 4829 | 4896 | LLVMValueRef operand = ir_llvm_value(g, instruction->operand); |
| 4830 | 4897 | |
| 4831 | 4898 | if (get_codegen_ptr_type(operand_type) == nullptr) { |
| 4832 | return LLVMBuildAtomicRMW(g->builder, op, ptr, operand, ordering, false); | |
| 4899 | return LLVMBuildAtomicRMW(g->builder, op, ptr, operand, ordering, g->is_single_threaded); | |
| 4833 | 4900 | } |
| 4834 | 4901 | |
| 4835 | 4902 | // it's a pointer but we need to treat it as an int |
| ... | ... | @@ -4990,14 +5057,89 @@ static LLVMValueRef ir_render_suspend_br(CodeGen *g, IrExecutable *executable, |
| 4990 | 5057 | return nullptr; |
| 4991 | 5058 | } |
| 4992 | 5059 | |
| 5060 | static LLVMValueRef ir_render_await(CodeGen *g, IrExecutable *executable, IrInstructionAwait *instruction) { | |
| 5061 | LLVMValueRef target_frame_ptr = ir_llvm_value(g, instruction->frame); | |
| 5062 | ZigType *result_type = instruction->base.value.type; | |
| 5063 | ZigType *ptr_result_type = get_pointer_to_type(g, result_type, true); | |
| 5064 | ||
| 5065 | // Prepare to be suspended | |
| 5066 | LLVMValueRef split_llvm_fn = make_fn_llvm_value(g, g->cur_fn); | |
| 5067 | LLVMValueRef fn_ptr_ptr = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr, coro_fn_ptr_index, ""); | |
| 5068 | LLVMBuildStore(g->builder, split_llvm_fn, fn_ptr_ptr); | |
| 5069 | ||
| 5070 | // At this point resuming the function will do the correct thing. | |
| 5071 | // This code is as if it is running inside the suspend block. | |
| 5072 | ||
| 5073 | LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; | |
| 5074 | // caller's own frame pointer | |
| 5075 | LLVMValueRef awaiter_init_val = LLVMBuildPtrToInt(g->builder, g->cur_ret_ptr, usize_type_ref, ""); | |
| 5076 | LLVMValueRef awaiter_ptr = LLVMBuildStructGEP(g->builder, target_frame_ptr, coro_awaiter_index, ""); | |
| 5077 | LLVMValueRef result_ptr_as_usize; | |
| 5078 | if (type_has_bits(result_type)) { | |
| 5079 | LLVMValueRef result_ptr_ptr = LLVMBuildStructGEP(g->builder, target_frame_ptr, coro_arg_start, ""); | |
| 5080 | LLVMValueRef result_ptr = LLVMBuildLoad(g->builder, result_ptr_ptr, ""); | |
| 5081 | result_ptr_as_usize = LLVMBuildPtrToInt(g->builder, result_ptr, usize_type_ref, ""); | |
| 5082 | } else { | |
| 5083 | result_ptr_as_usize = LLVMGetUndef(usize_type_ref); | |
| 5084 | } | |
| 5085 | LLVMValueRef prev_val = LLVMBuildAtomicRMW(g->builder, LLVMAtomicRMWBinOpXchg, awaiter_ptr, awaiter_init_val, | |
| 5086 | LLVMAtomicOrderingSequentiallyConsistent, g->is_single_threaded); | |
| 5087 | ||
| 5088 | LLVMBasicBlockRef bad_await_block = LLVMAppendBasicBlock(g->cur_fn_val, "BadAwait"); | |
| 5089 | LLVMBasicBlockRef complete_suspend_block = LLVMAppendBasicBlock(g->cur_fn_val, "CompleteSuspend"); | |
| 5090 | LLVMBasicBlockRef early_return_block = LLVMAppendBasicBlock(g->cur_fn_val, "EarlyReturn"); | |
| 5091 | ||
| 5092 | LLVMValueRef zero = LLVMConstNull(usize_type_ref); | |
| 5093 | LLVMValueRef all_ones = LLVMConstAllOnes(usize_type_ref); | |
| 5094 | LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, prev_val, bad_await_block, 2); | |
| 5095 | ||
| 5096 | LLVMAddCase(switch_instr, zero, complete_suspend_block); | |
| 5097 | LLVMAddCase(switch_instr, all_ones, early_return_block); | |
| 5098 | ||
| 5099 | // We discovered that another awaiter was already here. | |
| 5100 | LLVMPositionBuilderAtEnd(g->builder, bad_await_block); | |
| 5101 | gen_assertion(g, PanicMsgIdBadAwait, &instruction->base); | |
| 5102 | ||
| 5103 | // Rely on the target to resume us from suspension. | |
| 5104 | LLVMPositionBuilderAtEnd(g->builder, complete_suspend_block); | |
| 5105 | LLVMBuildRetVoid(g->builder); | |
| 5106 | ||
| 5107 | // The async function has already completed. So we use a tail call to resume ourselves. | |
| 5108 | LLVMPositionBuilderAtEnd(g->builder, early_return_block); | |
| 5109 | LLVMValueRef args[] = {g->cur_ret_ptr, result_ptr_as_usize}; | |
| 5110 | LLVMValueRef call_inst = ZigLLVMBuildCall(g->builder, split_llvm_fn, args, 2, LLVMFastCallConv, | |
| 5111 | ZigLLVM_FnInlineAuto, ""); | |
| 5112 | ZigLLVMSetTailCall(call_inst); | |
| 5113 | LLVMBuildRetVoid(g->builder); | |
| 5114 | ||
| 5115 | g->cur_fn_val = split_llvm_fn; | |
| 5116 | g->cur_ret_ptr = LLVMGetParam(split_llvm_fn, 0); | |
| 5117 | LLVMBasicBlockRef call_bb = LLVMAppendBasicBlock(split_llvm_fn, "AwaitResume"); | |
| 5118 | LLVMPositionBuilderAtEnd(g->builder, call_bb); | |
| 5119 | render_async_var_decls(g, instruction->base.scope); | |
| 5120 | ||
| 5121 | if (type_has_bits(result_type)) { | |
| 5122 | LLVMValueRef spilled_result_ptr = LLVMGetParam(g->cur_fn_val, 1); | |
| 5123 | LLVMValueRef casted_spilled_result_ptr = LLVMBuildIntToPtr(g->builder, spilled_result_ptr, | |
| 5124 | get_llvm_type(g, ptr_result_type), ""); | |
| 5125 | return get_handle_value(g, casted_spilled_result_ptr, result_type, ptr_result_type); | |
| 5126 | } else { | |
| 5127 | return nullptr; | |
| 5128 | } | |
| 5129 | } | |
| 5130 | ||
| 4993 | 5131 | static LLVMTypeRef anyframe_fn_type(CodeGen *g) { |
| 4994 | 5132 | if (g->anyframe_fn_type != nullptr) |
| 4995 | 5133 | return g->anyframe_fn_type; |
| 4996 | 5134 | |
| 5135 | LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; | |
| 4997 | 5136 | ZigType *anyframe_type = get_any_frame_type(g, nullptr); |
| 4998 | LLVMTypeRef param_type = get_llvm_type(g, anyframe_type); | |
| 4999 | 5137 | LLVMTypeRef return_type = LLVMVoidType(); |
| 5000 | LLVMTypeRef fn_type = LLVMFunctionType(return_type, &param_type, 1, false); | |
| 5138 | LLVMTypeRef param_types[] = { | |
| 5139 | get_llvm_type(g, anyframe_type), | |
| 5140 | usize_type_ref, | |
| 5141 | }; | |
| 5142 | LLVMTypeRef fn_type = LLVMFunctionType(return_type, param_types, 2, false); | |
| 5001 | 5143 | g->anyframe_fn_type = LLVMPointerType(fn_type, 0); |
| 5002 | 5144 | |
| 5003 | 5145 | return g->anyframe_fn_type; |
| ... | ... | @@ -5006,13 +5148,15 @@ static LLVMTypeRef anyframe_fn_type(CodeGen *g) { |
| 5006 | 5148 | static LLVMValueRef ir_render_coro_resume(CodeGen *g, IrExecutable *executable, |
| 5007 | 5149 | IrInstructionCoroResume *instruction) |
| 5008 | 5150 | { |
| 5151 | LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; | |
| 5009 | 5152 | LLVMValueRef frame = ir_llvm_value(g, instruction->frame); |
| 5010 | 5153 | ZigType *frame_type = instruction->frame->value.type; |
| 5011 | 5154 | assert(frame_type->id == ZigTypeIdAnyFrame); |
| 5012 | 5155 | LLVMValueRef fn_ptr_ptr = LLVMBuildStructGEP(g->builder, frame, coro_fn_ptr_index, ""); |
| 5013 | 5156 | LLVMValueRef uncasted_fn_val = LLVMBuildLoad(g->builder, fn_ptr_ptr, ""); |
| 5014 | 5157 | LLVMValueRef fn_val = LLVMBuildIntToPtr(g->builder, uncasted_fn_val, anyframe_fn_type(g), ""); |
| 5015 | ZigLLVMBuildCall(g->builder, fn_val, &frame, 1, LLVMFastCallConv, ZigLLVM_FnInlineAuto, ""); | |
| 5158 | LLVMValueRef args[] = {frame, LLVMGetUndef(usize_type_ref)}; | |
| 5159 | ZigLLVMBuildCall(g->builder, fn_val, args, 2, LLVMFastCallConv, ZigLLVM_FnInlineAuto, ""); | |
| 5016 | 5160 | return nullptr; |
| 5017 | 5161 | } |
| 5018 | 5162 | |
| ... | ... | @@ -5279,6 +5423,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, |
| 5279 | 5423 | return ir_render_coro_resume(g, executable, (IrInstructionCoroResume *)instruction); |
| 5280 | 5424 | case IrInstructionIdFrameSizeGen: |
| 5281 | 5425 | return ir_render_frame_size(g, executable, (IrInstructionFrameSizeGen *)instruction); |
| 5426 | case IrInstructionIdAwait: | |
| 5427 | return ir_render_await(g, executable, (IrInstructionAwait *)instruction); | |
| 5282 | 5428 | } |
| 5283 | 5429 | zig_unreachable(); |
| 5284 | 5430 | } |
src/ir.cpp+82-15| ... | ... | @@ -1052,6 +1052,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionSuspendBr *) { |
| 1052 | 1052 | return IrInstructionIdSuspendBr; |
| 1053 | 1053 | } |
| 1054 | 1054 | |
| 1055 | static constexpr IrInstructionId ir_instruction_id(IrInstructionAwait *) { | |
| 1056 | return IrInstructionIdAwait; | |
| 1057 | } | |
| 1058 | ||
| 1055 | 1059 | static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroResume *) { |
| 1056 | 1060 | return IrInstructionIdCoroResume; |
| 1057 | 1061 | } |
| ... | ... | @@ -3274,6 +3278,17 @@ static IrInstruction *ir_build_suspend_br(IrBuilder *irb, Scope *scope, AstNode |
| 3274 | 3278 | return &instruction->base; |
| 3275 | 3279 | } |
| 3276 | 3280 | |
| 3281 | static IrInstruction *ir_build_await(IrBuilder *irb, Scope *scope, AstNode *source_node, | |
| 3282 | IrInstruction *frame) | |
| 3283 | { | |
| 3284 | IrInstructionAwait *instruction = ir_build_instruction<IrInstructionAwait>(irb, scope, source_node); | |
| 3285 | instruction->frame = frame; | |
| 3286 | ||
| 3287 | ir_ref_instruction(frame, irb->current_basic_block); | |
| 3288 | ||
| 3289 | return &instruction->base; | |
| 3290 | } | |
| 3291 | ||
| 3277 | 3292 | static IrInstruction *ir_build_coro_resume(IrBuilder *irb, Scope *scope, AstNode *source_node, |
| 3278 | 3293 | IrInstruction *frame) |
| 3279 | 3294 | { |
| ... | ... | @@ -7774,11 +7789,26 @@ static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *scope, AstNode *node) |
| 7774 | 7789 | static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *node) { |
| 7775 | 7790 | assert(node->type == NodeTypeAwaitExpr); |
| 7776 | 7791 | |
| 7777 | IrInstruction *target_inst = ir_gen_node(irb, node->data.await_expr.expr, scope); | |
| 7792 | ZigFn *fn_entry = exec_fn_entry(irb->exec); | |
| 7793 | if (!fn_entry) { | |
| 7794 | add_node_error(irb->codegen, node, buf_sprintf("await outside function definition")); | |
| 7795 | return irb->codegen->invalid_instruction; | |
| 7796 | } | |
| 7797 | ScopeSuspend *existing_suspend_scope = get_scope_suspend(scope); | |
| 7798 | if (existing_suspend_scope) { | |
| 7799 | if (!existing_suspend_scope->reported_err) { | |
| 7800 | ErrorMsg *msg = add_node_error(irb->codegen, node, buf_sprintf("cannot await inside suspend block")); | |
| 7801 | add_error_note(irb->codegen, msg, existing_suspend_scope->base.source_node, buf_sprintf("suspend block here")); | |
| 7802 | existing_suspend_scope->reported_err = true; | |
| 7803 | } | |
| 7804 | return irb->codegen->invalid_instruction; | |
| 7805 | } | |
| 7806 | ||
| 7807 | IrInstruction *target_inst = ir_gen_node_extra(irb, node->data.await_expr.expr, scope, LValPtr, nullptr); | |
| 7778 | 7808 | if (target_inst == irb->codegen->invalid_instruction) |
| 7779 | 7809 | return irb->codegen->invalid_instruction; |
| 7780 | 7810 | |
| 7781 | zig_panic("TODO ir_gen_await_expr"); | |
| 7811 | return ir_build_await(irb, scope, node, target_inst); | |
| 7782 | 7812 | } |
| 7783 | 7813 | |
| 7784 | 7814 | static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNode *node) { |
| ... | ... | @@ -7789,15 +7819,6 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod |
| 7789 | 7819 | add_node_error(irb->codegen, node, buf_sprintf("suspend outside function definition")); |
| 7790 | 7820 | return irb->codegen->invalid_instruction; |
| 7791 | 7821 | } |
| 7792 | ScopeDeferExpr *scope_defer_expr = get_scope_defer_expr(parent_scope); | |
| 7793 | if (scope_defer_expr) { | |
| 7794 | if (!scope_defer_expr->reported_err) { | |
| 7795 | ErrorMsg *msg = add_node_error(irb->codegen, node, buf_sprintf("cannot suspend inside defer expression")); | |
| 7796 | add_error_note(irb->codegen, msg, scope_defer_expr->base.source_node, buf_sprintf("defer here")); | |
| 7797 | scope_defer_expr->reported_err = true; | |
| 7798 | } | |
| 7799 | return irb->codegen->invalid_instruction; | |
| 7800 | } | |
| 7801 | 7822 | ScopeSuspend *existing_suspend_scope = get_scope_suspend(parent_scope); |
| 7802 | 7823 | if (existing_suspend_scope) { |
| 7803 | 7824 | if (!existing_suspend_scope->reported_err) { |
| ... | ... | @@ -7808,7 +7829,7 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod |
| 7808 | 7829 | return irb->codegen->invalid_instruction; |
| 7809 | 7830 | } |
| 7810 | 7831 | |
| 7811 | IrBasicBlock *resume_block = ir_create_basic_block(irb, parent_scope, "Resume"); | |
| 7832 | IrBasicBlock *resume_block = ir_create_basic_block(irb, parent_scope, "SuspendResume"); | |
| 7812 | 7833 | |
| 7813 | 7834 | ir_build_suspend_begin(irb, parent_scope, node, resume_block); |
| 7814 | 7835 | if (node->data.suspend.block != nullptr) { |
| ... | ... | @@ -24372,19 +24393,62 @@ static IrInstruction *ir_analyze_instruction_suspend_br(IrAnalyze *ira, IrInstru |
| 24372 | 24393 | return ir_finish_anal(ira, result); |
| 24373 | 24394 | } |
| 24374 | 24395 | |
| 24375 | static IrInstruction *ir_analyze_instruction_coro_resume(IrAnalyze *ira, IrInstructionCoroResume *instruction) { | |
| 24396 | static IrInstruction *ir_analyze_instruction_await(IrAnalyze *ira, IrInstructionAwait *instruction) { | |
| 24376 | 24397 | IrInstruction *frame_ptr = instruction->frame->child; |
| 24377 | 24398 | if (type_is_invalid(frame_ptr->value.type)) |
| 24378 | 24399 | return ira->codegen->invalid_instruction; |
| 24379 | 24400 | |
| 24401 | ZigType *result_type; | |
| 24380 | 24402 | IrInstruction *frame; |
| 24381 | 24403 | if (frame_ptr->value.type->id == ZigTypeIdPointer && |
| 24382 | 24404 | frame_ptr->value.type->data.pointer.ptr_len == PtrLenSingle && |
| 24383 | frame_ptr->value.type->data.pointer.child_type->id == ZigTypeIdAnyFrame) | |
| 24405 | frame_ptr->value.type->data.pointer.child_type->id == ZigTypeIdCoroFrame) | |
| 24384 | 24406 | { |
| 24385 | frame = ir_get_deref(ira, &instruction->base, frame_ptr, nullptr); | |
| 24407 | result_type = frame_ptr->value.type->data.pointer.child_type->data.frame.fn->type_entry->data.fn.fn_type_id.return_type; | |
| 24408 | frame = frame_ptr; | |
| 24386 | 24409 | } else { |
| 24410 | frame = ir_get_deref(ira, &instruction->base, frame_ptr, nullptr); | |
| 24411 | if (frame->value.type->id != ZigTypeIdAnyFrame || | |
| 24412 | frame->value.type->data.any_frame.result_type == nullptr) | |
| 24413 | { | |
| 24414 | ir_add_error(ira, &instruction->base, | |
| 24415 | buf_sprintf("expected anyframe->T, found '%s'", buf_ptr(&frame->value.type->name))); | |
| 24416 | return ira->codegen->invalid_instruction; | |
| 24417 | } | |
| 24418 | result_type = frame->value.type->data.any_frame.result_type; | |
| 24419 | } | |
| 24420 | ||
| 24421 | ZigType *any_frame_type = get_any_frame_type(ira->codegen, result_type); | |
| 24422 | IrInstruction *casted_frame = ir_implicit_cast(ira, frame, any_frame_type); | |
| 24423 | if (type_is_invalid(casted_frame->value.type)) | |
| 24424 | return ira->codegen->invalid_instruction; | |
| 24425 | ||
| 24426 | ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec); | |
| 24427 | ir_assert(fn_entry != nullptr, &instruction->base); | |
| 24428 | ||
| 24429 | if (fn_entry->inferred_async_node == nullptr) { | |
| 24430 | fn_entry->inferred_async_node = instruction->base.source_node; | |
| 24431 | } | |
| 24432 | ||
| 24433 | IrInstruction *result = ir_build_await(&ira->new_irb, | |
| 24434 | instruction->base.scope, instruction->base.source_node, frame); | |
| 24435 | result->value.type = result_type; | |
| 24436 | return ir_finish_anal(ira, result); | |
| 24437 | } | |
| 24438 | ||
| 24439 | static IrInstruction *ir_analyze_instruction_coro_resume(IrAnalyze *ira, IrInstructionCoroResume *instruction) { | |
| 24440 | IrInstruction *frame_ptr = instruction->frame->child; | |
| 24441 | if (type_is_invalid(frame_ptr->value.type)) | |
| 24442 | return ira->codegen->invalid_instruction; | |
| 24443 | ||
| 24444 | IrInstruction *frame; | |
| 24445 | if (frame_ptr->value.type->id == ZigTypeIdPointer && | |
| 24446 | frame_ptr->value.type->data.pointer.ptr_len == PtrLenSingle && | |
| 24447 | frame_ptr->value.type->data.pointer.child_type->id == ZigTypeIdCoroFrame) | |
| 24448 | { | |
| 24387 | 24449 | frame = frame_ptr; |
| 24450 | } else { | |
| 24451 | frame = ir_get_deref(ira, &instruction->base, frame_ptr, nullptr); | |
| 24388 | 24452 | } |
| 24389 | 24453 | |
| 24390 | 24454 | ZigType *any_frame_type = get_any_frame_type(ira->codegen, nullptr); |
| ... | ... | @@ -24691,6 +24755,8 @@ static IrInstruction *ir_analyze_instruction_base(IrAnalyze *ira, IrInstruction |
| 24691 | 24755 | return ir_analyze_instruction_suspend_br(ira, (IrInstructionSuspendBr *)instruction); |
| 24692 | 24756 | case IrInstructionIdCoroResume: |
| 24693 | 24757 | return ir_analyze_instruction_coro_resume(ira, (IrInstructionCoroResume *)instruction); |
| 24758 | case IrInstructionIdAwait: | |
| 24759 | return ir_analyze_instruction_await(ira, (IrInstructionAwait *)instruction); | |
| 24694 | 24760 | } |
| 24695 | 24761 | zig_unreachable(); |
| 24696 | 24762 | } |
| ... | ... | @@ -24826,6 +24892,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { |
| 24826 | 24892 | case IrInstructionIdSuspendBegin: |
| 24827 | 24893 | case IrInstructionIdSuspendBr: |
| 24828 | 24894 | case IrInstructionIdCoroResume: |
| 24895 | case IrInstructionIdAwait: | |
| 24829 | 24896 | return true; |
| 24830 | 24897 | |
| 24831 | 24898 | case IrInstructionIdPhi: |
src/ir_print.cpp+9| ... | ... | @@ -1546,6 +1546,12 @@ static void ir_print_coro_resume(IrPrint *irp, IrInstructionCoroResume *instruct |
| 1546 | 1546 | fprintf(irp->f, ")"); |
| 1547 | 1547 | } |
| 1548 | 1548 | |
| 1549 | static void ir_print_await(IrPrint *irp, IrInstructionAwait *instruction) { | |
| 1550 | fprintf(irp->f, "@await("); | |
| 1551 | ir_print_other_instruction(irp, instruction->frame); | |
| 1552 | fprintf(irp->f, ")"); | |
| 1553 | } | |
| 1554 | ||
| 1549 | 1555 | static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { |
| 1550 | 1556 | ir_print_prefix(irp, instruction); |
| 1551 | 1557 | switch (instruction->id) { |
| ... | ... | @@ -2025,6 +2031,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { |
| 2025 | 2031 | case IrInstructionIdCoroResume: |
| 2026 | 2032 | ir_print_coro_resume(irp, (IrInstructionCoroResume *)instruction); |
| 2027 | 2033 | break; |
| 2034 | case IrInstructionIdAwait: | |
| 2035 | ir_print_await(irp, (IrInstructionAwait *)instruction); | |
| 2036 | break; | |
| 2028 | 2037 | } |
| 2029 | 2038 | fprintf(irp->f, "\n"); |
| 2030 | 2039 | } |
test/stage1/behavior/coroutine_await_struct.zig+4-4| ... | ... | @@ -6,12 +6,12 @@ const Foo = struct { |
| 6 | 6 | x: i32, |
| 7 | 7 | }; |
| 8 | 8 | |
| 9 | var await_a_promise: promise = undefined; | |
| 9 | var await_a_promise: anyframe = undefined; | |
| 10 | 10 | var await_final_result = Foo{ .x = 0 }; |
| 11 | 11 | |
| 12 | 12 | test "coroutine await struct" { |
| 13 | 13 | await_seq('a'); |
| 14 | const p = async<std.heap.direct_allocator> await_amain() catch unreachable; | |
| 14 | const p = async await_amain(); | |
| 15 | 15 | await_seq('f'); |
| 16 | 16 | resume await_a_promise; |
| 17 | 17 | await_seq('i'); |
| ... | ... | @@ -20,7 +20,7 @@ test "coroutine await struct" { |
| 20 | 20 | } |
| 21 | 21 | async fn await_amain() void { |
| 22 | 22 | await_seq('b'); |
| 23 | const p = async await_another() catch unreachable; | |
| 23 | const p = async await_another(); | |
| 24 | 24 | await_seq('e'); |
| 25 | 25 | await_final_result = await p; |
| 26 | 26 | await_seq('h'); |
| ... | ... | @@ -29,7 +29,7 @@ async fn await_another() Foo { |
| 29 | 29 | await_seq('c'); |
| 30 | 30 | suspend { |
| 31 | 31 | await_seq('d'); |
| 32 | await_a_promise = @handle(); | |
| 32 | await_a_promise = @frame(); | |
| 33 | 33 | } |
| 34 | 34 | await_seq('g'); |
| 35 | 35 | return Foo{ .x = 1234 }; |
test/stage1/behavior/coroutines.zig+97-112| ... | ... | @@ -180,97 +180,85 @@ async fn testSuspendBlock() void { |
| 180 | 180 | result = true; |
| 181 | 181 | } |
| 182 | 182 | |
| 183 | //var await_a_promise: anyframe = undefined; | |
| 184 | //var await_final_result: i32 = 0; | |
| 185 | // | |
| 186 | //test "coroutine await" { | |
| 187 | // await_seq('a'); | |
| 188 | // const p = async<allocator> await_amain() catch unreachable; | |
| 189 | // await_seq('f'); | |
| 190 | // resume await_a_promise; | |
| 191 | // await_seq('i'); | |
| 192 | // expect(await_final_result == 1234); | |
| 193 | // expect(std.mem.eql(u8, await_points, "abcdefghi")); | |
| 194 | //} | |
| 195 | //async fn await_amain() void { | |
| 196 | // await_seq('b'); | |
| 197 | // const p = async await_another() catch unreachable; | |
| 198 | // await_seq('e'); | |
| 199 | // await_final_result = await p; | |
| 200 | // await_seq('h'); | |
| 201 | //} | |
| 202 | //async fn await_another() i32 { | |
| 203 | // await_seq('c'); | |
| 204 | // suspend { | |
| 205 | // await_seq('d'); | |
| 206 | // await_a_promise = @frame(); | |
| 207 | // } | |
| 208 | // await_seq('g'); | |
| 209 | // return 1234; | |
| 210 | //} | |
| 211 | // | |
| 212 | //var await_points = [_]u8{0} ** "abcdefghi".len; | |
| 213 | //var await_seq_index: usize = 0; | |
| 214 | // | |
| 215 | //fn await_seq(c: u8) void { | |
| 216 | // await_points[await_seq_index] = c; | |
| 217 | // await_seq_index += 1; | |
| 218 | //} | |
| 219 | // | |
| 220 | //var early_final_result: i32 = 0; | |
| 221 | // | |
| 222 | //test "coroutine await early return" { | |
| 223 | // early_seq('a'); | |
| 224 | // const p = async<allocator> early_amain() catch @panic("out of memory"); | |
| 225 | // early_seq('f'); | |
| 226 | // expect(early_final_result == 1234); | |
| 227 | // expect(std.mem.eql(u8, early_points, "abcdef")); | |
| 228 | //} | |
| 229 | //async fn early_amain() void { | |
| 230 | // early_seq('b'); | |
| 231 | // const p = async early_another() catch @panic("out of memory"); | |
| 232 | // early_seq('d'); | |
| 233 | // early_final_result = await p; | |
| 234 | // early_seq('e'); | |
| 235 | //} | |
| 236 | //async fn early_another() i32 { | |
| 237 | // early_seq('c'); | |
| 238 | // return 1234; | |
| 239 | //} | |
| 240 | // | |
| 241 | //var early_points = [_]u8{0} ** "abcdef".len; | |
| 242 | //var early_seq_index: usize = 0; | |
| 243 | // | |
| 244 | //fn early_seq(c: u8) void { | |
| 245 | // early_points[early_seq_index] = c; | |
| 246 | // early_seq_index += 1; | |
| 247 | //} | |
| 248 | // | |
| 249 | //test "coro allocation failure" { | |
| 250 | // var failing_allocator = std.debug.FailingAllocator.init(std.debug.global_allocator, 0); | |
| 251 | // if (async<&failing_allocator.allocator> asyncFuncThatNeverGetsRun()) { | |
| 252 | // @panic("expected allocation failure"); | |
| 253 | // } else |err| switch (err) { | |
| 254 | // error.OutOfMemory => {}, | |
| 255 | // } | |
| 256 | //} | |
| 257 | //async fn asyncFuncThatNeverGetsRun() void { | |
| 258 | // @panic("coro frame allocation should fail"); | |
| 259 | //} | |
| 260 | // | |
| 261 | //test "async function with dot syntax" { | |
| 262 | // const S = struct { | |
| 263 | // var y: i32 = 1; | |
| 264 | // async fn foo() void { | |
| 265 | // y += 1; | |
| 266 | // suspend; | |
| 267 | // } | |
| 268 | // }; | |
| 269 | // const p = try async<allocator> S.foo(); | |
| 270 | // cancel p; | |
| 271 | // expect(S.y == 2); | |
| 272 | //} | |
| 273 | // | |
| 183 | var await_a_promise: anyframe = undefined; | |
| 184 | var await_final_result: i32 = 0; | |
| 185 | ||
| 186 | test "coroutine await" { | |
| 187 | await_seq('a'); | |
| 188 | const p = async await_amain(); | |
| 189 | await_seq('f'); | |
| 190 | resume await_a_promise; | |
| 191 | await_seq('i'); | |
| 192 | expect(await_final_result == 1234); | |
| 193 | expect(std.mem.eql(u8, await_points, "abcdefghi")); | |
| 194 | } | |
| 195 | async fn await_amain() void { | |
| 196 | await_seq('b'); | |
| 197 | const p = async await_another(); | |
| 198 | await_seq('e'); | |
| 199 | await_final_result = await p; | |
| 200 | await_seq('h'); | |
| 201 | } | |
| 202 | async fn await_another() i32 { | |
| 203 | await_seq('c'); | |
| 204 | suspend { | |
| 205 | await_seq('d'); | |
| 206 | await_a_promise = @frame(); | |
| 207 | } | |
| 208 | await_seq('g'); | |
| 209 | return 1234; | |
| 210 | } | |
| 211 | ||
| 212 | var await_points = [_]u8{0} ** "abcdefghi".len; | |
| 213 | var await_seq_index: usize = 0; | |
| 214 | ||
| 215 | fn await_seq(c: u8) void { | |
| 216 | await_points[await_seq_index] = c; | |
| 217 | await_seq_index += 1; | |
| 218 | } | |
| 219 | ||
| 220 | var early_final_result: i32 = 0; | |
| 221 | ||
| 222 | test "coroutine await early return" { | |
| 223 | early_seq('a'); | |
| 224 | const p = async early_amain(); | |
| 225 | early_seq('f'); | |
| 226 | expect(early_final_result == 1234); | |
| 227 | expect(std.mem.eql(u8, early_points, "abcdef")); | |
| 228 | } | |
| 229 | async fn early_amain() void { | |
| 230 | early_seq('b'); | |
| 231 | const p = async early_another(); | |
| 232 | early_seq('d'); | |
| 233 | early_final_result = await p; | |
| 234 | early_seq('e'); | |
| 235 | } | |
| 236 | async fn early_another() i32 { | |
| 237 | early_seq('c'); | |
| 238 | return 1234; | |
| 239 | } | |
| 240 | ||
| 241 | var early_points = [_]u8{0} ** "abcdef".len; | |
| 242 | var early_seq_index: usize = 0; | |
| 243 | ||
| 244 | fn early_seq(c: u8) void { | |
| 245 | early_points[early_seq_index] = c; | |
| 246 | early_seq_index += 1; | |
| 247 | } | |
| 248 | ||
| 249 | test "async function with dot syntax" { | |
| 250 | const S = struct { | |
| 251 | var y: i32 = 1; | |
| 252 | async fn foo() void { | |
| 253 | y += 1; | |
| 254 | suspend; | |
| 255 | } | |
| 256 | }; | |
| 257 | const p = async S.foo(); | |
| 258 | // can't cancel in tests because they are non-async functions | |
| 259 | expect(S.y == 2); | |
| 260 | } | |
| 261 | ||
| 274 | 262 | //test "async fn pointer in a struct field" { |
| 275 | 263 | // var data: i32 = 1; |
| 276 | 264 | // const Foo = struct { |
| ... | ... | @@ -287,18 +275,17 @@ async fn testSuspendBlock() void { |
| 287 | 275 | // y.* += 1; |
| 288 | 276 | // suspend; |
| 289 | 277 | //} |
| 290 | // | |
| 278 | ||
| 291 | 279 | //test "async fn with inferred error set" { |
| 292 | // const p = (async<allocator> failing()) catch unreachable; | |
| 280 | // const p = async failing(); | |
| 293 | 281 | // resume p; |
| 294 | // cancel p; | |
| 295 | 282 | //} |
| 296 | 283 | // |
| 297 | 284 | //async fn failing() !void { |
| 298 | 285 | // suspend; |
| 299 | 286 | // return error.Fail; |
| 300 | 287 | //} |
| 301 | // | |
| 288 | ||
| 302 | 289 | //test "error return trace across suspend points - early return" { |
| 303 | 290 | // const p = nonFailing(); |
| 304 | 291 | // resume p; |
| ... | ... | @@ -331,20 +318,18 @@ async fn testSuspendBlock() void { |
| 331 | 318 | // } |
| 332 | 319 | // }; |
| 333 | 320 | //} |
| 334 | // | |
| 335 | //test "break from suspend" { | |
| 336 | // var buf: [500]u8 = undefined; | |
| 337 | // var a = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator; | |
| 338 | // var my_result: i32 = 1; | |
| 339 | // const p = try async<a> testBreakFromSuspend(&my_result); | |
| 340 | // cancel p; | |
| 341 | // std.testing.expect(my_result == 2); | |
| 342 | //} | |
| 343 | //async fn testBreakFromSuspend(my_result: *i32) void { | |
| 344 | // suspend { | |
| 345 | // resume @frame(); | |
| 346 | // } | |
| 347 | // my_result.* += 1; | |
| 348 | // suspend; | |
| 349 | // my_result.* += 1; | |
| 350 | //} | |
| 321 | ||
| 322 | test "break from suspend" { | |
| 323 | var my_result: i32 = 1; | |
| 324 | const p = async testBreakFromSuspend(&my_result); | |
| 325 | // can't cancel here | |
| 326 | std.testing.expect(my_result == 2); | |
| 327 | } | |
| 328 | async fn testBreakFromSuspend(my_result: *i32) void { | |
| 329 | suspend { | |
| 330 | resume @frame(); | |
| 331 | } | |
| 332 | my_result.* += 1; | |
| 333 | suspend; | |
| 334 | my_result.* += 1; | |
| 335 | } |