| author | |
| committer | |
| log | 87710a1cc2c4d0e7ecc309e430f7d33baadc5f02 |
| tree | 1a5d38b2a65f0e63e463c5beb94d81c7af8a9320 |
| parent | c87920966133d3285b60ccd022282e3f53789e0c |
| signature |
8 files changed, 247 insertions(+), 61 deletions(-)
BRANCH_TODO+14-2| ... | ... | @@ -1,9 +1,8 @@ |
| 1 | * @asyncCall with an async function pointer | |
| 1 | 2 | * compile error for error: expected anyframe->T, found 'anyframe' |
| 2 | 3 | * compile error for error: expected anyframe->T, found 'i32' |
| 3 | 4 | * await of a non async function |
| 4 | * await in single-threaded mode | |
| 5 | 5 | * async call on a non async function |
| 6 | * @asyncCall with an async function pointer | |
| 7 | 6 | * cancel |
| 8 | 7 | * defer and errdefer |
| 9 | 8 | * safety for double await |
| ... | ... | @@ -21,3 +20,16 @@ |
| 21 | 20 | * compile error for copying a frame |
| 22 | 21 | * compile error for resuming a const frame pointer |
| 23 | 22 | * runtime safety enabling/disabling scope has to be coordinated across resume/await/calls/return |
| 23 | * await in single-threaded mode | |
| 24 | * calling a generic function which is async | |
| 25 | * make sure `await @asyncCall` and `await async` are handled correctly. | |
| 26 | * allow @asyncCall with a real @Frame(func) (the point of this is result pointer) | |
| 27 | * documentation | |
| 28 | - @asyncCall | |
| 29 | - @frame | |
| 30 | - @Frame | |
| 31 | - @frameSize | |
| 32 | - coroutines section | |
| 33 | - suspend | |
| 34 | - resume | |
| 35 | - anyframe, anyframe->T |
src/all_types.hpp+3| ... | ... | @@ -1503,6 +1503,7 @@ enum BuiltinFnId { |
| 1503 | 1503 | BuiltinFnIdInlineCall, |
| 1504 | 1504 | BuiltinFnIdNoInlineCall, |
| 1505 | 1505 | BuiltinFnIdNewStackCall, |
| 1506 | BuiltinFnIdAsyncCall, | |
| 1506 | 1507 | BuiltinFnIdTypeId, |
| 1507 | 1508 | BuiltinFnIdShlExact, |
| 1508 | 1509 | BuiltinFnIdShrExact, |
| ... | ... | @@ -1553,6 +1554,7 @@ enum PanicMsgId { |
| 1553 | 1554 | PanicMsgIdBadAwait, |
| 1554 | 1555 | PanicMsgIdBadReturn, |
| 1555 | 1556 | PanicMsgIdResumedAnAwaitingFn, |
| 1557 | PanicMsgIdFrameTooSmall, | |
| 1556 | 1558 | |
| 1557 | 1559 | PanicMsgIdCount, |
| 1558 | 1560 | }; |
| ... | ... | @@ -3699,6 +3701,7 @@ static const size_t maybe_null_index = 1; |
| 3699 | 3701 | static const size_t err_union_err_index = 0; |
| 3700 | 3702 | static const size_t err_union_payload_index = 1; |
| 3701 | 3703 | |
| 3704 | // label (grep this): [coro_frame_struct_layout] | |
| 3702 | 3705 | static const size_t coro_fn_ptr_index = 0; |
| 3703 | 3706 | static const size_t coro_awaiter_index = 1; |
| 3704 | 3707 | static const size_t coro_arg_start = 2; |
src/analyze.cpp+3| ... | ... | @@ -5205,6 +5205,7 @@ static Error resolve_coro_frame(CodeGen *g, ZigType *frame_type) { |
| 5205 | 5205 | call->frame_result_loc = &alloca_gen->base; |
| 5206 | 5206 | } |
| 5207 | 5207 | |
| 5208 | // label (grep this): [coro_frame_struct_layout] | |
| 5208 | 5209 | ZigList<ZigType *> field_types = {}; |
| 5209 | 5210 | ZigList<const char *> field_names = {}; |
| 5210 | 5211 | |
| ... | ... | @@ -7525,6 +7526,7 @@ static void resolve_llvm_types_any_frame(CodeGen *g, ZigType *any_frame_type, Re |
| 7525 | 7526 | if (result_type == nullptr) { |
| 7526 | 7527 | g->anyframe_fn_type = ptr_result_type; |
| 7527 | 7528 | } |
| 7529 | // label (grep this): [coro_frame_struct_layout] | |
| 7528 | 7530 | LLVMTypeRef field_types[] = { |
| 7529 | 7531 | ptr_result_type, // fn_ptr |
| 7530 | 7532 | usize_type_ref, // awaiter |
| ... | ... | @@ -7558,6 +7560,7 @@ static void resolve_llvm_types_any_frame(CodeGen *g, ZigType *any_frame_type, Re |
| 7558 | 7560 | ZigLLVMReplaceTemporary(g->dbuilder, frame_header_di_type, replacement_di_type); |
| 7559 | 7561 | } else { |
| 7560 | 7562 | ZigType *ptr_result_type = get_pointer_to_type(g, result_type, false); |
| 7563 | // label (grep this): [coro_frame_struct_layout] | |
| 7561 | 7564 | LLVMTypeRef field_types[] = { |
| 7562 | 7565 | LLVMPointerType(fn_type, 0), // fn_ptr |
| 7563 | 7566 | usize_type_ref, // awaiter |
src/codegen.cpp+79-26| ... | ... | @@ -879,6 +879,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) { |
| 879 | 879 | return buf_create_from_str("async function returned twice"); |
| 880 | 880 | case PanicMsgIdResumedAnAwaitingFn: |
| 881 | 881 | return buf_create_from_str("awaiting function resumed"); |
| 882 | case PanicMsgIdFrameTooSmall: | |
| 883 | return buf_create_from_str("frame too small"); | |
| 882 | 884 | } |
| 883 | 885 | zig_unreachable(); |
| 884 | 886 | } |
| ... | ... | @@ -3479,7 +3481,18 @@ static void render_async_var_decls(CodeGen *g, Scope *scope) { |
| 3479 | 3481 | } |
| 3480 | 3482 | } |
| 3481 | 3483 | |
| 3484 | static LLVMValueRef gen_frame_size(CodeGen *g, LLVMValueRef fn_val) { | |
| 3485 | LLVMTypeRef usize_llvm_type = g->builtin_types.entry_usize->llvm_type; | |
| 3486 | LLVMTypeRef ptr_usize_llvm_type = LLVMPointerType(usize_llvm_type, 0); | |
| 3487 | LLVMValueRef casted_fn_val = LLVMBuildBitCast(g->builder, fn_val, ptr_usize_llvm_type, ""); | |
| 3488 | LLVMValueRef negative_one = LLVMConstInt(LLVMInt32Type(), -1, true); | |
| 3489 | LLVMValueRef prefix_ptr = LLVMBuildInBoundsGEP(g->builder, casted_fn_val, &negative_one, 1, ""); | |
| 3490 | return LLVMBuildLoad(g->builder, prefix_ptr, ""); | |
| 3491 | } | |
| 3492 | ||
| 3482 | 3493 | static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstructionCallGen *instruction) { |
| 3494 | LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; | |
| 3495 | ||
| 3483 | 3496 | LLVMValueRef fn_val; |
| 3484 | 3497 | ZigType *fn_type; |
| 3485 | 3498 | bool callee_is_async; |
| ... | ... | @@ -3511,34 +3524,54 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr |
| 3511 | 3524 | LLVMValueRef awaiter_init_val; |
| 3512 | 3525 | LLVMValueRef ret_ptr; |
| 3513 | 3526 | if (instruction->is_async) { |
| 3514 | frame_result_loc = result_loc; | |
| 3515 | 3527 | awaiter_init_val = zero; |
| 3516 | if (ret_has_bits) { | |
| 3517 | ret_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, coro_arg_start + 1, ""); | |
| 3518 | } | |
| 3519 | 3528 | |
| 3520 | // Use the result location which is inside the frame if this is an async call. | |
| 3521 | if (ret_has_bits) { | |
| 3522 | LLVMValueRef ret_ptr_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, coro_arg_start, ""); | |
| 3523 | LLVMBuildStore(g->builder, ret_ptr, ret_ptr_ptr); | |
| 3529 | if (instruction->new_stack == nullptr) { | |
| 3530 | frame_result_loc = result_loc; | |
| 3531 | ||
| 3532 | if (ret_has_bits) { | |
| 3533 | // Use the result location which is inside the frame if this is an async call. | |
| 3534 | ret_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, coro_arg_start + 1, ""); | |
| 3535 | } | |
| 3536 | } else { | |
| 3537 | LLVMValueRef frame_slice_ptr = ir_llvm_value(g, instruction->new_stack); | |
| 3538 | if (ir_want_runtime_safety(g, &instruction->base)) { | |
| 3539 | LLVMValueRef given_len_ptr = LLVMBuildStructGEP(g->builder, frame_slice_ptr, slice_len_index, ""); | |
| 3540 | LLVMValueRef given_frame_len = LLVMBuildLoad(g->builder, given_len_ptr, ""); | |
| 3541 | LLVMValueRef actual_frame_len = gen_frame_size(g, fn_val); | |
| 3542 | ||
| 3543 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "FrameSizeCheckFail"); | |
| 3544 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "FrameSizeCheckOk"); | |
| 3545 | ||
| 3546 | LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntUGE, given_frame_len, actual_frame_len, ""); | |
| 3547 | LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); | |
| 3548 | ||
| 3549 | LLVMPositionBuilderAtEnd(g->builder, fail_block); | |
| 3550 | gen_safety_crash(g, PanicMsgIdFrameTooSmall); | |
| 3551 | ||
| 3552 | LLVMPositionBuilderAtEnd(g->builder, ok_block); | |
| 3553 | } | |
| 3554 | LLVMValueRef frame_ptr_ptr = LLVMBuildStructGEP(g->builder, frame_slice_ptr, slice_ptr_index, ""); | |
| 3555 | LLVMValueRef frame_ptr = LLVMBuildLoad(g->builder, frame_ptr_ptr, ""); | |
| 3556 | frame_result_loc = LLVMBuildBitCast(g->builder, frame_ptr, | |
| 3557 | get_llvm_type(g, instruction->base.value.type), ""); | |
| 3558 | ||
| 3559 | if (ret_has_bits) { | |
| 3560 | // Use the result location provided to the @asyncCall builtin | |
| 3561 | ret_ptr = result_loc; | |
| 3562 | } | |
| 3524 | 3563 | } |
| 3525 | 3564 | } else if (callee_is_async) { |
| 3526 | 3565 | frame_result_loc = ir_llvm_value(g, instruction->frame_result_loc); |
| 3527 | 3566 | awaiter_init_val = LLVMBuildPtrToInt(g->builder, g->cur_ret_ptr, |
| 3528 | 3567 | g->builtin_types.entry_usize->llvm_type, ""); // caller's own frame pointer |
| 3529 | 3568 | if (ret_has_bits) { |
| 3569 | // Use the call instruction's result location. | |
| 3530 | 3570 | ret_ptr = result_loc; |
| 3531 | 3571 | } |
| 3532 | ||
| 3533 | // Use the call instruction's result location. | |
| 3534 | if (ret_has_bits) { | |
| 3535 | LLVMValueRef ret_ptr_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, coro_arg_start, ""); | |
| 3536 | LLVMBuildStore(g->builder, result_loc, ret_ptr_ptr); | |
| 3537 | } | |
| 3538 | 3572 | } |
| 3539 | 3573 | if (instruction->is_async || callee_is_async) { |
| 3540 | 3574 | assert(frame_result_loc != nullptr); |
| 3541 | assert(instruction->fn_entry != nullptr); | |
| 3542 | 3575 | |
| 3543 | 3576 | if (prefix_arg_err_ret_stack) { |
| 3544 | 3577 | zig_panic("TODO"); |
| ... | ... | @@ -3547,6 +3580,10 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr |
| 3547 | 3580 | LLVMValueRef awaiter_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, coro_awaiter_index, ""); |
| 3548 | 3581 | LLVMBuildStore(g->builder, awaiter_init_val, awaiter_ptr); |
| 3549 | 3582 | |
| 3583 | if (ret_has_bits) { | |
| 3584 | LLVMValueRef ret_ptr_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, coro_arg_start, ""); | |
| 3585 | LLVMBuildStore(g->builder, ret_ptr, ret_ptr_ptr); | |
| 3586 | } | |
| 3550 | 3587 | } |
| 3551 | 3588 | if (!instruction->is_async && !callee_is_async) { |
| 3552 | 3589 | if (first_arg_ret) { |
| ... | ... | @@ -3581,16 +3618,37 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr |
| 3581 | 3618 | |
| 3582 | 3619 | if (instruction->is_async || callee_is_async) { |
| 3583 | 3620 | size_t ret_2_or_0 = type_has_bits(fn_type->data.fn.fn_type_id.return_type) ? 2 : 0; |
| 3621 | size_t arg_start_i = coro_arg_start + ret_2_or_0; | |
| 3622 | ||
| 3623 | LLVMValueRef casted_frame; | |
| 3624 | if (instruction->new_stack != nullptr) { | |
| 3625 | // We need the frame type to be a pointer to a struct that includes the args | |
| 3626 | // label (grep this): [coro_frame_struct_layout] | |
| 3627 | size_t field_count = arg_start_i + gen_param_values.length; | |
| 3628 | LLVMTypeRef *field_types = allocate_nonzero<LLVMTypeRef>(field_count); | |
| 3629 | LLVMGetStructElementTypes(LLVMGetElementType(LLVMTypeOf(frame_result_loc)), field_types); | |
| 3630 | for (size_t arg_i = 0; arg_i < gen_param_values.length; arg_i += 1) { | |
| 3631 | field_types[arg_start_i + arg_i] = LLVMTypeOf(gen_param_values.at(arg_i)); | |
| 3632 | } | |
| 3633 | LLVMTypeRef frame_with_args_type = LLVMStructType(field_types, field_count, false); | |
| 3634 | LLVMTypeRef ptr_frame_with_args_type = LLVMPointerType(frame_with_args_type, 0); | |
| 3635 | ||
| 3636 | casted_frame = LLVMBuildBitCast(g->builder, frame_result_loc, ptr_frame_with_args_type, ""); | |
| 3637 | } else { | |
| 3638 | casted_frame = frame_result_loc; | |
| 3639 | } | |
| 3640 | ||
| 3584 | 3641 | for (size_t arg_i = 0; arg_i < gen_param_values.length; arg_i += 1) { |
| 3585 | LLVMValueRef arg_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, | |
| 3586 | coro_arg_start + ret_2_or_0 + arg_i, ""); | |
| 3642 | LLVMValueRef arg_ptr = LLVMBuildStructGEP(g->builder, casted_frame, arg_start_i + arg_i, ""); | |
| 3587 | 3643 | LLVMBuildStore(g->builder, gen_param_values.at(arg_i), arg_ptr); |
| 3588 | 3644 | } |
| 3589 | 3645 | } |
| 3590 | LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; | |
| 3591 | 3646 | if (instruction->is_async) { |
| 3592 | 3647 | LLVMValueRef args[] = {frame_result_loc, LLVMGetUndef(usize_type_ref)}; |
| 3593 | 3648 | ZigLLVMBuildCall(g->builder, fn_val, args, 2, llvm_cc, fn_inline, ""); |
| 3649 | if (instruction->new_stack != nullptr) { | |
| 3650 | return frame_result_loc; | |
| 3651 | } | |
| 3594 | 3652 | return nullptr; |
| 3595 | 3653 | } else if (callee_is_async) { |
| 3596 | 3654 | ZigType *ptr_result_type = get_pointer_to_type(g, src_return_type, true); |
| ... | ... | @@ -5223,13 +5281,8 @@ static LLVMValueRef ir_render_coro_resume(CodeGen *g, IrExecutable *executable, |
| 5223 | 5281 | static LLVMValueRef ir_render_frame_size(CodeGen *g, IrExecutable *executable, |
| 5224 | 5282 | IrInstructionFrameSizeGen *instruction) |
| 5225 | 5283 | { |
| 5226 | LLVMTypeRef usize_llvm_type = g->builtin_types.entry_usize->llvm_type; | |
| 5227 | LLVMTypeRef ptr_usize_llvm_type = LLVMPointerType(usize_llvm_type, 0); | |
| 5228 | 5284 | LLVMValueRef fn_val = ir_llvm_value(g, instruction->fn); |
| 5229 | LLVMValueRef casted_fn_val = LLVMBuildBitCast(g->builder, fn_val, ptr_usize_llvm_type, ""); | |
| 5230 | LLVMValueRef negative_one = LLVMConstInt(LLVMInt32Type(), -1, true); | |
| 5231 | LLVMValueRef prefix_ptr = LLVMBuildInBoundsGEP(g->builder, casted_fn_val, &negative_one, 1, ""); | |
| 5232 | return LLVMBuildLoad(g->builder, prefix_ptr, ""); | |
| 5285 | return gen_frame_size(g, fn_val); | |
| 5233 | 5286 | } |
| 5234 | 5287 | |
| 5235 | 5288 | static void set_debug_location(CodeGen *g, IrInstruction *instruction) { |
| ... | ... | @@ -7097,13 +7150,13 @@ static void define_builtin_fns(CodeGen *g) { |
| 7097 | 7150 | create_builtin_fn(g, BuiltinFnIdFloor, "floor", 2); |
| 7098 | 7151 | create_builtin_fn(g, BuiltinFnIdCeil, "ceil", 2); |
| 7099 | 7152 | create_builtin_fn(g, BuiltinFnIdTrunc, "trunc", 2); |
| 7100 | //Needs library support on Windows | |
| 7101 | //create_builtin_fn(g, BuiltinFnIdNearbyInt, "nearbyInt", 2); | |
| 7153 | create_builtin_fn(g, BuiltinFnIdNearbyInt, "nearbyInt", 2); | |
| 7102 | 7154 | create_builtin_fn(g, BuiltinFnIdRound, "round", 2); |
| 7103 | 7155 | create_builtin_fn(g, BuiltinFnIdMulAdd, "mulAdd", 4); |
| 7104 | 7156 | create_builtin_fn(g, BuiltinFnIdInlineCall, "inlineCall", SIZE_MAX); |
| 7105 | 7157 | create_builtin_fn(g, BuiltinFnIdNoInlineCall, "noInlineCall", SIZE_MAX); |
| 7106 | 7158 | create_builtin_fn(g, BuiltinFnIdNewStackCall, "newStackCall", SIZE_MAX); |
| 7159 | create_builtin_fn(g, BuiltinFnIdAsyncCall, "asyncCall", SIZE_MAX); | |
| 7107 | 7160 | create_builtin_fn(g, BuiltinFnIdTypeId, "typeId", 1); |
| 7108 | 7161 | create_builtin_fn(g, BuiltinFnIdShlExact, "shlExact", 2); |
| 7109 | 7162 | create_builtin_fn(g, BuiltinFnIdShrExact, "shrExact", 2); |
src/ir.cpp+85-17| ... | ... | @@ -1402,6 +1402,10 @@ static IrInstruction *ir_build_call_src(IrBuilder *irb, Scope *scope, AstNode *s |
| 1402 | 1402 | if (fn_ref != nullptr) ir_ref_instruction(fn_ref, irb->current_basic_block); |
| 1403 | 1403 | for (size_t i = 0; i < arg_count; i += 1) |
| 1404 | 1404 | ir_ref_instruction(args[i], irb->current_basic_block); |
| 1405 | if (is_async && new_stack != nullptr) { | |
| 1406 | // in this case the arg at the end is the return pointer | |
| 1407 | ir_ref_instruction(args[arg_count], irb->current_basic_block); | |
| 1408 | } | |
| 1405 | 1409 | if (new_stack != nullptr) ir_ref_instruction(new_stack, irb->current_basic_block); |
| 1406 | 1410 | |
| 1407 | 1411 | return &call_instruction->base; |
| ... | ... | @@ -5203,8 +5207,10 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 5203 | 5207 | } |
| 5204 | 5208 | case BuiltinFnIdNewStackCall: |
| 5205 | 5209 | { |
| 5206 | if (node->data.fn_call_expr.params.length == 0) { | |
| 5207 | add_node_error(irb->codegen, node, buf_sprintf("expected at least 1 argument, found 0")); | |
| 5210 | if (node->data.fn_call_expr.params.length < 2) { | |
| 5211 | add_node_error(irb->codegen, node, | |
| 5212 | buf_sprintf("expected at least 2 arguments, found %" ZIG_PRI_usize, | |
| 5213 | node->data.fn_call_expr.params.length)); | |
| 5208 | 5214 | return irb->codegen->invalid_instruction; |
| 5209 | 5215 | } |
| 5210 | 5216 | |
| ... | ... | @@ -5232,6 +5238,50 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 5232 | 5238 | FnInlineAuto, false, new_stack, result_loc); |
| 5233 | 5239 | return ir_lval_wrap(irb, scope, call, lval, result_loc); |
| 5234 | 5240 | } |
| 5241 | case BuiltinFnIdAsyncCall: | |
| 5242 | { | |
| 5243 | size_t arg_offset = 3; | |
| 5244 | if (node->data.fn_call_expr.params.length < arg_offset) { | |
| 5245 | add_node_error(irb->codegen, node, | |
| 5246 | buf_sprintf("expected at least %" ZIG_PRI_usize " arguments, found %" ZIG_PRI_usize, | |
| 5247 | arg_offset, node->data.fn_call_expr.params.length)); | |
| 5248 | return irb->codegen->invalid_instruction; | |
| 5249 | } | |
| 5250 | ||
| 5251 | AstNode *bytes_node = node->data.fn_call_expr.params.at(0); | |
| 5252 | IrInstruction *bytes = ir_gen_node(irb, bytes_node, scope); | |
| 5253 | if (bytes == irb->codegen->invalid_instruction) | |
| 5254 | return bytes; | |
| 5255 | ||
| 5256 | AstNode *ret_ptr_node = node->data.fn_call_expr.params.at(1); | |
| 5257 | IrInstruction *ret_ptr = ir_gen_node(irb, ret_ptr_node, scope); | |
| 5258 | if (ret_ptr == irb->codegen->invalid_instruction) | |
| 5259 | return ret_ptr; | |
| 5260 | ||
| 5261 | AstNode *fn_ref_node = node->data.fn_call_expr.params.at(2); | |
| 5262 | IrInstruction *fn_ref = ir_gen_node(irb, fn_ref_node, scope); | |
| 5263 | if (fn_ref == irb->codegen->invalid_instruction) | |
| 5264 | return fn_ref; | |
| 5265 | ||
| 5266 | size_t arg_count = node->data.fn_call_expr.params.length - arg_offset; | |
| 5267 | ||
| 5268 | // last "arg" is return pointer | |
| 5269 | IrInstruction **args = allocate<IrInstruction*>(arg_count + 1); | |
| 5270 | ||
| 5271 | for (size_t i = 0; i < arg_count; i += 1) { | |
| 5272 | AstNode *arg_node = node->data.fn_call_expr.params.at(i + arg_offset); | |
| 5273 | IrInstruction *arg = ir_gen_node(irb, arg_node, scope); | |
| 5274 | if (arg == irb->codegen->invalid_instruction) | |
| 5275 | return arg; | |
| 5276 | args[i] = arg; | |
| 5277 | } | |
| 5278 | ||
| 5279 | args[arg_count] = ret_ptr; | |
| 5280 | ||
| 5281 | IrInstruction *call = ir_build_call_src(irb, scope, node, nullptr, fn_ref, arg_count, args, false, | |
| 5282 | FnInlineAuto, true, bytes, result_loc); | |
| 5283 | return ir_lval_wrap(irb, scope, call, lval, result_loc); | |
| 5284 | } | |
| 5235 | 5285 | case BuiltinFnIdTypeId: |
| 5236 | 5286 | { |
| 5237 | 5287 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); |
| ... | ... | @@ -14817,11 +14867,31 @@ static IrInstruction *ir_analyze_instruction_reset_result(IrAnalyze *ira, IrInst |
| 14817 | 14867 | } |
| 14818 | 14868 | |
| 14819 | 14869 | static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCallSrc *call_instruction, ZigFn *fn_entry, |
| 14820 | ZigType *fn_type, IrInstruction *fn_ref, IrInstruction **casted_args, size_t arg_count) | |
| 14870 | ZigType *fn_type, IrInstruction *fn_ref, IrInstruction **casted_args, size_t arg_count, | |
| 14871 | IrInstruction *casted_new_stack) | |
| 14821 | 14872 | { |
| 14822 | 14873 | if (fn_entry == nullptr) { |
| 14823 | ir_add_error(ira, fn_ref, buf_sprintf("function is not comptime-known; @asyncCall required")); | |
| 14824 | return ira->codegen->invalid_instruction; | |
| 14874 | if (call_instruction->new_stack == nullptr) { | |
| 14875 | ir_add_error(ira, fn_ref, buf_sprintf("function is not comptime-known; @asyncCall required")); | |
| 14876 | return ira->codegen->invalid_instruction; | |
| 14877 | } | |
| 14878 | // this is an @asyncCall | |
| 14879 | ||
| 14880 | if (fn_type->data.fn.fn_type_id.cc != CallingConventionAsync) { | |
| 14881 | ir_add_error(ira, fn_ref, | |
| 14882 | buf_sprintf("expected async function, found '%s'", buf_ptr(&fn_type->name))); | |
| 14883 | return ira->codegen->invalid_instruction; | |
| 14884 | } | |
| 14885 | ||
| 14886 | IrInstruction *ret_ptr = call_instruction->args[call_instruction->arg_count]->child; | |
| 14887 | if (type_is_invalid(ret_ptr->value.type)) | |
| 14888 | return ira->codegen->invalid_instruction; | |
| 14889 | ||
| 14890 | ZigType *anyframe_type = get_any_frame_type(ira->codegen, fn_type->data.fn.fn_type_id.return_type); | |
| 14891 | ||
| 14892 | IrInstructionCallGen *call_gen = ir_build_call_gen(ira, &call_instruction->base, nullptr, fn_ref, | |
| 14893 | arg_count, casted_args, FnInlineAuto, true, casted_new_stack, ret_ptr, anyframe_type); | |
| 14894 | return &call_gen->base; | |
| 14825 | 14895 | } |
| 14826 | 14896 | |
| 14827 | 14897 | ZigType *frame_type = get_coro_frame_type(ira->codegen, fn_entry); |
| ... | ... | @@ -15559,13 +15629,13 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c |
| 15559 | 15629 | |
| 15560 | 15630 | size_t impl_param_count = impl_fn_type_id->param_count; |
| 15561 | 15631 | if (call_instruction->is_async) { |
| 15562 | zig_panic("TODO async call"); | |
| 15632 | IrInstruction *result = ir_analyze_async_call(ira, call_instruction, impl_fn, impl_fn->type_entry, | |
| 15633 | nullptr, casted_args, call_param_count, casted_new_stack); | |
| 15634 | return ir_finish_anal(ira, result); | |
| 15563 | 15635 | } |
| 15564 | 15636 | |
| 15565 | if (!call_instruction->is_async) { | |
| 15566 | if (impl_fn_type_id->cc == CallingConventionAsync && parent_fn_entry->inferred_async_node == nullptr) { | |
| 15567 | parent_fn_entry->inferred_async_node = fn_ref->source_node; | |
| 15568 | } | |
| 15637 | if (impl_fn_type_id->cc == CallingConventionAsync && parent_fn_entry->inferred_async_node == nullptr) { | |
| 15638 | parent_fn_entry->inferred_async_node = fn_ref->source_node; | |
| 15569 | 15639 | } |
| 15570 | 15640 | |
| 15571 | 15641 | IrInstructionCallGen *new_call_instruction = ir_build_call_gen(ira, &call_instruction->base, |
| ... | ... | @@ -15645,18 +15715,16 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c |
| 15645 | 15715 | return ira->codegen->invalid_instruction; |
| 15646 | 15716 | } |
| 15647 | 15717 | |
| 15648 | if (!call_instruction->is_async) { | |
| 15649 | if (fn_type_id->cc == CallingConventionAsync && parent_fn_entry->inferred_async_node == nullptr) { | |
| 15650 | parent_fn_entry->inferred_async_node = fn_ref->source_node; | |
| 15651 | } | |
| 15652 | } | |
| 15653 | ||
| 15654 | 15718 | if (call_instruction->is_async) { |
| 15655 | 15719 | IrInstruction *result = ir_analyze_async_call(ira, call_instruction, fn_entry, fn_type, fn_ref, |
| 15656 | casted_args, call_param_count); | |
| 15720 | casted_args, call_param_count, casted_new_stack); | |
| 15657 | 15721 | return ir_finish_anal(ira, result); |
| 15658 | 15722 | } |
| 15659 | 15723 | |
| 15724 | if (fn_type_id->cc == CallingConventionAsync && parent_fn_entry->inferred_async_node == nullptr) { | |
| 15725 | parent_fn_entry->inferred_async_node = fn_ref->source_node; | |
| 15726 | } | |
| 15727 | ||
| 15660 | 15728 | IrInstruction *result_loc; |
| 15661 | 15729 | if (handle_is_ptr(return_type)) { |
| 15662 | 15730 | result_loc = ir_resolve_result(ira, &call_instruction->base, call_instruction->result_loc, |
test/compile_errors.zig+12| ... | ... | @@ -2,6 +2,18 @@ const tests = @import("tests.zig"); |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | |
| 4 | 4 | pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5 | cases.add( | |
| 6 | "non async function pointer passed to @asyncCall", | |
| 7 | \\export fn entry() void { | |
| 8 | \\ var ptr = afunc; | |
| 9 | \\ var bytes: [100]u8 = undefined; | |
| 10 | \\ _ = @asyncCall(&bytes, {}, ptr); | |
| 11 | \\} | |
| 12 | \\fn afunc() void { } | |
| 13 | , | |
| 14 | "tmp.zig:4:32: error: expected async function, found 'fn() void'", | |
| 15 | ); | |
| 16 | ||
| 5 | 17 | cases.add( |
| 6 | 18 | "runtime-known async function called", |
| 7 | 19 | \\export fn entry() void { |
test/runtime_safety.zig+15| ... | ... | @@ -1,6 +1,20 @@ |
| 1 | 1 | const tests = @import("tests.zig"); |
| 2 | 2 | |
| 3 | 3 | pub fn addCases(cases: *tests.CompareOutputContext) void { |
| 4 | cases.addRuntimeSafety("@asyncCall with too small a frame", | |
| 5 | \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { | |
| 6 | \\ @import("std").os.exit(126); | |
| 7 | \\} | |
| 8 | \\pub fn main() void { | |
| 9 | \\ var bytes: [1]u8 = undefined; | |
| 10 | \\ var ptr = other; | |
| 11 | \\ var frame = @asyncCall(&bytes, {}, ptr); | |
| 12 | \\} | |
| 13 | \\async fn other() void { | |
| 14 | \\ suspend; | |
| 15 | \\} | |
| 16 | ); | |
| 17 | ||
| 4 | 18 | cases.addRuntimeSafety("resuming a function which is awaiting a frame", |
| 5 | 19 | \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { |
| 6 | 20 | \\ @import("std").os.exit(126); |
| ... | ... | @@ -17,6 +31,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { |
| 17 | 31 | \\ suspend; |
| 18 | 32 | \\} |
| 19 | 33 | ); |
| 34 | ||
| 20 | 35 | cases.addRuntimeSafety("resuming a function which is awaiting a call", |
| 21 | 36 | \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { |
| 22 | 37 | \\ @import("std").os.exit(126); |
test/stage1/behavior/coroutines.zig+36-16| ... | ... | @@ -260,22 +260,42 @@ test "async function with dot syntax" { |
| 260 | 260 | expect(S.y == 2); |
| 261 | 261 | } |
| 262 | 262 | |
| 263 | //test "async fn pointer in a struct field" { | |
| 264 | // var data: i32 = 1; | |
| 265 | // const Foo = struct { | |
| 266 | // bar: async fn (*i32) void, | |
| 267 | // }; | |
| 268 | // var foo = Foo{ .bar = simpleAsyncFn2 }; | |
| 269 | // const p = async foo.bar(&data); | |
| 270 | // expect(data == 2); | |
| 271 | // resume p; | |
| 272 | // expect(data == 4); | |
| 273 | //} | |
| 274 | //async fn simpleAsyncFn2(y: *i32) void { | |
| 275 | // defer y.* += 2; | |
| 276 | // y.* += 1; | |
| 277 | // suspend; | |
| 278 | //} | |
| 263 | test "async fn pointer in a struct field" { | |
| 264 | var data: i32 = 1; | |
| 265 | const Foo = struct { | |
| 266 | bar: async fn (*i32) void, | |
| 267 | }; | |
| 268 | var foo = Foo{ .bar = simpleAsyncFn2 }; | |
| 269 | var bytes: [64]u8 = undefined; | |
| 270 | const p = @asyncCall(&bytes, {}, foo.bar, &data); | |
| 271 | comptime expect(@typeOf(p) == anyframe->void); | |
| 272 | expect(data == 2); | |
| 273 | resume p; | |
| 274 | expect(data == 4); | |
| 275 | } | |
| 276 | async fn simpleAsyncFn2(y: *i32) void { | |
| 277 | defer y.* += 2; | |
| 278 | y.* += 1; | |
| 279 | suspend; | |
| 280 | } | |
| 281 | ||
| 282 | test "@asyncCall with return type" { | |
| 283 | const Foo = struct { | |
| 284 | bar: async fn () i32, | |
| 285 | ||
| 286 | async fn afunc() i32 { | |
| 287 | suspend; | |
| 288 | return 1234; | |
| 289 | } | |
| 290 | }; | |
| 291 | var foo = Foo{ .bar = Foo.afunc }; | |
| 292 | var bytes: [64]u8 = undefined; | |
| 293 | var aresult: i32 = 0; | |
| 294 | const frame = @asyncCall(&bytes, &aresult, foo.bar); | |
| 295 | expect(aresult == 0); | |
| 296 | resume frame; | |
| 297 | expect(aresult == 1234); | |
| 298 | } | |
| 279 | 299 | |
| 280 | 300 | //test "async fn with inferred error set" { |
| 281 | 301 | // const p = async failing(); |