authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-08-31 18:50:16-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-08-31 18:50:16-04:00
log5c3a9a1a3eef82ffad17bc295da05ecccd9006a5
treebc59e5a8e4526a1d1fa7b8dad10af509a6286419
parenta2230639232c069e4052a2e994dd5c0bd4e2517f
signaturelock-open Commit is signed but in an unrecognized format.

improvements to `@asyncCall`

* `await @asyncCall` generates better code. See #3065 * `@asyncCall` works with a real `@Frame(func)` in addition to a byte slice. Closes #3072 * `@asyncCall` allows passing `{}` (a void value) as the result pointer, which uses the result location inside the frame. Closes #3068 * support `await @asyncCall` on a non-async function. This is in preparation for safe recursion (#1006).

6 files changed, 308 insertions(+), 105 deletions(-)

src/all_types.hpp+2
...@@ -2719,6 +2719,7 @@ struct IrInstructionCallSrc {...@@ -2719,6 +2719,7 @@ struct IrInstructionCallSrc {
2719 IrInstruction *new_stack;2719 IrInstruction *new_stack;
2720 FnInline fn_inline;2720 FnInline fn_inline;
2721 bool is_async;2721 bool is_async;
2722 bool is_async_call_builtin;
2722 bool is_comptime;2723 bool is_comptime;
2723};2724};
27242725
...@@ -2735,6 +2736,7 @@ struct IrInstructionCallGen {...@@ -2735,6 +2736,7 @@ struct IrInstructionCallGen {
2735 IrInstruction *new_stack;2736 IrInstruction *new_stack;
2736 FnInline fn_inline;2737 FnInline fn_inline;
2737 bool is_async;2738 bool is_async;
2739 bool is_async_call_builtin;
2738};2740};
27392741
2740struct IrInstructionConst {2742struct IrInstructionConst {
src/analyze.cpp+4
...@@ -5727,6 +5727,10 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) {...@@ -5727,6 +5727,10 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) {
57275727
5728 for (size_t i = 0; i < fn->call_list.length; i += 1) {5728 for (size_t i = 0; i < fn->call_list.length; i += 1) {
5729 IrInstructionCallGen *call = fn->call_list.at(i);5729 IrInstructionCallGen *call = fn->call_list.at(i);
5730 if (call->new_stack != nullptr) {
5731 // don't need to allocate a frame for this
5732 continue;
5733 }
5730 ZigFn *callee = call->fn_entry;5734 ZigFn *callee = call->fn_entry;
5731 if (callee == nullptr) {5735 if (callee == nullptr) {
5732 add_node_error(g, call->base.source_node,5736 add_node_error(g, call->base.source_node,
src/codegen.cpp+42-19
...@@ -3826,17 +3826,18 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr...@@ -3826,17 +3826,18 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
3826 LLVMValueRef awaiter_init_val;3826 LLVMValueRef awaiter_init_val;
3827 LLVMValueRef ret_ptr;3827 LLVMValueRef ret_ptr;
3828 if (callee_is_async) {3828 if (callee_is_async) {
3829 if (instruction->is_async) {3829 if (instruction->new_stack == nullptr) {
3830 if (instruction->new_stack == nullptr) {3830 if (instruction->is_async) {
3831 awaiter_init_val = zero;
3832 frame_result_loc = result_loc;3831 frame_result_loc = result_loc;
38333832 } else {
3834 if (ret_has_bits) {3833 frame_result_loc = ir_llvm_value(g, instruction->frame_result_loc);
3835 // Use the result location which is inside the frame if this is an async call.3834 }
3836 ret_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, frame_ret_start + 2, "");3835 } else {
3837 }3836 if (instruction->new_stack->value.type->id == ZigTypeIdPointer &&
3838 } else if (cc == CallingConventionAsync) {3837 instruction->new_stack->value.type->data.pointer.child_type->id == ZigTypeIdFnFrame)
3839 awaiter_init_val = zero;3838 {
3839 frame_result_loc = ir_llvm_value(g, instruction->new_stack);
3840 } else {
3840 LLVMValueRef frame_slice_ptr = ir_llvm_value(g, instruction->new_stack);3841 LLVMValueRef frame_slice_ptr = ir_llvm_value(g, instruction->new_stack);
3841 if (ir_want_runtime_safety(g, &instruction->base)) {3842 if (ir_want_runtime_safety(g, &instruction->base)) {
3842 LLVMValueRef given_len_ptr = LLVMBuildStructGEP(g->builder, frame_slice_ptr, slice_len_index, "");3843 LLVMValueRef given_len_ptr = LLVMBuildStructGEP(g->builder, frame_slice_ptr, slice_len_index, "");
...@@ -3856,15 +3857,37 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr...@@ -3856,15 +3857,37 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
3856 }3857 }
3857 LLVMValueRef frame_ptr_ptr = LLVMBuildStructGEP(g->builder, frame_slice_ptr, slice_ptr_index, "");3858 LLVMValueRef frame_ptr_ptr = LLVMBuildStructGEP(g->builder, frame_slice_ptr, slice_ptr_index, "");
3858 LLVMValueRef frame_ptr = LLVMBuildLoad(g->builder, frame_ptr_ptr, "");3859 LLVMValueRef frame_ptr = LLVMBuildLoad(g->builder, frame_ptr_ptr, "");
3859 frame_result_loc = LLVMBuildBitCast(g->builder, frame_ptr,3860 if (instruction->fn_entry == nullptr) {
3860 get_llvm_type(g, instruction->base.value.type), "");3861 ZigType *anyframe_type = get_any_frame_type(g, src_return_type);
3862 frame_result_loc = LLVMBuildBitCast(g->builder, frame_ptr, get_llvm_type(g, anyframe_type), "");
3863 } else {
3864 ZigType *ptr_frame_type = get_pointer_to_type(g,
3865 get_fn_frame_type(g, instruction->fn_entry), false);
3866 frame_result_loc = LLVMBuildBitCast(g->builder, frame_ptr,
3867 get_llvm_type(g, ptr_frame_type), "");
3868 }
3869 }
3870 }
3871 if (instruction->is_async) {
3872 if (instruction->new_stack == nullptr) {
3873 awaiter_init_val = zero;
38613874
3862 if (ret_has_bits) {3875 if (ret_has_bits) {
3863 // Use the result location provided to the @asyncCall builtin3876 // Use the result location which is inside the frame if this is an async call.
3864 ret_ptr = result_loc;3877 ret_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, frame_ret_start + 2, "");
3865 }3878 }
3866 } else {3879 } else {
3867 zig_unreachable();3880 awaiter_init_val = zero;
3881
3882 if (ret_has_bits) {
3883 if (result_loc != nullptr) {
3884 // Use the result location provided to the @asyncCall builtin
3885 ret_ptr = result_loc;
3886 } else {
3887 // no result location provided to @asyncCall - use the one inside the frame.
3888 ret_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, frame_ret_start + 2, "");
3889 }
3890 }
3868 }3891 }
38693892
3870 // even if prefix_arg_err_ret_stack is true, let the async function do its own3893 // even if prefix_arg_err_ret_stack is true, let the async function do its own
...@@ -3872,7 +3895,6 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr...@@ -3872,7 +3895,6 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
3872 } else {3895 } else {
3873 // async function called as a normal function3896 // async function called as a normal function
38743897
3875 frame_result_loc = ir_llvm_value(g, instruction->frame_result_loc);
3876 awaiter_init_val = LLVMBuildPtrToInt(g->builder, g->cur_frame_ptr, usize_type_ref, ""); // caller's own frame pointer3898 awaiter_init_val = LLVMBuildPtrToInt(g->builder, g->cur_frame_ptr, usize_type_ref, ""); // caller's own frame pointer
3877 if (ret_has_bits) {3899 if (ret_has_bits) {
3878 if (result_loc == nullptr) {3900 if (result_loc == nullptr) {
...@@ -3988,7 +4010,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr...@@ -3988,7 +4010,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
3988 uint32_t arg_start_i = frame_index_arg(g, fn_type->data.fn.fn_type_id.return_type);4010 uint32_t arg_start_i = frame_index_arg(g, fn_type->data.fn.fn_type_id.return_type);
39894011
3990 LLVMValueRef casted_frame;4012 LLVMValueRef casted_frame;
3991 if (instruction->new_stack != nullptr) {4013 if (instruction->new_stack != nullptr && instruction->fn_entry == nullptr) {
3992 // We need the frame type to be a pointer to a struct that includes the args4014 // We need the frame type to be a pointer to a struct that includes the args
3993 size_t field_count = arg_start_i + gen_param_values.length;4015 size_t field_count = arg_start_i + gen_param_values.length;
3994 LLVMTypeRef *field_types = allocate_nonzero<LLVMTypeRef>(field_count);4016 LLVMTypeRef *field_types = allocate_nonzero<LLVMTypeRef>(field_count);
...@@ -4014,7 +4036,8 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr...@@ -4014,7 +4036,8 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
4014 if (instruction->is_async) {4036 if (instruction->is_async) {
4015 gen_resume(g, fn_val, frame_result_loc, ResumeIdCall);4037 gen_resume(g, fn_val, frame_result_loc, ResumeIdCall);
4016 if (instruction->new_stack != nullptr) {4038 if (instruction->new_stack != nullptr) {
4017 return frame_result_loc;4039 return LLVMBuildBitCast(g->builder, frame_result_loc,
4040 get_llvm_type(g, instruction->base.value.type), "");
4018 }4041 }
4019 return nullptr;4042 return nullptr;
4020 } else {4043 } else {
...@@ -4041,7 +4064,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr...@@ -4041,7 +4064,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
4041 }4064 }
4042 }4065 }
40434066
4044 if (instruction->new_stack == nullptr) {4067 if (instruction->new_stack == nullptr || instruction->is_async_call_builtin) {
4045 result = ZigLLVMBuildCall(g->builder, fn_val,4068 result = ZigLLVMBuildCall(g->builder, fn_val,
4046 gen_param_values.items, (unsigned)gen_param_values.length, llvm_cc, fn_inline, "");4069 gen_param_values.items, (unsigned)gen_param_values.length, llvm_cc, fn_inline, "");
4047 } else if (instruction->is_async) {4070 } else if (instruction->is_async) {
src/ir.cpp+141-84
...@@ -1382,7 +1382,7 @@ static IrInstruction *ir_build_union_field_ptr(IrBuilder *irb, Scope *scope, Ast...@@ -1382,7 +1382,7 @@ static IrInstruction *ir_build_union_field_ptr(IrBuilder *irb, Scope *scope, Ast
13821382
1383static IrInstruction *ir_build_call_src(IrBuilder *irb, Scope *scope, AstNode *source_node,1383static IrInstruction *ir_build_call_src(IrBuilder *irb, Scope *scope, AstNode *source_node,
1384 ZigFn *fn_entry, IrInstruction *fn_ref, size_t arg_count, IrInstruction **args,1384 ZigFn *fn_entry, IrInstruction *fn_ref, size_t arg_count, IrInstruction **args,
1385 bool is_comptime, FnInline fn_inline, bool is_async,1385 bool is_comptime, FnInline fn_inline, bool is_async, bool is_async_call_builtin,
1386 IrInstruction *new_stack, ResultLoc *result_loc)1386 IrInstruction *new_stack, ResultLoc *result_loc)
1387{1387{
1388 IrInstructionCallSrc *call_instruction = ir_build_instruction<IrInstructionCallSrc>(irb, scope, source_node);1388 IrInstructionCallSrc *call_instruction = ir_build_instruction<IrInstructionCallSrc>(irb, scope, source_node);
...@@ -1393,6 +1393,7 @@ static IrInstruction *ir_build_call_src(IrBuilder *irb, Scope *scope, AstNode *s...@@ -1393,6 +1393,7 @@ static IrInstruction *ir_build_call_src(IrBuilder *irb, Scope *scope, AstNode *s
1393 call_instruction->args = args;1393 call_instruction->args = args;
1394 call_instruction->arg_count = arg_count;1394 call_instruction->arg_count = arg_count;
1395 call_instruction->is_async = is_async;1395 call_instruction->is_async = is_async;
1396 call_instruction->is_async_call_builtin = is_async_call_builtin;
1396 call_instruction->new_stack = new_stack;1397 call_instruction->new_stack = new_stack;
1397 call_instruction->result_loc = result_loc;1398 call_instruction->result_loc = result_loc;
13981399
...@@ -1410,7 +1411,7 @@ static IrInstruction *ir_build_call_src(IrBuilder *irb, Scope *scope, AstNode *s...@@ -1410,7 +1411,7 @@ static IrInstruction *ir_build_call_src(IrBuilder *irb, Scope *scope, AstNode *s
14101411
1411static IrInstructionCallGen *ir_build_call_gen(IrAnalyze *ira, IrInstruction *source_instruction,1412static IrInstructionCallGen *ir_build_call_gen(IrAnalyze *ira, IrInstruction *source_instruction,
1412 ZigFn *fn_entry, IrInstruction *fn_ref, size_t arg_count, IrInstruction **args,1413 ZigFn *fn_entry, IrInstruction *fn_ref, size_t arg_count, IrInstruction **args,
1413 FnInline fn_inline, bool is_async, IrInstruction *new_stack,1414 FnInline fn_inline, bool is_async, IrInstruction *new_stack, bool is_async_call_builtin,
1414 IrInstruction *result_loc, ZigType *return_type)1415 IrInstruction *result_loc, ZigType *return_type)
1415{1416{
1416 IrInstructionCallGen *call_instruction = ir_build_instruction<IrInstructionCallGen>(&ira->new_irb,1417 IrInstructionCallGen *call_instruction = ir_build_instruction<IrInstructionCallGen>(&ira->new_irb,
...@@ -1422,6 +1423,7 @@ static IrInstructionCallGen *ir_build_call_gen(IrAnalyze *ira, IrInstruction *so...@@ -1422,6 +1423,7 @@ static IrInstructionCallGen *ir_build_call_gen(IrAnalyze *ira, IrInstruction *so
1422 call_instruction->args = args;1423 call_instruction->args = args;
1423 call_instruction->arg_count = arg_count;1424 call_instruction->arg_count = arg_count;
1424 call_instruction->is_async = is_async;1425 call_instruction->is_async = is_async;
1426 call_instruction->is_async_call_builtin = is_async_call_builtin;
1425 call_instruction->new_stack = new_stack;1427 call_instruction->new_stack = new_stack;
1426 call_instruction->result_loc = result_loc;1428 call_instruction->result_loc = result_loc;
14271429
...@@ -4351,6 +4353,54 @@ static IrInstruction *ir_gen_this(IrBuilder *irb, Scope *orig_scope, AstNode *no...@@ -4351,6 +4353,54 @@ static IrInstruction *ir_gen_this(IrBuilder *irb, Scope *orig_scope, AstNode *no
4351 zig_unreachable();4353 zig_unreachable();
4352}4354}
43534355
4356static IrInstruction *ir_gen_async_call(IrBuilder *irb, Scope *scope, AstNode *await_node, AstNode *call_node,
4357 LVal lval, ResultLoc *result_loc)
4358{
4359 size_t arg_offset = 3;
4360 if (call_node->data.fn_call_expr.params.length < arg_offset) {
4361 add_node_error(irb->codegen, call_node,
4362 buf_sprintf("expected at least %" ZIG_PRI_usize " arguments, found %" ZIG_PRI_usize,
4363 arg_offset, call_node->data.fn_call_expr.params.length));
4364 return irb->codegen->invalid_instruction;
4365 }
4366
4367 AstNode *bytes_node = call_node->data.fn_call_expr.params.at(0);
4368 IrInstruction *bytes = ir_gen_node(irb, bytes_node, scope);
4369 if (bytes == irb->codegen->invalid_instruction)
4370 return bytes;
4371
4372 AstNode *ret_ptr_node = call_node->data.fn_call_expr.params.at(1);
4373 IrInstruction *ret_ptr = ir_gen_node(irb, ret_ptr_node, scope);
4374 if (ret_ptr == irb->codegen->invalid_instruction)
4375 return ret_ptr;
4376
4377 AstNode *fn_ref_node = call_node->data.fn_call_expr.params.at(2);
4378 IrInstruction *fn_ref = ir_gen_node(irb, fn_ref_node, scope);
4379 if (fn_ref == irb->codegen->invalid_instruction)
4380 return fn_ref;
4381
4382 size_t arg_count = call_node->data.fn_call_expr.params.length - arg_offset;
4383
4384 // last "arg" is return pointer
4385 IrInstruction **args = allocate<IrInstruction*>(arg_count + 1);
4386
4387 for (size_t i = 0; i < arg_count; i += 1) {
4388 AstNode *arg_node = call_node->data.fn_call_expr.params.at(i + arg_offset);
4389 IrInstruction *arg = ir_gen_node(irb, arg_node, scope);
4390 if (arg == irb->codegen->invalid_instruction)
4391 return arg;
4392 args[i] = arg;
4393 }
4394
4395 args[arg_count] = ret_ptr;
4396
4397 bool is_async = await_node == nullptr;
4398 bool is_async_call_builtin = true;
4399 IrInstruction *call = ir_build_call_src(irb, scope, call_node, nullptr, fn_ref, arg_count, args, false,
4400 FnInlineAuto, is_async, is_async_call_builtin, bytes, result_loc);
4401 return ir_lval_wrap(irb, scope, call, lval, result_loc);
4402}
4403
4354static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval,4404static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval,
4355 ResultLoc *result_loc)4405 ResultLoc *result_loc)
4356{4406{
...@@ -4360,7 +4410,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4360,7 +4410,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4360 Buf *name = fn_ref_expr->data.symbol_expr.symbol;4410 Buf *name = fn_ref_expr->data.symbol_expr.symbol;
4361 auto entry = irb->codegen->builtin_fn_table.maybe_get(name);4411 auto entry = irb->codegen->builtin_fn_table.maybe_get(name);
43624412
4363 if (!entry) { // new built in not found4413 if (!entry) {
4364 add_node_error(irb->codegen, node,4414 add_node_error(irb->codegen, node,
4365 buf_sprintf("invalid builtin function: '%s'", buf_ptr(name)));4415 buf_sprintf("invalid builtin function: '%s'", buf_ptr(name)));
4366 return irb->codegen->invalid_instruction;4416 return irb->codegen->invalid_instruction;
...@@ -5224,7 +5274,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -5224,7 +5274,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
5224 FnInline fn_inline = (builtin_fn->id == BuiltinFnIdInlineCall) ? FnInlineAlways : FnInlineNever;5274 FnInline fn_inline = (builtin_fn->id == BuiltinFnIdInlineCall) ? FnInlineAlways : FnInlineNever;
52255275
5226 IrInstruction *call = ir_build_call_src(irb, scope, node, nullptr, fn_ref, arg_count, args, false,5276 IrInstruction *call = ir_build_call_src(irb, scope, node, nullptr, fn_ref, arg_count, args, false,
5227 fn_inline, false, nullptr, result_loc);5277 fn_inline, false, false, nullptr, result_loc);
5228 return ir_lval_wrap(irb, scope, call, lval, result_loc);5278 return ir_lval_wrap(irb, scope, call, lval, result_loc);
5229 }5279 }
5230 case BuiltinFnIdNewStackCall:5280 case BuiltinFnIdNewStackCall:
...@@ -5257,53 +5307,11 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -5257,53 +5307,11 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
5257 }5307 }
52585308
5259 IrInstruction *call = ir_build_call_src(irb, scope, node, nullptr, fn_ref, arg_count, args, false,5309 IrInstruction *call = ir_build_call_src(irb, scope, node, nullptr, fn_ref, arg_count, args, false,
5260 FnInlineAuto, false, new_stack, result_loc);5310 FnInlineAuto, false, false, new_stack, result_loc);
5261 return ir_lval_wrap(irb, scope, call, lval, result_loc);5311 return ir_lval_wrap(irb, scope, call, lval, result_loc);
5262 }5312 }
5263 case BuiltinFnIdAsyncCall:5313 case BuiltinFnIdAsyncCall:
5264 {5314 return ir_gen_async_call(irb, scope, nullptr, node, lval, result_loc);
5265 size_t arg_offset = 3;
5266 if (node->data.fn_call_expr.params.length < arg_offset) {
5267 add_node_error(irb->codegen, node,
5268 buf_sprintf("expected at least %" ZIG_PRI_usize " arguments, found %" ZIG_PRI_usize,
5269 arg_offset, node->data.fn_call_expr.params.length));
5270 return irb->codegen->invalid_instruction;
5271 }
5272
5273 AstNode *bytes_node = node->data.fn_call_expr.params.at(0);
5274 IrInstruction *bytes = ir_gen_node(irb, bytes_node, scope);
5275 if (bytes == irb->codegen->invalid_instruction)
5276 return bytes;
5277
5278 AstNode *ret_ptr_node = node->data.fn_call_expr.params.at(1);
5279 IrInstruction *ret_ptr = ir_gen_node(irb, ret_ptr_node, scope);
5280 if (ret_ptr == irb->codegen->invalid_instruction)
5281 return ret_ptr;
5282
5283 AstNode *fn_ref_node = node->data.fn_call_expr.params.at(2);
5284 IrInstruction *fn_ref = ir_gen_node(irb, fn_ref_node, scope);
5285 if (fn_ref == irb->codegen->invalid_instruction)
5286 return fn_ref;
5287
5288 size_t arg_count = node->data.fn_call_expr.params.length - arg_offset;
5289
5290 // last "arg" is return pointer
5291 IrInstruction **args = allocate<IrInstruction*>(arg_count + 1);
5292
5293 for (size_t i = 0; i < arg_count; i += 1) {
5294 AstNode *arg_node = node->data.fn_call_expr.params.at(i + arg_offset);
5295 IrInstruction *arg = ir_gen_node(irb, arg_node, scope);
5296 if (arg == irb->codegen->invalid_instruction)
5297 return arg;
5298 args[i] = arg;
5299 }
5300
5301 args[arg_count] = ret_ptr;
5302
5303 IrInstruction *call = ir_build_call_src(irb, scope, node, nullptr, fn_ref, arg_count, args, false,
5304 FnInlineAuto, true, bytes, result_loc);
5305 return ir_lval_wrap(irb, scope, call, lval, result_loc);
5306 }
5307 case BuiltinFnIdTypeId:5315 case BuiltinFnIdTypeId:
5308 {5316 {
5309 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);5317 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
...@@ -5607,7 +5615,7 @@ static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node...@@ -5607,7 +5615,7 @@ static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node
56075615
5608 bool is_async = node->data.fn_call_expr.is_async;5616 bool is_async = node->data.fn_call_expr.is_async;
5609 IrInstruction *fn_call = ir_build_call_src(irb, scope, node, nullptr, fn_ref, arg_count, args, false,5617 IrInstruction *fn_call = ir_build_call_src(irb, scope, node, nullptr, fn_ref, arg_count, args, false,
5610 FnInlineAuto, is_async, nullptr, result_loc);5618 FnInlineAuto, is_async, false, nullptr, result_loc);
5611 return ir_lval_wrap(irb, scope, fn_call, lval, result_loc);5619 return ir_lval_wrap(irb, scope, fn_call, lval, result_loc);
5612}5620}
56135621
...@@ -7900,6 +7908,19 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n...@@ -7900,6 +7908,19 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n
7900{7908{
7901 assert(node->type == NodeTypeAwaitExpr);7909 assert(node->type == NodeTypeAwaitExpr);
79027910
7911 AstNode *expr_node = node->data.await_expr.expr;
7912 if (expr_node->type == NodeTypeFnCallExpr && expr_node->data.fn_call_expr.is_builtin) {
7913 AstNode *fn_ref_expr = expr_node->data.fn_call_expr.fn_ref_expr;
7914 Buf *name = fn_ref_expr->data.symbol_expr.symbol;
7915 auto entry = irb->codegen->builtin_fn_table.maybe_get(name);
7916 if (entry != nullptr) {
7917 BuiltinFnEntry *builtin_fn = entry->value;
7918 if (builtin_fn->id == BuiltinFnIdAsyncCall) {
7919 return ir_gen_async_call(irb, scope, node, expr_node, lval, result_loc);
7920 }
7921 }
7922 }
7923
7903 ZigFn *fn_entry = exec_fn_entry(irb->exec);7924 ZigFn *fn_entry = exec_fn_entry(irb->exec);
7904 if (!fn_entry) {7925 if (!fn_entry) {
7905 add_node_error(irb->codegen, node, buf_sprintf("await outside function definition"));7926 add_node_error(irb->codegen, node, buf_sprintf("await outside function definition"));
...@@ -7915,7 +7936,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n...@@ -7915,7 +7936,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n
7915 return irb->codegen->invalid_instruction;7936 return irb->codegen->invalid_instruction;
7916 }7937 }
79177938
7918 IrInstruction *target_inst = ir_gen_node_extra(irb, node->data.await_expr.expr, scope, LValPtr, nullptr);7939 IrInstruction *target_inst = ir_gen_node_extra(irb, expr_node, scope, LValPtr, nullptr);
7919 if (target_inst == irb->codegen->invalid_instruction)7940 if (target_inst == irb->codegen->invalid_instruction)
7920 return irb->codegen->invalid_instruction;7941 return irb->codegen->invalid_instruction;
79217942
...@@ -15244,44 +15265,61 @@ static IrInstruction *ir_analyze_instruction_reset_result(IrAnalyze *ira, IrInst...@@ -15244,44 +15265,61 @@ static IrInstruction *ir_analyze_instruction_reset_result(IrAnalyze *ira, IrInst
15244 return ir_const_void(ira, &instruction->base);15265 return ir_const_void(ira, &instruction->base);
15245}15266}
1524615267
15268static IrInstruction *get_async_call_result_loc(IrAnalyze *ira, IrInstructionCallSrc *call_instruction,
15269 ZigType *fn_ret_type)
15270{
15271 ir_assert(call_instruction->is_async_call_builtin, &call_instruction->base);
15272 IrInstruction *ret_ptr_uncasted = call_instruction->args[call_instruction->arg_count]->child;
15273 if (type_is_invalid(ret_ptr_uncasted->value.type))
15274 return ira->codegen->invalid_instruction;
15275 if (ret_ptr_uncasted->value.type->id == ZigTypeIdVoid) {
15276 // Result location will be inside the async frame.
15277 return nullptr;
15278 }
15279 return ir_implicit_cast(ira, ret_ptr_uncasted, get_pointer_to_type(ira->codegen, fn_ret_type, false));
15280}
15281
15247static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCallSrc *call_instruction, ZigFn *fn_entry,15282static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCallSrc *call_instruction, ZigFn *fn_entry,
15248 ZigType *fn_type, IrInstruction *fn_ref, IrInstruction **casted_args, size_t arg_count,15283 ZigType *fn_type, IrInstruction *fn_ref, IrInstruction **casted_args, size_t arg_count,
15249 IrInstruction *casted_new_stack)15284 IrInstruction *casted_new_stack)
15250{15285{
15251 if (casted_new_stack != nullptr) {15286 if (fn_entry == nullptr) {
15252 // this is an @asyncCall
15253
15254 if (fn_type->data.fn.fn_type_id.cc != CallingConventionAsync) {15287 if (fn_type->data.fn.fn_type_id.cc != CallingConventionAsync) {
15255 ir_add_error(ira, fn_ref,15288 ir_add_error(ira, fn_ref,
15256 buf_sprintf("expected async function, found '%s'", buf_ptr(&fn_type->name)));15289 buf_sprintf("expected async function, found '%s'", buf_ptr(&fn_type->name)));
15257 return ira->codegen->invalid_instruction;15290 return ira->codegen->invalid_instruction;
15258 }15291 }
1525915292 if (casted_new_stack == nullptr) {
15260 IrInstruction *ret_ptr = call_instruction->args[call_instruction->arg_count]->child;15293 ir_add_error(ira, fn_ref, buf_sprintf("function is not comptime-known; @asyncCall required"));
15261 if (type_is_invalid(ret_ptr->value.type))15294 return ira->codegen->invalid_instruction;
15295 }
15296 }
15297 if (casted_new_stack != nullptr) {
15298 ZigType *fn_ret_type = fn_type->data.fn.fn_type_id.return_type;
15299 IrInstruction *ret_ptr = get_async_call_result_loc(ira, call_instruction, fn_ret_type);
15300 if (ret_ptr != nullptr && type_is_invalid(ret_ptr->value.type))
15262 return ira->codegen->invalid_instruction;15301 return ira->codegen->invalid_instruction;
1526315302
15264 ZigType *anyframe_type = get_any_frame_type(ira->codegen, fn_type->data.fn.fn_type_id.return_type);15303 ZigType *anyframe_type = get_any_frame_type(ira->codegen, fn_ret_type);
1526515304
15266 IrInstructionCallGen *call_gen = ir_build_call_gen(ira, &call_instruction->base, nullptr, fn_ref,15305 IrInstructionCallGen *call_gen = ir_build_call_gen(ira, &call_instruction->base, fn_entry, fn_ref,
15267 arg_count, casted_args, FnInlineAuto, true, casted_new_stack, ret_ptr, anyframe_type);15306 arg_count, casted_args, FnInlineAuto, true, casted_new_stack,
15307 call_instruction->is_async_call_builtin, ret_ptr, anyframe_type);
15268 return &call_gen->base;15308 return &call_gen->base;
15269 } else if (fn_entry == nullptr) {15309 } else {
15270 ir_add_error(ira, fn_ref, buf_sprintf("function is not comptime-known; @asyncCall required"));15310 ZigType *frame_type = get_fn_frame_type(ira->codegen, fn_entry);
15271 return ira->codegen->invalid_instruction;15311 IrInstruction *result_loc = ir_resolve_result(ira, &call_instruction->base, call_instruction->result_loc,
15272 }15312 frame_type, nullptr, true, true, false);
1527315313 if (type_is_invalid(result_loc->value.type) || instr_is_unreachable(result_loc)) {
15274 ZigType *frame_type = get_fn_frame_type(ira->codegen, fn_entry);15314 return result_loc;
15275 IrInstruction *result_loc = ir_resolve_result(ira, &call_instruction->base, call_instruction->result_loc,15315 }
15276 frame_type, nullptr, true, true, false);15316 result_loc = ir_implicit_cast(ira, result_loc, get_pointer_to_type(ira->codegen, frame_type, false));
15277 if (type_is_invalid(result_loc->value.type) || instr_is_unreachable(result_loc)) {15317 if (type_is_invalid(result_loc->value.type))
15278 return result_loc;15318 return ira->codegen->invalid_instruction;
15319 return &ir_build_call_gen(ira, &call_instruction->base, fn_entry, fn_ref, arg_count,
15320 casted_args, FnInlineAuto, true, casted_new_stack, call_instruction->is_async_call_builtin,
15321 result_loc, frame_type)->base;
15279 }15322 }
15280 result_loc = ir_implicit_cast(ira, result_loc, get_pointer_to_type(ira->codegen, frame_type, false));
15281 if (type_is_invalid(result_loc->value.type))
15282 return ira->codegen->invalid_instruction;
15283 return &ir_build_call_gen(ira, &call_instruction->base, fn_entry, fn_ref, arg_count,
15284 casted_args, FnInlineAuto, true, nullptr, result_loc, frame_type)->base;
15285}15323}
15286static bool ir_analyze_fn_call_inline_arg(IrAnalyze *ira, AstNode *fn_proto_node,15324static bool ir_analyze_fn_call_inline_arg(IrAnalyze *ira, AstNode *fn_proto_node,
15287 IrInstruction *arg, Scope **exec_scope, size_t *next_proto_i)15325 IrInstruction *arg, Scope **exec_scope, size_t *next_proto_i)
...@@ -15790,16 +15828,27 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c...@@ -15790,16 +15828,27 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c
1579015828
15791 IrInstruction *casted_new_stack = nullptr;15829 IrInstruction *casted_new_stack = nullptr;
15792 if (call_instruction->new_stack != nullptr) {15830 if (call_instruction->new_stack != nullptr) {
15793 ZigType *u8_ptr = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8,
15794 false, false, PtrLenUnknown, target_fn_align(ira->codegen->zig_target), 0, 0, false);
15795 ZigType *u8_slice = get_slice_type(ira->codegen, u8_ptr);
15796 IrInstruction *new_stack = call_instruction->new_stack->child;15831 IrInstruction *new_stack = call_instruction->new_stack->child;
15797 if (type_is_invalid(new_stack->value.type))15832 if (type_is_invalid(new_stack->value.type))
15798 return ira->codegen->invalid_instruction;15833 return ira->codegen->invalid_instruction;
1579915834
15800 casted_new_stack = ir_implicit_cast(ira, new_stack, u8_slice);15835 if (call_instruction->is_async_call_builtin &&
15801 if (type_is_invalid(casted_new_stack->value.type))15836 fn_entry != nullptr && new_stack->value.type->id == ZigTypeIdPointer &&
15802 return ira->codegen->invalid_instruction;15837 new_stack->value.type->data.pointer.child_type->id == ZigTypeIdFnFrame)
15838 {
15839 ZigType *needed_frame_type = get_pointer_to_type(ira->codegen,
15840 get_fn_frame_type(ira->codegen, fn_entry), false);
15841 casted_new_stack = ir_implicit_cast(ira, new_stack, needed_frame_type);
15842 if (type_is_invalid(casted_new_stack->value.type))
15843 return ira->codegen->invalid_instruction;
15844 } else {
15845 ZigType *u8_ptr = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8,
15846 false, false, PtrLenUnknown, target_fn_align(ira->codegen->zig_target), 0, 0, false);
15847 ZigType *u8_slice = get_slice_type(ira->codegen, u8_ptr);
15848 casted_new_stack = ir_implicit_cast(ira, new_stack, u8_slice);
15849 if (type_is_invalid(casted_new_stack->value.type))
15850 return ira->codegen->invalid_instruction;
15851 }
15803 }15852 }
1580415853
15805 if (fn_type->data.fn.is_generic) {15854 if (fn_type->data.fn.is_generic) {
...@@ -16010,7 +16059,11 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c...@@ -16010,7 +16059,11 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c
1601016059
16011 FnTypeId *impl_fn_type_id = &impl_fn->type_entry->data.fn.fn_type_id;16060 FnTypeId *impl_fn_type_id = &impl_fn->type_entry->data.fn.fn_type_id;
16012 IrInstruction *result_loc;16061 IrInstruction *result_loc;
16013 if (handle_is_ptr(impl_fn_type_id->return_type)) {16062 if (call_instruction->is_async_call_builtin) {
16063 result_loc = get_async_call_result_loc(ira, call_instruction, impl_fn_type_id->return_type);
16064 if (result_loc != nullptr && type_is_invalid(result_loc->value.type))
16065 return ira->codegen->invalid_instruction;
16066 } else if (handle_is_ptr(impl_fn_type_id->return_type)) {
16014 result_loc = ir_resolve_result(ira, &call_instruction->base, call_instruction->result_loc,16067 result_loc = ir_resolve_result(ira, &call_instruction->base, call_instruction->result_loc,
16015 impl_fn_type_id->return_type, nullptr, true, true, false);16068 impl_fn_type_id->return_type, nullptr, true, true, false);
16016 if (result_loc != nullptr) {16069 if (result_loc != nullptr) {
...@@ -16044,7 +16097,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c...@@ -16044,7 +16097,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c
1604416097
16045 IrInstructionCallGen *new_call_instruction = ir_build_call_gen(ira, &call_instruction->base,16098 IrInstructionCallGen *new_call_instruction = ir_build_call_gen(ira, &call_instruction->base,
16046 impl_fn, nullptr, impl_param_count, casted_args, fn_inline,16099 impl_fn, nullptr, impl_param_count, casted_args, fn_inline,
16047 false, casted_new_stack, result_loc,16100 false, casted_new_stack, call_instruction->is_async_call_builtin, result_loc,
16048 impl_fn_type_id->return_type);16101 impl_fn_type_id->return_type);
1604916102
16050 parent_fn_entry->call_list.append(new_call_instruction);16103 parent_fn_entry->call_list.append(new_call_instruction);
...@@ -16167,7 +16220,11 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c...@@ -16167,7 +16220,11 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c
16167 }16220 }
1616816221
16169 IrInstruction *result_loc;16222 IrInstruction *result_loc;
16170 if (handle_is_ptr(return_type)) {16223 if (call_instruction->is_async_call_builtin) {
16224 result_loc = get_async_call_result_loc(ira, call_instruction, return_type);
16225 if (result_loc != nullptr && type_is_invalid(result_loc->value.type))
16226 return ira->codegen->invalid_instruction;
16227 } else if (handle_is_ptr(return_type)) {
16171 result_loc = ir_resolve_result(ira, &call_instruction->base, call_instruction->result_loc,16228 result_loc = ir_resolve_result(ira, &call_instruction->base, call_instruction->result_loc,
16172 return_type, nullptr, true, true, false);16229 return_type, nullptr, true, true, false);
16173 if (result_loc != nullptr) {16230 if (result_loc != nullptr) {
...@@ -16185,7 +16242,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c...@@ -16185,7 +16242,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c
1618516242
16186 IrInstructionCallGen *new_call_instruction = ir_build_call_gen(ira, &call_instruction->base, fn_entry, fn_ref,16243 IrInstructionCallGen *new_call_instruction = ir_build_call_gen(ira, &call_instruction->base, fn_entry, fn_ref,
16187 call_param_count, casted_args, fn_inline, false, casted_new_stack,16244 call_param_count, casted_args, fn_inline, false, casted_new_stack,
16188 result_loc, return_type);16245 call_instruction->is_async_call_builtin, result_loc, return_type);
16189 parent_fn_entry->call_list.append(new_call_instruction);16246 parent_fn_entry->call_list.append(new_call_instruction);
16190 return ir_finish_anal(ira, &new_call_instruction->base);16247 return ir_finish_anal(ira, &new_call_instruction->base);
16191}16248}
test/compile_errors.zig+16
...@@ -2,6 +2,22 @@ const tests = @import("tests.zig");...@@ -2,6 +2,22 @@ const tests = @import("tests.zig");
2const builtin = @import("builtin");2const builtin = @import("builtin");
33
4pub fn addCases(cases: *tests.CompileErrorContext) void {4pub fn addCases(cases: *tests.CompileErrorContext) void {
5 cases.add(
6 "wrong type for result ptr to @asyncCall",
7 \\export fn entry() void {
8 \\ _ = async amain();
9 \\}
10 \\fn amain() i32 {
11 \\ var frame: @Frame(foo) = undefined;
12 \\ return await @asyncCall(&frame, false, foo);
13 \\}
14 \\fn foo() i32 {
15 \\ return 1234;
16 \\}
17 ,
18 "tmp.zig:6:37: error: expected type '*i32', found 'bool'",
19 );
20
5 cases.add(21 cases.add(
6 "struct depends on itself via optional field",22 "struct depends on itself via optional field",
7 \\const LhsExpr = struct {23 \\const LhsExpr = struct {
test/stage1/behavior/async_fn.zig+103-2
...@@ -331,8 +331,9 @@ test "async fn with inferred error set" {...@@ -331,8 +331,9 @@ test "async fn with inferred error set" {
331331
332 fn doTheTest() void {332 fn doTheTest() void {
333 var frame: [1]@Frame(middle) = undefined;333 var frame: [1]@Frame(middle) = undefined;
334 var result: anyerror!void = undefined;334 var fn_ptr = middle;
335 _ = @asyncCall(@sliceToBytes(frame[0..]), &result, middle);335 var result: @typeOf(fn_ptr).ReturnType.ErrorSet!void = undefined;
336 _ = @asyncCall(@sliceToBytes(frame[0..]), &result, fn_ptr);
336 resume global_frame;337 resume global_frame;
337 std.testing.expectError(error.Fail, result);338 std.testing.expectError(error.Fail, result);
338 }339 }
...@@ -819,6 +820,34 @@ test "struct parameter to async function is copied to the frame" {...@@ -819,6 +820,34 @@ test "struct parameter to async function is copied to the frame" {
819}820}
820821
821test "cast fn to async fn when it is inferred to be async" {822test "cast fn to async fn when it is inferred to be async" {
823 const S = struct {
824 var frame: anyframe = undefined;
825 var ok = false;
826
827 fn doTheTest() void {
828 var ptr: async fn () i32 = undefined;
829 ptr = func;
830 var buf: [100]u8 align(16) = undefined;
831 var result: i32 = undefined;
832 const f = @asyncCall(&buf, &result, ptr);
833 _ = await f;
834 expect(result == 1234);
835 ok = true;
836 }
837
838 fn func() i32 {
839 suspend {
840 frame = @frame();
841 }
842 return 1234;
843 }
844 };
845 _ = async S.doTheTest();
846 resume S.frame;
847 expect(S.ok);
848}
849
850test "cast fn to async fn when it is inferred to be async, awaited directly" {
822 const S = struct {851 const S = struct {
823 var frame: anyframe = undefined;852 var frame: anyframe = undefined;
824 var ok = false;853 var ok = false;
...@@ -919,3 +948,75 @@ fn recursiveAsyncFunctionTest(comptime suspending_implementation: bool) type {...@@ -919,3 +948,75 @@ fn recursiveAsyncFunctionTest(comptime suspending_implementation: bool) type {
919 }948 }
920 };949 };
921}950}
951
952test "@asyncCall with comptime-known function, but not awaited directly" {
953 const S = struct {
954 var global_frame: anyframe = undefined;
955
956 fn doTheTest() void {
957 var frame: [1]@Frame(middle) = undefined;
958 var result: @typeOf(middle).ReturnType.ErrorSet!void = undefined;
959 _ = @asyncCall(@sliceToBytes(frame[0..]), &result, middle);
960 resume global_frame;
961 std.testing.expectError(error.Fail, result);
962 }
963
964 async fn middle() !void {
965 var f = async middle2();
966 return await f;
967 }
968
969 fn middle2() !void {
970 return failing();
971 }
972
973 fn failing() !void {
974 global_frame = @frame();
975 suspend;
976 return error.Fail;
977 }
978 };
979 S.doTheTest();
980}
981
982test "@asyncCall with actual frame instead of byte buffer" {
983 const S = struct {
984 fn func() i32 {
985 suspend;
986 return 1234;
987 }
988 };
989 var frame: @Frame(S.func) = undefined;
990 var result: i32 = undefined;
991 const ptr = @asyncCall(&frame, &result, S.func);
992 resume ptr;
993 expect(result == 1234);
994}
995
996test "@asyncCall using the result location inside the frame" {
997 const S = struct {
998 async fn simple2(y: *i32) i32 {
999 defer y.* += 2;
1000 y.* += 1;
1001 suspend;
1002 return 1234;
1003 }
1004 fn getAnswer(f: anyframe->i32, out: *i32) void {
1005 var res = await f; // TODO https://github.com/ziglang/zig/issues/3077
1006 out.* = res;
1007 }
1008 };
1009 var data: i32 = 1;
1010 const Foo = struct {
1011 bar: async fn (*i32) i32,
1012 };
1013 var foo = Foo{ .bar = S.simple2 };
1014 var bytes: [64]u8 align(16) = undefined;
1015 const f = @asyncCall(&bytes, {}, foo.bar, &data);
1016 comptime expect(@typeOf(f) == anyframe->i32);
1017 expect(data == 2);
1018 resume f;
1019 expect(data == 4);
1020 _ = async S.getAnswer(f, &data);
1021 expect(data == 1234);
1022}