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 {
27192719 IrInstruction *new_stack;
27202720 FnInline fn_inline;
27212721 bool is_async;
2722 bool is_async_call_builtin;
27222723 bool is_comptime;
27232724};
27242725
......@@ -2735,6 +2736,7 @@ struct IrInstructionCallGen {
27352736 IrInstruction *new_stack;
27362737 FnInline fn_inline;
27372738 bool is_async;
2739 bool is_async_call_builtin;
27382740};
27392741
27402742struct IrInstructionConst {
src/analyze.cpp+4
......@@ -5727,6 +5727,10 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) {
57275727
57285728 for (size_t i = 0; i < fn->call_list.length; i += 1) {
57295729 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 }
57305734 ZigFn *callee = call->fn_entry;
57315735 if (callee == nullptr) {
57325736 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
38263826 LLVMValueRef awaiter_init_val;
38273827 LLVMValueRef ret_ptr;
38283828 if (callee_is_async) {
3829 if (instruction->is_async) {
3830 if (instruction->new_stack == nullptr) {
3831 awaiter_init_val = zero;
3829 if (instruction->new_stack == nullptr) {
3830 if (instruction->is_async) {
38323831 frame_result_loc = result_loc;
3833
3834 if (ret_has_bits) {
3835 // Use the result location which is inside the frame if this is an async call.
3836 ret_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, frame_ret_start + 2, "");
3837 }
3838 } else if (cc == CallingConventionAsync) {
3839 awaiter_init_val = zero;
3832 } else {
3833 frame_result_loc = ir_llvm_value(g, instruction->frame_result_loc);
3834 }
3835 } else {
3836 if (instruction->new_stack->value.type->id == ZigTypeIdPointer &&
3837 instruction->new_stack->value.type->data.pointer.child_type->id == ZigTypeIdFnFrame)
3838 {
3839 frame_result_loc = ir_llvm_value(g, instruction->new_stack);
3840 } else {
38403841 LLVMValueRef frame_slice_ptr = ir_llvm_value(g, instruction->new_stack);
38413842 if (ir_want_runtime_safety(g, &instruction->base)) {
38423843 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
38563857 }
38573858 LLVMValueRef frame_ptr_ptr = LLVMBuildStructGEP(g->builder, frame_slice_ptr, slice_ptr_index, "");
38583859 LLVMValueRef frame_ptr = LLVMBuildLoad(g->builder, frame_ptr_ptr, "");
3859 frame_result_loc = LLVMBuildBitCast(g->builder, frame_ptr,
3860 get_llvm_type(g, instruction->base.value.type), "");
3860 if (instruction->fn_entry == nullptr) {
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
38623875 if (ret_has_bits) {
3863 // Use the result location provided to the @asyncCall builtin
3864 ret_ptr = result_loc;
3876 // Use the result location which is inside the frame if this is an async call.
3877 ret_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, frame_ret_start + 2, "");
38653878 }
38663879 } 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 }
38683891 }
38693892
38703893 // 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
38723895 } else {
38733896 // async function called as a normal function
38743897
3875 frame_result_loc = ir_llvm_value(g, instruction->frame_result_loc);
38763898 awaiter_init_val = LLVMBuildPtrToInt(g->builder, g->cur_frame_ptr, usize_type_ref, ""); // caller's own frame pointer
38773899 if (ret_has_bits) {
38783900 if (result_loc == nullptr) {
......@@ -3988,7 +4010,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
39884010 uint32_t arg_start_i = frame_index_arg(g, fn_type->data.fn.fn_type_id.return_type);
39894011
39904012 LLVMValueRef casted_frame;
3991 if (instruction->new_stack != nullptr) {
4013 if (instruction->new_stack != nullptr && instruction->fn_entry == nullptr) {
39924014 // We need the frame type to be a pointer to a struct that includes the args
39934015 size_t field_count = arg_start_i + gen_param_values.length;
39944016 LLVMTypeRef *field_types = allocate_nonzero<LLVMTypeRef>(field_count);
......@@ -4014,7 +4036,8 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
40144036 if (instruction->is_async) {
40154037 gen_resume(g, fn_val, frame_result_loc, ResumeIdCall);
40164038 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), "");
40184041 }
40194042 return nullptr;
40204043 } else {
......@@ -4041,7 +4064,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
40414064 }
40424065 }
40434066
4044 if (instruction->new_stack == nullptr) {
4067 if (instruction->new_stack == nullptr || instruction->is_async_call_builtin) {
40454068 result = ZigLLVMBuildCall(g->builder, fn_val,
40464069 gen_param_values.items, (unsigned)gen_param_values.length, llvm_cc, fn_inline, "");
40474070 } 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
13821382
13831383static IrInstruction *ir_build_call_src(IrBuilder *irb, Scope *scope, AstNode *source_node,
13841384 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,
13861386 IrInstruction *new_stack, ResultLoc *result_loc)
13871387{
13881388 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
13931393 call_instruction->args = args;
13941394 call_instruction->arg_count = arg_count;
13951395 call_instruction->is_async = is_async;
1396 call_instruction->is_async_call_builtin = is_async_call_builtin;
13961397 call_instruction->new_stack = new_stack;
13971398 call_instruction->result_loc = result_loc;
13981399
......@@ -1410,7 +1411,7 @@ static IrInstruction *ir_build_call_src(IrBuilder *irb, Scope *scope, AstNode *s
14101411
14111412static IrInstructionCallGen *ir_build_call_gen(IrAnalyze *ira, IrInstruction *source_instruction,
14121413 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,
14141415 IrInstruction *result_loc, ZigType *return_type)
14151416{
14161417 IrInstructionCallGen *call_instruction = ir_build_instruction<IrInstructionCallGen>(&ira->new_irb,
......@@ -1422,6 +1423,7 @@ static IrInstructionCallGen *ir_build_call_gen(IrAnalyze *ira, IrInstruction *so
14221423 call_instruction->args = args;
14231424 call_instruction->arg_count = arg_count;
14241425 call_instruction->is_async = is_async;
1426 call_instruction->is_async_call_builtin = is_async_call_builtin;
14251427 call_instruction->new_stack = new_stack;
14261428 call_instruction->result_loc = result_loc;
14271429
......@@ -4351,6 +4353,54 @@ static IrInstruction *ir_gen_this(IrBuilder *irb, Scope *orig_scope, AstNode *no
43514353 zig_unreachable();
43524354}
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
43544404static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval,
43554405 ResultLoc *result_loc)
43564406{
......@@ -4360,7 +4410,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
43604410 Buf *name = fn_ref_expr->data.symbol_expr.symbol;
43614411 auto entry = irb->codegen->builtin_fn_table.maybe_get(name);
43624412
4363 if (!entry) { // new built in not found
4413 if (!entry) {
43644414 add_node_error(irb->codegen, node,
43654415 buf_sprintf("invalid builtin function: '%s'", buf_ptr(name)));
43664416 return irb->codegen->invalid_instruction;
......@@ -5224,7 +5274,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
52245274 FnInline fn_inline = (builtin_fn->id == BuiltinFnIdInlineCall) ? FnInlineAlways : FnInlineNever;
52255275
52265276 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);
52285278 return ir_lval_wrap(irb, scope, call, lval, result_loc);
52295279 }
52305280 case BuiltinFnIdNewStackCall:
......@@ -5257,53 +5307,11 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
52575307 }
52585308
52595309 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);
52615311 return ir_lval_wrap(irb, scope, call, lval, result_loc);
52625312 }
52635313 case BuiltinFnIdAsyncCall:
5264 {
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 }
5314 return ir_gen_async_call(irb, scope, nullptr, node, lval, result_loc);
53075315 case BuiltinFnIdTypeId:
53085316 {
53095317 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
56075615
56085616 bool is_async = node->data.fn_call_expr.is_async;
56095617 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);
56115619 return ir_lval_wrap(irb, scope, fn_call, lval, result_loc);
56125620}
56135621
......@@ -7900,6 +7908,19 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n
79007908{
79017909 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
79037924 ZigFn *fn_entry = exec_fn_entry(irb->exec);
79047925 if (!fn_entry) {
79057926 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
79157936 return irb->codegen->invalid_instruction;
79167937 }
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);
79197940 if (target_inst == irb->codegen->invalid_instruction)
79207941 return irb->codegen->invalid_instruction;
79217942
......@@ -15244,44 +15265,61 @@ static IrInstruction *ir_analyze_instruction_reset_result(IrAnalyze *ira, IrInst
1524415265 return ir_const_void(ira, &instruction->base);
1524515266}
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
1524715282static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCallSrc *call_instruction, ZigFn *fn_entry,
1524815283 ZigType *fn_type, IrInstruction *fn_ref, IrInstruction **casted_args, size_t arg_count,
1524915284 IrInstruction *casted_new_stack)
1525015285{
15251 if (casted_new_stack != nullptr) {
15252 // this is an @asyncCall
15253
15286 if (fn_entry == nullptr) {
1525415287 if (fn_type->data.fn.fn_type_id.cc != CallingConventionAsync) {
1525515288 ir_add_error(ira, fn_ref,
1525615289 buf_sprintf("expected async function, found '%s'", buf_ptr(&fn_type->name)));
1525715290 return ira->codegen->invalid_instruction;
1525815291 }
15259
15260 IrInstruction *ret_ptr = call_instruction->args[call_instruction->arg_count]->child;
15261 if (type_is_invalid(ret_ptr->value.type))
15292 if (casted_new_stack == nullptr) {
15293 ir_add_error(ira, fn_ref, buf_sprintf("function is not comptime-known; @asyncCall required"));
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))
1526215301 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,
15267 arg_count, casted_args, FnInlineAuto, true, casted_new_stack, ret_ptr, anyframe_type);
15305 IrInstructionCallGen *call_gen = ir_build_call_gen(ira, &call_instruction->base, fn_entry, fn_ref,
15306 arg_count, casted_args, FnInlineAuto, true, casted_new_stack,
15307 call_instruction->is_async_call_builtin, ret_ptr, anyframe_type);
1526815308 return &call_gen->base;
15269 } else if (fn_entry == nullptr) {
15270 ir_add_error(ira, fn_ref, buf_sprintf("function is not comptime-known; @asyncCall required"));
15271 return ira->codegen->invalid_instruction;
15272 }
15273
15274 ZigType *frame_type = get_fn_frame_type(ira->codegen, fn_entry);
15275 IrInstruction *result_loc = ir_resolve_result(ira, &call_instruction->base, call_instruction->result_loc,
15276 frame_type, nullptr, true, true, false);
15277 if (type_is_invalid(result_loc->value.type) || instr_is_unreachable(result_loc)) {
15278 return result_loc;
15309 } else {
15310 ZigType *frame_type = get_fn_frame_type(ira->codegen, fn_entry);
15311 IrInstruction *result_loc = ir_resolve_result(ira, &call_instruction->base, call_instruction->result_loc,
15312 frame_type, nullptr, true, true, false);
15313 if (type_is_invalid(result_loc->value.type) || instr_is_unreachable(result_loc)) {
15314 return result_loc;
15315 }
15316 result_loc = ir_implicit_cast(ira, result_loc, get_pointer_to_type(ira->codegen, frame_type, false));
15317 if (type_is_invalid(result_loc->value.type))
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;
1527915322 }
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;
1528515323}
1528615324static bool ir_analyze_fn_call_inline_arg(IrAnalyze *ira, AstNode *fn_proto_node,
1528715325 IrInstruction *arg, Scope **exec_scope, size_t *next_proto_i)
......@@ -15790,16 +15828,27 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c
1579015828
1579115829 IrInstruction *casted_new_stack = nullptr;
1579215830 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);
1579615831 IrInstruction *new_stack = call_instruction->new_stack->child;
1579715832 if (type_is_invalid(new_stack->value.type))
1579815833 return ira->codegen->invalid_instruction;
1579915834
15800 casted_new_stack = ir_implicit_cast(ira, new_stack, u8_slice);
15801 if (type_is_invalid(casted_new_stack->value.type))
15802 return ira->codegen->invalid_instruction;
15835 if (call_instruction->is_async_call_builtin &&
15836 fn_entry != nullptr && new_stack->value.type->id == ZigTypeIdPointer &&
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 }
1580315852 }
1580415853
1580515854 if (fn_type->data.fn.is_generic) {
......@@ -16010,7 +16059,11 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c
1601016059
1601116060 FnTypeId *impl_fn_type_id = &impl_fn->type_entry->data.fn.fn_type_id;
1601216061 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)) {
1601416067 result_loc = ir_resolve_result(ira, &call_instruction->base, call_instruction->result_loc,
1601516068 impl_fn_type_id->return_type, nullptr, true, true, false);
1601616069 if (result_loc != nullptr) {
......@@ -16044,7 +16097,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c
1604416097
1604516098 IrInstructionCallGen *new_call_instruction = ir_build_call_gen(ira, &call_instruction->base,
1604616099 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,
1604816101 impl_fn_type_id->return_type);
1604916102
1605016103 parent_fn_entry->call_list.append(new_call_instruction);
......@@ -16167,7 +16220,11 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c
1616716220 }
1616816221
1616916222 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)) {
1617116228 result_loc = ir_resolve_result(ira, &call_instruction->base, call_instruction->result_loc,
1617216229 return_type, nullptr, true, true, false);
1617316230 if (result_loc != nullptr) {
......@@ -16185,7 +16242,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c
1618516242
1618616243 IrInstructionCallGen *new_call_instruction = ir_build_call_gen(ira, &call_instruction->base, fn_entry, fn_ref,
1618716244 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);
1618916246 parent_fn_entry->call_list.append(new_call_instruction);
1619016247 return ir_finish_anal(ira, &new_call_instruction->base);
1619116248}
test/compile_errors.zig+16
......@@ -2,6 +2,22 @@ const tests = @import("tests.zig");
22const builtin = @import("builtin");
33
44pub 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
521 cases.add(
622 "struct depends on itself via optional field",
723 \\const LhsExpr = struct {
test/stage1/behavior/async_fn.zig+103-2
......@@ -331,8 +331,9 @@ test "async fn with inferred error set" {
331331
332332 fn doTheTest() void {
333333 var frame: [1]@Frame(middle) = undefined;
334 var result: anyerror!void = undefined;
335 _ = @asyncCall(@sliceToBytes(frame[0..]), &result, middle);
334 var fn_ptr = middle;
335 var result: @typeOf(fn_ptr).ReturnType.ErrorSet!void = undefined;
336 _ = @asyncCall(@sliceToBytes(frame[0..]), &result, fn_ptr);
336337 resume global_frame;
337338 std.testing.expectError(error.Fail, result);
338339 }
......@@ -819,6 +820,34 @@ test "struct parameter to async function is copied to the frame" {
819820}
820821
821822test "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" {
822851 const S = struct {
823852 var frame: anyframe = undefined;
824853 var ok = false;
......@@ -919,3 +948,75 @@ fn recursiveAsyncFunctionTest(comptime suspending_implementation: bool) type {
919948 }
920949 };
921950}
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}