Commit 87710a1cc2
Changed files (8)
src/all_types.hpp
@@ -1503,6 +1503,7 @@ enum BuiltinFnId {
BuiltinFnIdInlineCall,
BuiltinFnIdNoInlineCall,
BuiltinFnIdNewStackCall,
+ BuiltinFnIdAsyncCall,
BuiltinFnIdTypeId,
BuiltinFnIdShlExact,
BuiltinFnIdShrExact,
@@ -1553,6 +1554,7 @@ enum PanicMsgId {
PanicMsgIdBadAwait,
PanicMsgIdBadReturn,
PanicMsgIdResumedAnAwaitingFn,
+ PanicMsgIdFrameTooSmall,
PanicMsgIdCount,
};
@@ -3699,6 +3701,7 @@ static const size_t maybe_null_index = 1;
static const size_t err_union_err_index = 0;
static const size_t err_union_payload_index = 1;
+// label (grep this): [coro_frame_struct_layout]
static const size_t coro_fn_ptr_index = 0;
static const size_t coro_awaiter_index = 1;
static const size_t coro_arg_start = 2;
src/analyze.cpp
@@ -5205,6 +5205,7 @@ static Error resolve_coro_frame(CodeGen *g, ZigType *frame_type) {
call->frame_result_loc = &alloca_gen->base;
}
+ // label (grep this): [coro_frame_struct_layout]
ZigList<ZigType *> field_types = {};
ZigList<const char *> field_names = {};
@@ -7525,6 +7526,7 @@ static void resolve_llvm_types_any_frame(CodeGen *g, ZigType *any_frame_type, Re
if (result_type == nullptr) {
g->anyframe_fn_type = ptr_result_type;
}
+ // label (grep this): [coro_frame_struct_layout]
LLVMTypeRef field_types[] = {
ptr_result_type, // fn_ptr
usize_type_ref, // awaiter
@@ -7558,6 +7560,7 @@ static void resolve_llvm_types_any_frame(CodeGen *g, ZigType *any_frame_type, Re
ZigLLVMReplaceTemporary(g->dbuilder, frame_header_di_type, replacement_di_type);
} else {
ZigType *ptr_result_type = get_pointer_to_type(g, result_type, false);
+ // label (grep this): [coro_frame_struct_layout]
LLVMTypeRef field_types[] = {
LLVMPointerType(fn_type, 0), // fn_ptr
usize_type_ref, // awaiter
src/codegen.cpp
@@ -879,6 +879,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) {
return buf_create_from_str("async function returned twice");
case PanicMsgIdResumedAnAwaitingFn:
return buf_create_from_str("awaiting function resumed");
+ case PanicMsgIdFrameTooSmall:
+ return buf_create_from_str("frame too small");
}
zig_unreachable();
}
@@ -3479,7 +3481,18 @@ static void render_async_var_decls(CodeGen *g, Scope *scope) {
}
}
+static LLVMValueRef gen_frame_size(CodeGen *g, LLVMValueRef fn_val) {
+ LLVMTypeRef usize_llvm_type = g->builtin_types.entry_usize->llvm_type;
+ LLVMTypeRef ptr_usize_llvm_type = LLVMPointerType(usize_llvm_type, 0);
+ LLVMValueRef casted_fn_val = LLVMBuildBitCast(g->builder, fn_val, ptr_usize_llvm_type, "");
+ LLVMValueRef negative_one = LLVMConstInt(LLVMInt32Type(), -1, true);
+ LLVMValueRef prefix_ptr = LLVMBuildInBoundsGEP(g->builder, casted_fn_val, &negative_one, 1, "");
+ return LLVMBuildLoad(g->builder, prefix_ptr, "");
+}
+
static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstructionCallGen *instruction) {
+ LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type;
+
LLVMValueRef fn_val;
ZigType *fn_type;
bool callee_is_async;
@@ -3511,34 +3524,54 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
LLVMValueRef awaiter_init_val;
LLVMValueRef ret_ptr;
if (instruction->is_async) {
- frame_result_loc = result_loc;
awaiter_init_val = zero;
- if (ret_has_bits) {
- ret_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, coro_arg_start + 1, "");
- }
- // Use the result location which is inside the frame if this is an async call.
- if (ret_has_bits) {
- LLVMValueRef ret_ptr_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, coro_arg_start, "");
- LLVMBuildStore(g->builder, ret_ptr, ret_ptr_ptr);
+ if (instruction->new_stack == nullptr) {
+ frame_result_loc = result_loc;
+
+ if (ret_has_bits) {
+ // Use the result location which is inside the frame if this is an async call.
+ ret_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, coro_arg_start + 1, "");
+ }
+ } else {
+ LLVMValueRef frame_slice_ptr = ir_llvm_value(g, instruction->new_stack);
+ if (ir_want_runtime_safety(g, &instruction->base)) {
+ LLVMValueRef given_len_ptr = LLVMBuildStructGEP(g->builder, frame_slice_ptr, slice_len_index, "");
+ LLVMValueRef given_frame_len = LLVMBuildLoad(g->builder, given_len_ptr, "");
+ LLVMValueRef actual_frame_len = gen_frame_size(g, fn_val);
+
+ LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "FrameSizeCheckFail");
+ LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "FrameSizeCheckOk");
+
+ LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntUGE, given_frame_len, actual_frame_len, "");
+ LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
+
+ LLVMPositionBuilderAtEnd(g->builder, fail_block);
+ gen_safety_crash(g, PanicMsgIdFrameTooSmall);
+
+ LLVMPositionBuilderAtEnd(g->builder, ok_block);
+ }
+ LLVMValueRef frame_ptr_ptr = LLVMBuildStructGEP(g->builder, frame_slice_ptr, slice_ptr_index, "");
+ LLVMValueRef frame_ptr = LLVMBuildLoad(g->builder, frame_ptr_ptr, "");
+ frame_result_loc = LLVMBuildBitCast(g->builder, frame_ptr,
+ get_llvm_type(g, instruction->base.value.type), "");
+
+ if (ret_has_bits) {
+ // Use the result location provided to the @asyncCall builtin
+ ret_ptr = result_loc;
+ }
}
} else if (callee_is_async) {
frame_result_loc = ir_llvm_value(g, instruction->frame_result_loc);
awaiter_init_val = LLVMBuildPtrToInt(g->builder, g->cur_ret_ptr,
g->builtin_types.entry_usize->llvm_type, ""); // caller's own frame pointer
if (ret_has_bits) {
+ // Use the call instruction's result location.
ret_ptr = result_loc;
}
-
- // Use the call instruction's result location.
- if (ret_has_bits) {
- LLVMValueRef ret_ptr_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, coro_arg_start, "");
- LLVMBuildStore(g->builder, result_loc, ret_ptr_ptr);
- }
}
if (instruction->is_async || callee_is_async) {
assert(frame_result_loc != nullptr);
- assert(instruction->fn_entry != nullptr);
if (prefix_arg_err_ret_stack) {
zig_panic("TODO");
@@ -3547,6 +3580,10 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
LLVMValueRef awaiter_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, coro_awaiter_index, "");
LLVMBuildStore(g->builder, awaiter_init_val, awaiter_ptr);
+ if (ret_has_bits) {
+ LLVMValueRef ret_ptr_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, coro_arg_start, "");
+ LLVMBuildStore(g->builder, ret_ptr, ret_ptr_ptr);
+ }
}
if (!instruction->is_async && !callee_is_async) {
if (first_arg_ret) {
@@ -3581,16 +3618,37 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
if (instruction->is_async || callee_is_async) {
size_t ret_2_or_0 = type_has_bits(fn_type->data.fn.fn_type_id.return_type) ? 2 : 0;
+ size_t arg_start_i = coro_arg_start + ret_2_or_0;
+
+ LLVMValueRef casted_frame;
+ if (instruction->new_stack != nullptr) {
+ // We need the frame type to be a pointer to a struct that includes the args
+ // label (grep this): [coro_frame_struct_layout]
+ size_t field_count = arg_start_i + gen_param_values.length;
+ LLVMTypeRef *field_types = allocate_nonzero<LLVMTypeRef>(field_count);
+ LLVMGetStructElementTypes(LLVMGetElementType(LLVMTypeOf(frame_result_loc)), field_types);
+ for (size_t arg_i = 0; arg_i < gen_param_values.length; arg_i += 1) {
+ field_types[arg_start_i + arg_i] = LLVMTypeOf(gen_param_values.at(arg_i));
+ }
+ LLVMTypeRef frame_with_args_type = LLVMStructType(field_types, field_count, false);
+ LLVMTypeRef ptr_frame_with_args_type = LLVMPointerType(frame_with_args_type, 0);
+
+ casted_frame = LLVMBuildBitCast(g->builder, frame_result_loc, ptr_frame_with_args_type, "");
+ } else {
+ casted_frame = frame_result_loc;
+ }
+
for (size_t arg_i = 0; arg_i < gen_param_values.length; arg_i += 1) {
- LLVMValueRef arg_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc,
- coro_arg_start + ret_2_or_0 + arg_i, "");
+ LLVMValueRef arg_ptr = LLVMBuildStructGEP(g->builder, casted_frame, arg_start_i + arg_i, "");
LLVMBuildStore(g->builder, gen_param_values.at(arg_i), arg_ptr);
}
}
- LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type;
if (instruction->is_async) {
LLVMValueRef args[] = {frame_result_loc, LLVMGetUndef(usize_type_ref)};
ZigLLVMBuildCall(g->builder, fn_val, args, 2, llvm_cc, fn_inline, "");
+ if (instruction->new_stack != nullptr) {
+ return frame_result_loc;
+ }
return nullptr;
} else if (callee_is_async) {
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,
static LLVMValueRef ir_render_frame_size(CodeGen *g, IrExecutable *executable,
IrInstructionFrameSizeGen *instruction)
{
- LLVMTypeRef usize_llvm_type = g->builtin_types.entry_usize->llvm_type;
- LLVMTypeRef ptr_usize_llvm_type = LLVMPointerType(usize_llvm_type, 0);
LLVMValueRef fn_val = ir_llvm_value(g, instruction->fn);
- LLVMValueRef casted_fn_val = LLVMBuildBitCast(g->builder, fn_val, ptr_usize_llvm_type, "");
- LLVMValueRef negative_one = LLVMConstInt(LLVMInt32Type(), -1, true);
- LLVMValueRef prefix_ptr = LLVMBuildInBoundsGEP(g->builder, casted_fn_val, &negative_one, 1, "");
- return LLVMBuildLoad(g->builder, prefix_ptr, "");
+ return gen_frame_size(g, fn_val);
}
static void set_debug_location(CodeGen *g, IrInstruction *instruction) {
@@ -7097,13 +7150,13 @@ static void define_builtin_fns(CodeGen *g) {
create_builtin_fn(g, BuiltinFnIdFloor, "floor", 2);
create_builtin_fn(g, BuiltinFnIdCeil, "ceil", 2);
create_builtin_fn(g, BuiltinFnIdTrunc, "trunc", 2);
- //Needs library support on Windows
- //create_builtin_fn(g, BuiltinFnIdNearbyInt, "nearbyInt", 2);
+ create_builtin_fn(g, BuiltinFnIdNearbyInt, "nearbyInt", 2);
create_builtin_fn(g, BuiltinFnIdRound, "round", 2);
create_builtin_fn(g, BuiltinFnIdMulAdd, "mulAdd", 4);
create_builtin_fn(g, BuiltinFnIdInlineCall, "inlineCall", SIZE_MAX);
create_builtin_fn(g, BuiltinFnIdNoInlineCall, "noInlineCall", SIZE_MAX);
create_builtin_fn(g, BuiltinFnIdNewStackCall, "newStackCall", SIZE_MAX);
+ create_builtin_fn(g, BuiltinFnIdAsyncCall, "asyncCall", SIZE_MAX);
create_builtin_fn(g, BuiltinFnIdTypeId, "typeId", 1);
create_builtin_fn(g, BuiltinFnIdShlExact, "shlExact", 2);
create_builtin_fn(g, BuiltinFnIdShrExact, "shrExact", 2);
src/ir.cpp
@@ -1402,6 +1402,10 @@ static IrInstruction *ir_build_call_src(IrBuilder *irb, Scope *scope, AstNode *s
if (fn_ref != nullptr) ir_ref_instruction(fn_ref, irb->current_basic_block);
for (size_t i = 0; i < arg_count; i += 1)
ir_ref_instruction(args[i], irb->current_basic_block);
+ if (is_async && new_stack != nullptr) {
+ // in this case the arg at the end is the return pointer
+ ir_ref_instruction(args[arg_count], irb->current_basic_block);
+ }
if (new_stack != nullptr) ir_ref_instruction(new_stack, irb->current_basic_block);
return &call_instruction->base;
@@ -5203,8 +5207,10 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
}
case BuiltinFnIdNewStackCall:
{
- if (node->data.fn_call_expr.params.length == 0) {
- add_node_error(irb->codegen, node, buf_sprintf("expected at least 1 argument, found 0"));
+ if (node->data.fn_call_expr.params.length < 2) {
+ add_node_error(irb->codegen, node,
+ buf_sprintf("expected at least 2 arguments, found %" ZIG_PRI_usize,
+ node->data.fn_call_expr.params.length));
return irb->codegen->invalid_instruction;
}
@@ -5232,6 +5238,50 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
FnInlineAuto, false, new_stack, result_loc);
return ir_lval_wrap(irb, scope, call, lval, result_loc);
}
+ case BuiltinFnIdAsyncCall:
+ {
+ size_t arg_offset = 3;
+ if (node->data.fn_call_expr.params.length < arg_offset) {
+ add_node_error(irb->codegen, node,
+ buf_sprintf("expected at least %" ZIG_PRI_usize " arguments, found %" ZIG_PRI_usize,
+ arg_offset, node->data.fn_call_expr.params.length));
+ return irb->codegen->invalid_instruction;
+ }
+
+ AstNode *bytes_node = node->data.fn_call_expr.params.at(0);
+ IrInstruction *bytes = ir_gen_node(irb, bytes_node, scope);
+ if (bytes == irb->codegen->invalid_instruction)
+ return bytes;
+
+ AstNode *ret_ptr_node = node->data.fn_call_expr.params.at(1);
+ IrInstruction *ret_ptr = ir_gen_node(irb, ret_ptr_node, scope);
+ if (ret_ptr == irb->codegen->invalid_instruction)
+ return ret_ptr;
+
+ AstNode *fn_ref_node = node->data.fn_call_expr.params.at(2);
+ IrInstruction *fn_ref = ir_gen_node(irb, fn_ref_node, scope);
+ if (fn_ref == irb->codegen->invalid_instruction)
+ return fn_ref;
+
+ size_t arg_count = node->data.fn_call_expr.params.length - arg_offset;
+
+ // last "arg" is return pointer
+ IrInstruction **args = allocate<IrInstruction*>(arg_count + 1);
+
+ for (size_t i = 0; i < arg_count; i += 1) {
+ AstNode *arg_node = node->data.fn_call_expr.params.at(i + arg_offset);
+ IrInstruction *arg = ir_gen_node(irb, arg_node, scope);
+ if (arg == irb->codegen->invalid_instruction)
+ return arg;
+ args[i] = arg;
+ }
+
+ args[arg_count] = ret_ptr;
+
+ IrInstruction *call = ir_build_call_src(irb, scope, node, nullptr, fn_ref, arg_count, args, false,
+ FnInlineAuto, true, bytes, result_loc);
+ return ir_lval_wrap(irb, scope, call, lval, result_loc);
+ }
case BuiltinFnIdTypeId:
{
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
}
static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCallSrc *call_instruction, ZigFn *fn_entry,
- ZigType *fn_type, IrInstruction *fn_ref, IrInstruction **casted_args, size_t arg_count)
+ ZigType *fn_type, IrInstruction *fn_ref, IrInstruction **casted_args, size_t arg_count,
+ IrInstruction *casted_new_stack)
{
if (fn_entry == nullptr) {
- ir_add_error(ira, fn_ref, buf_sprintf("function is not comptime-known; @asyncCall required"));
- return ira->codegen->invalid_instruction;
+ if (call_instruction->new_stack == nullptr) {
+ ir_add_error(ira, fn_ref, buf_sprintf("function is not comptime-known; @asyncCall required"));
+ return ira->codegen->invalid_instruction;
+ }
+ // this is an @asyncCall
+
+ if (fn_type->data.fn.fn_type_id.cc != CallingConventionAsync) {
+ ir_add_error(ira, fn_ref,
+ buf_sprintf("expected async function, found '%s'", buf_ptr(&fn_type->name)));
+ return ira->codegen->invalid_instruction;
+ }
+
+ IrInstruction *ret_ptr = call_instruction->args[call_instruction->arg_count]->child;
+ if (type_is_invalid(ret_ptr->value.type))
+ return ira->codegen->invalid_instruction;
+
+ ZigType *anyframe_type = get_any_frame_type(ira->codegen, fn_type->data.fn.fn_type_id.return_type);
+
+ IrInstructionCallGen *call_gen = ir_build_call_gen(ira, &call_instruction->base, nullptr, fn_ref,
+ arg_count, casted_args, FnInlineAuto, true, casted_new_stack, ret_ptr, anyframe_type);
+ return &call_gen->base;
}
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
size_t impl_param_count = impl_fn_type_id->param_count;
if (call_instruction->is_async) {
- zig_panic("TODO async call");
+ IrInstruction *result = ir_analyze_async_call(ira, call_instruction, impl_fn, impl_fn->type_entry,
+ nullptr, casted_args, call_param_count, casted_new_stack);
+ return ir_finish_anal(ira, result);
}
- if (!call_instruction->is_async) {
- if (impl_fn_type_id->cc == CallingConventionAsync && parent_fn_entry->inferred_async_node == nullptr) {
- parent_fn_entry->inferred_async_node = fn_ref->source_node;
- }
+ if (impl_fn_type_id->cc == CallingConventionAsync && parent_fn_entry->inferred_async_node == nullptr) {
+ parent_fn_entry->inferred_async_node = fn_ref->source_node;
}
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
return ira->codegen->invalid_instruction;
}
- if (!call_instruction->is_async) {
- if (fn_type_id->cc == CallingConventionAsync && parent_fn_entry->inferred_async_node == nullptr) {
- parent_fn_entry->inferred_async_node = fn_ref->source_node;
- }
- }
-
if (call_instruction->is_async) {
IrInstruction *result = ir_analyze_async_call(ira, call_instruction, fn_entry, fn_type, fn_ref,
- casted_args, call_param_count);
+ casted_args, call_param_count, casted_new_stack);
return ir_finish_anal(ira, result);
}
+ if (fn_type_id->cc == CallingConventionAsync && parent_fn_entry->inferred_async_node == nullptr) {
+ parent_fn_entry->inferred_async_node = fn_ref->source_node;
+ }
+
IrInstruction *result_loc;
if (handle_is_ptr(return_type)) {
result_loc = ir_resolve_result(ira, &call_instruction->base, call_instruction->result_loc,
test/stage1/behavior/coroutines.zig
@@ -260,22 +260,42 @@ test "async function with dot syntax" {
expect(S.y == 2);
}
-//test "async fn pointer in a struct field" {
-// var data: i32 = 1;
-// const Foo = struct {
-// bar: async fn (*i32) void,
-// };
-// var foo = Foo{ .bar = simpleAsyncFn2 };
-// const p = async foo.bar(&data);
-// expect(data == 2);
-// resume p;
-// expect(data == 4);
-//}
-//async fn simpleAsyncFn2(y: *i32) void {
-// defer y.* += 2;
-// y.* += 1;
-// suspend;
-//}
+test "async fn pointer in a struct field" {
+ var data: i32 = 1;
+ const Foo = struct {
+ bar: async fn (*i32) void,
+ };
+ var foo = Foo{ .bar = simpleAsyncFn2 };
+ var bytes: [64]u8 = undefined;
+ const p = @asyncCall(&bytes, {}, foo.bar, &data);
+ comptime expect(@typeOf(p) == anyframe->void);
+ expect(data == 2);
+ resume p;
+ expect(data == 4);
+}
+async fn simpleAsyncFn2(y: *i32) void {
+ defer y.* += 2;
+ y.* += 1;
+ suspend;
+}
+
+test "@asyncCall with return type" {
+ const Foo = struct {
+ bar: async fn () i32,
+
+ async fn afunc() i32 {
+ suspend;
+ return 1234;
+ }
+ };
+ var foo = Foo{ .bar = Foo.afunc };
+ var bytes: [64]u8 = undefined;
+ var aresult: i32 = 0;
+ const frame = @asyncCall(&bytes, &aresult, foo.bar);
+ expect(aresult == 0);
+ resume frame;
+ expect(aresult == 1234);
+}
//test "async fn with inferred error set" {
// const p = async failing();
test/compile_errors.zig
@@ -2,6 +2,18 @@ const tests = @import("tests.zig");
const builtin = @import("builtin");
pub fn addCases(cases: *tests.CompileErrorContext) void {
+ cases.add(
+ "non async function pointer passed to @asyncCall",
+ \\export fn entry() void {
+ \\ var ptr = afunc;
+ \\ var bytes: [100]u8 = undefined;
+ \\ _ = @asyncCall(&bytes, {}, ptr);
+ \\}
+ \\fn afunc() void { }
+ ,
+ "tmp.zig:4:32: error: expected async function, found 'fn() void'",
+ );
+
cases.add(
"runtime-known async function called",
\\export fn entry() void {
test/runtime_safety.zig
@@ -1,6 +1,20 @@
const tests = @import("tests.zig");
pub fn addCases(cases: *tests.CompareOutputContext) void {
+ cases.addRuntimeSafety("@asyncCall with too small a frame",
+ \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
+ \\ @import("std").os.exit(126);
+ \\}
+ \\pub fn main() void {
+ \\ var bytes: [1]u8 = undefined;
+ \\ var ptr = other;
+ \\ var frame = @asyncCall(&bytes, {}, ptr);
+ \\}
+ \\async fn other() void {
+ \\ suspend;
+ \\}
+ );
+
cases.addRuntimeSafety("resuming a function which is awaiting a frame",
\\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
\\ @import("std").os.exit(126);
@@ -17,6 +31,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
\\ suspend;
\\}
);
+
cases.addRuntimeSafety("resuming a function which is awaiting a call",
\\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
\\ @import("std").os.exit(126);
BRANCH_TODO
@@ -1,9 +1,8 @@
+ * @asyncCall with an async function pointer
* compile error for error: expected anyframe->T, found 'anyframe'
* compile error for error: expected anyframe->T, found 'i32'
* await of a non async function
- * await in single-threaded mode
* async call on a non async function
- * @asyncCall with an async function pointer
* cancel
* defer and errdefer
* safety for double await
@@ -21,3 +20,16 @@
* compile error for copying a frame
* compile error for resuming a const frame pointer
* runtime safety enabling/disabling scope has to be coordinated across resume/await/calls/return
+ * await in single-threaded mode
+ * calling a generic function which is async
+ * make sure `await @asyncCall` and `await async` are handled correctly.
+ * allow @asyncCall with a real @Frame(func) (the point of this is result pointer)
+ * documentation
+ - @asyncCall
+ - @frame
+ - @Frame
+ - @frameSize
+ - coroutines section
+ - suspend
+ - resume
+ - anyframe, anyframe->T