remove async and await keywords

Also remove `@frameSize`, closing #3654.

While the other machinery might remain depending on #23446, it is
settled that there will not be `async`/ `await` keywords in the
language.
This commit is contained in:
Andrew Kelley
2025-07-07 11:22:28 -07:00
parent 31e46be743
commit 40d11cc25a
48 changed files with 24 additions and 2913 deletions
+6 -40
View File
@@ -4279,16 +4279,9 @@ pub fn print(self: *Writer, arg0: []const u8, arg1: i32) !void {
{#header_close#}
{#header_open|Async Functions#}
<p>Async functions regressed with the release of 0.11.0. Their future in
the Zig language is unclear due to multiple unsolved problems:</p>
<ul>
<li>LLVM's lack of ability to optimize them.</li>
<li>Third-party debuggers' lack of ability to debug them.</li>
<li><a href="https://github.com/ziglang/zig/issues/5913">The cancellation problem</a>.</li>
<li>Async function pointers preventing the stack size from being known.</li>
</ul>
<p>These problems are surmountable, but it will take time. The Zig team
is currently focused on other priorities.</p>
<p>Async functions regressed with the release of 0.11.0. The current plan is to
reintroduce them as a lower level primitive that powers I/O implementations.</p>
<p>Tracking issue: <a href="https://github.com/ziglang/zig/issues/23446">Proposal: stackless coroutines as low-level primitives</a></p>
{#header_close#}
{#header_open|Builtin Functions|2col#}
@@ -7372,29 +7365,6 @@ fn readU32Be() u32 {}
</ul>
</td>
</tr>
<tr>
<th scope="row">
<pre>{#syntax#}async{#endsyntax#}</pre>
</th>
<td>
{#syntax#}async{#endsyntax#} can be used before a function call to get a pointer to the function's frame when it suspends.
<ul>
<li>See also {#link|Async Functions#}</li>
</ul>
</td>
</tr>
<tr>
<th scope="row">
<pre>{#syntax#}await{#endsyntax#}</pre>
</th>
<td>
{#syntax#}await{#endsyntax#} can be used to suspend the current function until the frame provided after the {#syntax#}await{#endsyntax#} completes.
{#syntax#}await{#endsyntax#} copies the value returned from the target function's frame to the caller.
<ul>
<li>See also {#link|Async Functions#}</li>
</ul>
</td>
</tr>
<tr>
<th scope="row">
<pre>{#syntax#}break{#endsyntax#}</pre>
@@ -8006,8 +7976,7 @@ TypeExpr <- PrefixTypeOp* ErrorUnionExpr
ErrorUnionExpr <- SuffixExpr (EXCLAMATIONMARK TypeExpr)?
SuffixExpr
<- KEYWORD_async PrimaryTypeExpr SuffixOp* FnCallArguments
/ PrimaryTypeExpr (SuffixOp / FnCallArguments)*
<- PrimaryTypeExpr (SuffixOp / FnCallArguments)*
PrimaryTypeExpr
<- BUILTINIDENTIFIER FnCallArguments
@@ -8183,7 +8152,6 @@ PrefixOp
/ MINUSPERCENT
/ AMPERSAND
/ KEYWORD_try
/ KEYWORD_await
PrefixTypeOp
<- QUESTIONMARK
@@ -8404,8 +8372,6 @@ KEYWORD_and <- 'and' end_of_word
KEYWORD_anyframe <- 'anyframe' end_of_word
KEYWORD_anytype <- 'anytype' end_of_word
KEYWORD_asm <- 'asm' end_of_word
KEYWORD_async <- 'async' end_of_word
KEYWORD_await <- 'await' end_of_word
KEYWORD_break <- 'break' end_of_word
KEYWORD_callconv <- 'callconv' end_of_word
KEYWORD_catch <- 'catch' end_of_word
@@ -8448,8 +8414,8 @@ KEYWORD_volatile <- 'volatile' end_of_word
KEYWORD_while <- 'while' end_of_word
keyword <- KEYWORD_addrspace / KEYWORD_align / KEYWORD_allowzero / KEYWORD_and
/ KEYWORD_anyframe / KEYWORD_anytype / KEYWORD_asm / KEYWORD_async
/ KEYWORD_await / KEYWORD_break / KEYWORD_callconv / KEYWORD_catch
/ KEYWORD_anyframe / KEYWORD_anytype / KEYWORD_asm
/ KEYWORD_break / KEYWORD_callconv / KEYWORD_catch
/ KEYWORD_comptime / KEYWORD_const / KEYWORD_continue / KEYWORD_defer
/ KEYWORD_else / KEYWORD_enum / KEYWORD_errdefer / KEYWORD_error / KEYWORD_export
/ KEYWORD_extern / KEYWORD_fn / KEYWORD_for / KEYWORD_if
-5
View File
@@ -335,7 +335,6 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void {
.address_of,
.@"try",
.@"resume",
.@"await",
.deref,
=> {
return walkExpression(w, ast.nodeData(node).node);
@@ -379,12 +378,8 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void {
.call_one,
.call_one_comma,
.async_call_one,
.async_call_one_comma,
.call,
.call_comma,
.async_call,
.async_call_comma,
=> {
var buf: [1]Ast.Node.Index = undefined;
return walkCall(w, ast.fullCall(&buf, node).?);
-9
View File
@@ -238,12 +238,8 @@ pub const File = struct {
.call_one,
.call_one_comma,
.async_call_one,
.async_call_one_comma,
.call,
.call_comma,
.async_call,
.async_call_comma,
=> {
var buf: [1]Ast.Node.Index = undefined;
return categorize_call(file_index, node, ast.fullCall(&buf, node).?);
@@ -743,7 +739,6 @@ fn expr(w: *Walk, scope: *Scope, parent_decl: Decl.Index, node: Ast.Node.Index)
.@"comptime",
.@"nosuspend",
.@"suspend",
.@"await",
.@"resume",
.@"try",
=> try expr(w, scope, parent_decl, ast.nodeData(node).node),
@@ -806,12 +801,8 @@ fn expr(w: *Walk, scope: *Scope, parent_decl: Decl.Index, node: Ast.Node.Index)
.call_one,
.call_one_comma,
.async_call_one,
.async_call_one_comma,
.call,
.call_comma,
.async_call,
.async_call_comma,
=> {
var buf: [1]Ast.Node.Index = undefined;
const full = ast.fullCall(&buf, node).?;
-2
View File
@@ -101,8 +101,6 @@ pub fn fileSourceHtml(
.keyword_align,
.keyword_and,
.keyword_asm,
.keyword_async,
.keyword_await,
.keyword_break,
.keyword_catch,
.keyword_comptime,
+1 -12
View File
@@ -199,8 +199,6 @@ pub const CallingConvention = union(enum(u8)) {
pub const C: CallingConvention = .c;
/// Deprecated; use `.naked`.
pub const Naked: CallingConvention = .naked;
/// Deprecated; use `.@"async"`.
pub const Async: CallingConvention = .@"async";
/// Deprecated; use `.@"inline"`.
pub const Inline: CallingConvention = .@"inline";
/// Deprecated; use `.x86_64_interrupt`, `.x86_interrupt`, or `.avr_interrupt`.
@@ -866,32 +864,23 @@ pub const WasiExecModel = enum {
pub const CallModifier = enum {
/// Equivalent to function call syntax.
auto,
/// Equivalent to async keyword used with function call syntax.
async_kw,
/// Prevents tail call optimization. This guarantees that the return
/// address will point to the callsite, as opposed to the callsite's
/// callsite. If the call is otherwise required to be tail-called
/// or inlined, a compile error is emitted instead.
never_tail,
/// Guarantees that the call will not be inlined. If the call is
/// otherwise required to be inlined, a compile error is emitted instead.
never_inline,
/// Asserts that the function call will not suspend. This allows a
/// non-async function to call an async function.
no_async,
no_suspend,
/// Guarantees that the call will be generated with tail call optimization.
/// If this is not possible, a compile error is emitted instead.
always_tail,
/// Guarantees that the call will be inlined at the callsite.
/// If this is not possible, a compile error is emitted instead.
always_inline,
/// Evaluates the call at compile-time. If the call cannot be completed at
/// compile-time, a compile error is emitted instead.
compile_time,
+7 -64
View File
@@ -606,7 +606,6 @@ pub fn firstToken(tree: Ast, node: Node.Index) TokenIndex {
.negation_wrap,
.address_of,
.@"try",
.@"await",
.optional_type,
.@"switch",
.switch_comma,
@@ -763,20 +762,6 @@ pub fn firstToken(tree: Ast, node: Node.Index) TokenIndex {
return main_token - end_offset;
},
.async_call_one,
.async_call_one_comma,
=> {
end_offset += 1; // async token
n = tree.nodeData(n).node_and_opt_node[0];
},
.async_call,
.async_call_comma,
=> {
end_offset += 1; // async token
n = tree.nodeData(n).node_and_extra[0];
},
.container_field_init,
.container_field_align,
.container_field,
@@ -903,7 +888,6 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex {
.negation_wrap,
.address_of,
.@"try",
.@"await",
.optional_type,
.@"suspend",
.@"resume",
@@ -1022,7 +1006,7 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex {
};
},
.call, .async_call => {
.call => {
_, const extra_index = tree.nodeData(n).node_and_extra;
const params = tree.extraData(extra_index, Node.SubRange);
assert(params.start != params.end);
@@ -1041,7 +1025,6 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex {
}
},
.call_comma,
.async_call_comma,
.tagged_union_enum_tag_trailing,
=> {
_, const extra_index = tree.nodeData(n).node_and_extra;
@@ -1122,7 +1105,6 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex {
n = @enumFromInt(tree.extra_data[@intFromEnum(range.end) - 1]); // last member
},
.call_one,
.async_call_one,
=> {
_, const first_param = tree.nodeData(n).node_and_opt_node;
end_offset += 1; // for the rparen
@@ -1271,7 +1253,6 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex {
n = first_element;
},
.call_one_comma,
.async_call_one_comma,
.struct_init_one_comma,
=> {
_, const first_field = tree.nodeData(n).node_and_opt_node;
@@ -1988,21 +1969,21 @@ pub fn forFull(tree: Ast, node: Node.Index) full.For {
pub fn callOne(tree: Ast, buffer: *[1]Node.Index, node: Node.Index) full.Call {
const fn_expr, const first_param = tree.nodeData(node).node_and_opt_node;
const params = loadOptionalNodesIntoBuffer(1, buffer, .{first_param});
return tree.fullCallComponents(.{
return .{ .ast = .{
.lparen = tree.nodeMainToken(node),
.fn_expr = fn_expr,
.params = params,
});
} };
}
pub fn callFull(tree: Ast, node: Node.Index) full.Call {
const fn_expr, const extra_index = tree.nodeData(node).node_and_extra;
const params = tree.extraDataSlice(tree.extraData(extra_index, Node.SubRange), Node.Index);
return tree.fullCallComponents(.{
return .{ .ast = .{
.lparen = tree.nodeMainToken(node),
.fn_expr = fn_expr,
.params = params,
});
} };
}
fn fullVarDeclComponents(tree: Ast, info: full.VarDecl.Components) full.VarDecl {
@@ -2336,18 +2317,6 @@ fn fullForComponents(tree: Ast, info: full.For.Components) full.For {
return result;
}
fn fullCallComponents(tree: Ast, info: full.Call.Components) full.Call {
var result: full.Call = .{
.ast = info,
.async_token = null,
};
const first_token = tree.firstToken(info.fn_expr);
if (tree.isTokenPrecededByTags(first_token, &.{.keyword_async})) {
result.async_token = first_token - 1;
}
return result;
}
pub fn fullVarDecl(tree: Ast, node: Node.Index) ?full.VarDecl {
return switch (tree.nodeTag(node)) {
.global_var_decl => tree.globalVarDecl(node),
@@ -2488,8 +2457,8 @@ pub fn fullAsm(tree: Ast, node: Node.Index) ?full.Asm {
pub fn fullCall(tree: Ast, buffer: *[1]Ast.Node.Index, node: Node.Index) ?full.Call {
return switch (tree.nodeTag(node)) {
.call, .call_comma, .async_call, .async_call_comma => tree.callFull(node),
.call_one, .call_one_comma, .async_call_one, .async_call_one_comma => tree.callOne(buffer, node),
.call, .call_comma => tree.callFull(node),
.call_one, .call_one_comma => tree.callOne(buffer, node),
else => null,
};
}
@@ -2882,7 +2851,6 @@ pub const full = struct {
pub const Call = struct {
ast: Components,
async_token: ?TokenIndex,
pub const Components = struct {
lparen: TokenIndex,
@@ -3301,8 +3269,6 @@ pub const Node = struct {
address_of,
/// `try expr`. The `main_token` field is the `try` token.
@"try",
/// `await expr`. The `main_token` field is the `await` token.
@"await",
/// `?expr`. The `main_token` field is the `?` token.
optional_type,
/// `[lhs]rhs`. The `main_token` field is the `[` token.
@@ -3498,17 +3464,6 @@ pub const Node = struct {
/// Same as `call_one` except there is known to be a trailing comma
/// before the final rparen.
call_one_comma,
/// `async a(b)`, `async a()`.
///
/// The `data` field is a `.node_and_opt_node`:
/// 1. a `Node.Index` to the function expression.
/// 2. a `Node.OptionalIndex` to the first argument, if any.
///
/// The `main_token` field is the `(` token.
async_call_one,
/// Same as `async_call_one` except there is known to be a trailing
/// comma before the final rparen.
async_call_one_comma,
/// `a(b, c, d)`.
///
/// The `data` field is a `.node_and_extra`:
@@ -3521,18 +3476,6 @@ pub const Node = struct {
/// Same as `call` except there is known to be a trailing comma before
/// the final rparen.
call_comma,
/// `async a(b, c, d)`.
///
/// The `data` field is a `.node_and_extra`:
/// 1. a `Node.Index` to the function expression.
/// 2. a `ExtraIndex` to a `SubRange` that stores a `Node.Index` for
/// each argument.
///
/// The `main_token` field is the `(` token.
async_call,
/// Same as `async_call` except there is known to be a trailing comma
/// before the final rparen.
async_call_comma,
/// `switch(a) {}`.
///
/// The `data` field is a `.node_and_extra`:
+1 -69
View File
@@ -510,12 +510,8 @@ fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Ins
.number_literal,
.call,
.call_comma,
.async_call,
.async_call_comma,
.call_one,
.call_one_comma,
.async_call_one,
.async_call_one_comma,
.unreachable_literal,
.@"return",
.@"if",
@@ -547,7 +543,6 @@ fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Ins
.merge_error_sets,
.switch_range,
.for_range,
.@"await",
.bit_not,
.negation,
.negation_wrap,
@@ -836,12 +831,8 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
.call_one,
.call_one_comma,
.async_call_one,
.async_call_one_comma,
.call,
.call_comma,
.async_call,
.async_call_comma,
=> {
var buf: [1]Ast.Node.Index = undefined;
return callExpr(gz, scope, ri, .none, node, tree.fullCall(&buf, node).?);
@@ -1114,7 +1105,6 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
.@"nosuspend" => return nosuspendExpr(gz, scope, ri, node),
.@"suspend" => return suspendExpr(gz, scope, node),
.@"await" => return awaitExpr(gz, scope, ri, node),
.@"resume" => return resumeExpr(gz, scope, ri, node),
.@"try" => return tryExpr(gz, scope, ri, node, tree.nodeData(node).node),
@@ -1259,33 +1249,6 @@ fn suspendExpr(
return suspend_inst.toRef();
}
fn awaitExpr(
gz: *GenZir,
scope: *Scope,
ri: ResultInfo,
node: Ast.Node.Index,
) InnerError!Zir.Inst.Ref {
const astgen = gz.astgen;
const tree = astgen.tree;
const rhs_node = tree.nodeData(node).node;
if (gz.suspend_node.unwrap()) |suspend_node| {
return astgen.failNodeNotes(node, "cannot await inside suspend block", .{}, &[_]u32{
try astgen.errNoteNode(suspend_node, "suspend block here", .{}),
});
}
const operand = try expr(gz, scope, .{ .rl = .ref }, rhs_node);
const result = if (gz.nosuspend_node != .none)
try gz.addExtendedPayload(.await_nosuspend, Zir.Inst.UnNode{
.node = gz.nodeIndexToRelative(node),
.operand = operand,
})
else
try gz.addUnNode(.@"await", operand, node);
return rvalue(gz, ri, result, node);
}
fn resumeExpr(
gz: *GenZir,
scope: *Scope,
@@ -2853,7 +2816,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
.tag_name,
.type_name,
.frame_type,
.frame_size,
.int_from_float,
.float_from_int,
.ptr_from_int,
@@ -2887,7 +2849,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
.min,
.c_import,
.@"resume",
.@"await",
.ret_err_value_code,
.ret_ptr,
.ret_type,
@@ -9501,7 +9462,6 @@ fn builtinCall(
.tag_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .tag_name),
.type_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .type_name),
.Frame => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .frame_type),
.frame_size => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .frame_size),
.int_from_float => return typeCast(gz, scope, ri, node, params[0], .int_from_float, builtin_name),
.float_from_int => return typeCast(gz, scope, ri, node, params[0], .float_from_int, builtin_name),
@@ -9767,16 +9727,6 @@ fn builtinCall(
});
return rvalue(gz, ri, result, node);
},
.async_call => {
const result = try gz.addExtendedPayload(.builtin_async_call, Zir.Inst.AsyncCall{
.node = gz.nodeIndexToRelative(node),
.frame_buffer = try expr(gz, scope, .{ .rl = .none }, params[0]),
.result_ptr = try expr(gz, scope, .{ .rl = .none }, params[1]),
.fn_ptr = try expr(gz, scope, .{ .rl = .none }, params[2]),
.args = try expr(gz, scope, .{ .rl = .none }, params[3]),
});
return rvalue(gz, ri, result, node);
},
.Vector => {
const result = try gz.addPlNode(.vector_type, node, Zir.Inst.Bin{
.lhs = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0], .type),
@@ -10175,11 +10125,8 @@ fn callExpr(
const callee = try calleeExpr(gz, scope, ri.rl, override_decl_literal_type, call.ast.fn_expr);
const modifier: std.builtin.CallModifier = blk: {
if (call.async_token != null) {
break :blk .async_kw;
}
if (gz.nosuspend_node != .none) {
break :blk .no_async;
break :blk .no_suspend;
}
break :blk .auto;
};
@@ -10483,12 +10430,8 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) BuiltinFn.Ev
.switch_comma,
.call_one,
.call_one_comma,
.async_call_one,
.async_call_one_comma,
.call,
.call_comma,
.async_call,
.async_call_comma,
=> return .maybe,
.@"return",
@@ -10613,7 +10556,6 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) BuiltinFn.Ev
// Forward the question to the LHS sub-expression.
.@"try",
.@"await",
.@"comptime",
.@"nosuspend",
=> node = tree.nodeData(node).node,
@@ -10803,12 +10745,8 @@ fn nodeImpliesMoreThanOnePossibleValue(tree: *const Ast, start_node: Ast.Node.In
.switch_comma,
.call_one,
.call_one_comma,
.async_call_one,
.async_call_one_comma,
.call,
.call_comma,
.async_call,
.async_call_comma,
.block_two,
.block_two_semicolon,
.block,
@@ -10826,7 +10764,6 @@ fn nodeImpliesMoreThanOnePossibleValue(tree: *const Ast, start_node: Ast.Node.In
// Forward the question to the LHS sub-expression.
.@"try",
.@"await",
.@"comptime",
.@"nosuspend",
=> node = tree.nodeData(node).node,
@@ -11047,12 +10984,8 @@ fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool {
.switch_comma,
.call_one,
.call_one_comma,
.async_call_one,
.async_call_one_comma,
.call,
.call_comma,
.async_call,
.async_call_comma,
.block_two,
.block_two_semicolon,
.block,
@@ -11079,7 +11012,6 @@ fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool {
// Forward the question to the LHS sub-expression.
.@"try",
.@"await",
.@"comptime",
.@"nosuspend",
=> node = tree.nodeData(node).node,
-18
View File
@@ -334,12 +334,8 @@ fn expr(astrl: *AstRlAnnotate, node: Ast.Node.Index, block: ?*Block, ri: ResultI
.call_one,
.call_one_comma,
.async_call_one,
.async_call_one_comma,
.call,
.call_comma,
.async_call,
.async_call_comma,
=> {
var buf: [1]Ast.Node.Index = undefined;
const full = tree.fullCall(&buf, node).?;
@@ -353,11 +349,6 @@ fn expr(astrl: *AstRlAnnotate, node: Ast.Node.Index, block: ?*Block, ri: ResultI
.call,
.call_comma,
=> false, // TODO: once function calls are passed result locations this will change
.async_call_one,
.async_call_one_comma,
.async_call,
.async_call_comma,
=> ri.have_ptr, // always use result ptr for frames
else => unreachable,
};
},
@@ -503,7 +494,6 @@ fn expr(astrl: *AstRlAnnotate, node: Ast.Node.Index, block: ?*Block, ri: ResultI
return false;
},
.@"try",
.@"await",
.@"nosuspend",
=> return astrl.expr(tree.nodeData(node).node, block, ri),
.grouped_expression,
@@ -948,7 +938,6 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.
.tag_name,
.type_name,
.Frame,
.frame_size,
.int_from_float,
.float_from_int,
.ptr_from_int,
@@ -1079,13 +1068,6 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.
_ = try astrl.expr(args[3], block, ResultInfo.none);
return false;
},
.async_call => {
_ = try astrl.expr(args[0], block, ResultInfo.none);
_ = try astrl.expr(args[1], block, ResultInfo.none);
_ = try astrl.expr(args[2], block, ResultInfo.none);
_ = try astrl.expr(args[3], block, ResultInfo.none);
return false; // buffer passed as arg for frame data
},
.Vector => {
_ = try astrl.expr(args[0], block, ResultInfo.type_only);
_ = try astrl.expr(args[1], block, ResultInfo.type_only);
-16
View File
@@ -4,7 +4,6 @@ pub const Tag = enum {
align_cast,
align_of,
as,
async_call,
atomic_load,
atomic_rmw,
atomic_store,
@@ -55,7 +54,6 @@ pub const Tag = enum {
frame,
Frame,
frame_address,
frame_size,
has_decl,
has_field,
import,
@@ -184,13 +182,6 @@ pub const list = list: {
.param_count = 2,
},
},
.{
"@asyncCall",
.{
.tag = .async_call,
.param_count = 4,
},
},
.{
"@atomicLoad",
.{
@@ -550,13 +541,6 @@ pub const list = list: {
.illegal_outside_function = true,
},
},
.{
"@frameSize",
.{
.tag = .frame_size,
.param_count = 1,
},
},
.{
"@hasDecl",
.{
+1 -53
View File
@@ -1688,7 +1688,6 @@ fn parseExprPrecedence(p: *Parse, min_prec: i32) Error!?Node.Index {
/// / MINUSPERCENT
/// / AMPERSAND
/// / KEYWORD_try
/// / KEYWORD_await
fn parsePrefixExpr(p: *Parse) Error!?Node.Index {
const tag: Node.Tag = switch (p.tokenTag(p.tok_i)) {
.bang => .bool_not,
@@ -1697,7 +1696,6 @@ fn parsePrefixExpr(p: *Parse) Error!?Node.Index {
.minus_percent => .negation_wrap,
.ampersand => .address_of,
.keyword_try => .@"try",
.keyword_await => .@"await",
else => return p.parsePrimaryExpr(),
};
return try p.addNode(.{
@@ -2385,62 +2383,12 @@ fn parseErrorUnionExpr(p: *Parse) !?Node.Index {
}
/// SuffixExpr
/// <- KEYWORD_async PrimaryTypeExpr SuffixOp* FnCallArguments
/// / PrimaryTypeExpr (SuffixOp / FnCallArguments)*
/// <- PrimaryTypeExpr (SuffixOp / FnCallArguments)*
///
/// FnCallArguments <- LPAREN ExprList RPAREN
///
/// ExprList <- (Expr COMMA)* Expr?
fn parseSuffixExpr(p: *Parse) !?Node.Index {
if (p.eatToken(.keyword_async)) |_| {
var res = try p.expectPrimaryTypeExpr();
while (true) {
res = try p.parseSuffixOp(res) orelse break;
}
const lparen = p.eatToken(.l_paren) orelse {
try p.warn(.expected_param_list);
return res;
};
const scratch_top = p.scratch.items.len;
defer p.scratch.shrinkRetainingCapacity(scratch_top);
while (true) {
if (p.eatToken(.r_paren)) |_| break;
const param = try p.expectExpr();
try p.scratch.append(p.gpa, param);
switch (p.tokenTag(p.tok_i)) {
.comma => p.tok_i += 1,
.r_paren => {
p.tok_i += 1;
break;
},
.colon, .r_brace, .r_bracket => return p.failExpected(.r_paren),
// Likely just a missing comma; give error but continue parsing.
else => try p.warn(.expected_comma_after_arg),
}
}
const comma = (p.tokenTag(p.tok_i - 2)) == .comma;
const params = p.scratch.items[scratch_top..];
if (params.len <= 1) {
return try p.addNode(.{
.tag = if (comma) .async_call_one_comma else .async_call_one,
.main_token = lparen,
.data = .{ .node_and_opt_node = .{
res,
if (params.len >= 1) params[0].toOptional() else .none,
} },
});
} else {
return try p.addNode(.{
.tag = if (comma) .async_call_comma else .async_call,
.main_token = lparen,
.data = .{ .node_and_extra = .{
res,
try p.addExtra(try p.listToSpan(params)),
} },
});
}
}
var res = try p.parsePrimaryTypeExpr() orelse return null;
while (true) {
const opt_suffix_op = try p.parseSuffixOp(res);
-26
View File
@@ -899,8 +899,6 @@ pub const Inst = struct {
type_name,
/// Implement builtin `@Frame`. Uses `un_node`.
frame_type,
/// Implement builtin `@frameSize`. Uses `un_node`.
frame_size,
/// Implements the `@intFromFloat` builtin.
/// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.
@@ -1044,7 +1042,6 @@ pub const Inst = struct {
/// Implements `resume` syntax. Uses `un_node` field.
@"resume",
@"await",
/// A defer statement.
/// Uses the `defer` union field.
@@ -1241,7 +1238,6 @@ pub const Inst = struct {
.tag_name,
.type_name,
.frame_type,
.frame_size,
.int_from_float,
.float_from_int,
.ptr_from_int,
@@ -1279,7 +1275,6 @@ pub const Inst = struct {
.min,
.c_import,
.@"resume",
.@"await",
.ret_err_value_code,
.extended,
.ret_ptr,
@@ -1526,7 +1521,6 @@ pub const Inst = struct {
.tag_name,
.type_name,
.frame_type,
.frame_size,
.int_from_float,
.float_from_int,
.ptr_from_int,
@@ -1560,7 +1554,6 @@ pub const Inst = struct {
.min,
.c_import,
.@"resume",
.@"await",
.ret_err_value_code,
.@"break",
.break_inline,
@@ -1791,7 +1784,6 @@ pub const Inst = struct {
.tag_name = .un_node,
.type_name = .un_node,
.frame_type = .un_node,
.frame_size = .un_node,
.int_from_float = .pl_node,
.float_from_int = .pl_node,
@@ -1852,7 +1844,6 @@ pub const Inst = struct {
.make_ptr_const = .un_node,
.@"resume" = .un_node,
.@"await" = .un_node,
.@"defer" = .@"defer",
.defer_err_code = .defer_err_code,
@@ -2016,8 +2007,6 @@ pub const Inst = struct {
/// Implements the `@errorCast` builtin.
/// `operand` is payload index to `BinNode`. `lhs` is dest type, `rhs` is operand.
error_cast,
/// `operand` is payload index to `UnNode`.
await_nosuspend,
/// Implements `@breakpoint`.
/// `operand` is `src_node: Ast.Node.Offset`.
breakpoint,
@@ -2038,9 +2027,6 @@ pub const Inst = struct {
/// `operand` is payload index to `Reify`.
/// `small` contains `NameStrategy`.
reify,
/// Implements the `@asyncCall` builtin.
/// `operand` is payload index to `AsyncCall`.
builtin_async_call,
/// Implements the `@cmpxchgStrong` and `@cmpxchgWeak` builtins.
/// `small` 0=>weak 1=>strong
/// `operand` is payload index to `Cmpxchg`.
@@ -3771,14 +3757,6 @@ pub const Inst = struct {
b: Ref,
};
pub const AsyncCall = struct {
node: Ast.Node.Offset,
frame_buffer: Ref,
result_ptr: Ref,
fn_ptr: Ref,
args: Ref,
};
/// Trailing: inst: Index // for every body_len
pub const Param = struct {
/// Null-terminated string index.
@@ -4297,7 +4275,6 @@ fn findTrackableInner(
.tag_name,
.type_name,
.frame_type,
.frame_size,
.int_from_float,
.float_from_int,
.ptr_from_int,
@@ -4337,7 +4314,6 @@ fn findTrackableInner(
.resolve_inferred_alloc,
.make_ptr_const,
.@"resume",
.@"await",
.save_err_ret_index,
.restore_err_ret_index_unconditional,
.restore_err_ret_index_fn_entry,
@@ -4380,14 +4356,12 @@ fn findTrackableInner(
.prefetch,
.set_float_mode,
.error_cast,
.await_nosuspend,
.breakpoint,
.disable_instrumentation,
.disable_intrinsics,
.select,
.int_from_error,
.error_from_int,
.builtin_async_call,
.cmpxchg,
.c_va_arg,
.c_va_copy,
-5
View File
@@ -204,12 +204,8 @@ fn expr(zg: *ZonGen, node: Ast.Node.Index, dest_node: Zoir.Node.Index) Allocator
.call_one,
.call_one_comma,
.async_call_one,
.async_call_one_comma,
.call,
.call_comma,
.async_call,
.async_call_comma,
.@"return",
.if_simple,
.@"if",
@@ -226,7 +222,6 @@ fn expr(zg: *ZonGen, node: Ast.Node.Index, dest_node: Zoir.Node.Index) Allocator
.switch_comma,
.@"nosuspend",
.@"suspend",
.@"await",
.@"resume",
.@"try",
.unreachable_literal,
-76
View File
@@ -341,15 +341,6 @@ test "zig fmt: nosuspend block" {
);
}
test "zig fmt: nosuspend await" {
try testCanonical(
\\fn foo() void {
\\ x = nosuspend await y;
\\}
\\
);
}
test "zig fmt: container declaration, single line" {
try testCanonical(
\\const X = struct { foo: i32 };
@@ -1093,18 +1084,6 @@ test "zig fmt: block in slice expression" {
);
}
test "zig fmt: async function" {
try testCanonical(
\\pub const Server = struct {
\\ handleRequestFn: fn (*Server, *const std.net.Address, File) callconv(.@"async") void,
\\};
\\test "hi" {
\\ var ptr: fn (i32) callconv(.@"async") void = @ptrCast(other);
\\}
\\
);
}
test "zig fmt: whitespace fixes" {
try testTransform("test \"\" {\r\n\tconst hi = x;\r\n}\n// zig fmt: off\ntest \"\"{\r\n\tconst a = b;}\r\n",
\\test "" {
@@ -1549,17 +1528,6 @@ test "zig fmt: spaces around slice operator" {
);
}
test "zig fmt: async call in if condition" {
try testCanonical(
\\comptime {
\\ if (async b()) {
\\ a();
\\ }
\\}
\\
);
}
test "zig fmt: 2nd arg multiline string" {
try testCanonical(
\\comptime {
@@ -3946,27 +3914,6 @@ test "zig fmt: inline asm" {
);
}
test "zig fmt: async functions" {
try testCanonical(
\\fn simpleAsyncFn() void {
\\ const a = async a.b();
\\ x += 1;
\\ suspend {}
\\ x += 1;
\\ suspend {}
\\ const p: anyframe->void = async simpleAsyncFn() catch unreachable;
\\ await p;
\\}
\\
\\test "suspend, resume, await" {
\\ const p: anyframe = async testAsyncSeq();
\\ resume p;
\\ await p;
\\}
\\
);
}
test "zig fmt: nosuspend" {
try testCanonical(
\\const a = nosuspend foo();
@@ -6181,29 +6128,6 @@ test "recovery: missing return type" {
});
}
test "recovery: continue after invalid decl" {
try testError(
\\fn foo {
\\ inline;
\\}
\\pub test "" {
\\ async a & b;
\\}
, &[_]Error{
.expected_token,
.expected_pub_item,
.expected_param_list,
});
try testError(
\\threadlocal test "" {
\\ @a & b;
\\}
, &[_]Error{
.expected_var_decl,
.expected_param_list,
});
}
test "recovery: invalid extern/inline" {
try testError(
\\inline test "" { a & b; }
-8
View File
@@ -591,7 +591,6 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
.@"try",
.@"resume",
.@"await",
=> {
try renderToken(r, tree.nodeMainToken(node), .space);
return renderExpression(r, tree.nodeData(node).node, space);
@@ -635,12 +634,8 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
.call_one,
.call_one_comma,
.async_call_one,
.async_call_one_comma,
.call,
.call_comma,
.async_call,
.async_call_comma,
=> {
var buf: [1]Ast.Node.Index = undefined;
return renderCall(r, tree.fullCall(&buf, node).?, space);
@@ -2551,9 +2546,6 @@ fn renderCall(
call: Ast.full.Call,
space: Space,
) Error!void {
if (call.async_token) |async_token| {
try renderToken(r, async_token, .space);
}
try renderExpression(r, call.ast.fn_expr, .none);
try renderParamList(r, call.ast.lparen, call.ast.params, space);
}
-6
View File
@@ -17,8 +17,6 @@ pub const Token = struct {
.{ "anyframe", .keyword_anyframe },
.{ "anytype", .keyword_anytype },
.{ "asm", .keyword_asm },
.{ "async", .keyword_async },
.{ "await", .keyword_await },
.{ "break", .keyword_break },
.{ "callconv", .keyword_callconv },
.{ "catch", .keyword_catch },
@@ -146,8 +144,6 @@ pub const Token = struct {
keyword_anyframe,
keyword_anytype,
keyword_asm,
keyword_async,
keyword_await,
keyword_break,
keyword_callconv,
keyword_catch,
@@ -273,8 +269,6 @@ pub const Token = struct {
.keyword_anyframe => "anyframe",
.keyword_anytype => "anytype",
.keyword_asm => "asm",
.keyword_async => "async",
.keyword_await => "await",
.keyword_break => "break",
.keyword_callconv => "callconv",
.keyword_catch => "catch",
+3 -54
View File
@@ -1280,7 +1280,6 @@ fn analyzeBodyInner(
.tag_name => try sema.zirTagName(block, inst),
.type_name => try sema.zirTypeName(block, inst),
.frame_type => try sema.zirFrameType(block, inst),
.frame_size => try sema.zirFrameSize(block, inst),
.int_from_float => try sema.zirIntFromFloat(block, inst),
.float_from_int => try sema.zirFloatFromInt(block, inst),
.ptr_from_int => try sema.zirPtrFromInt(block, inst),
@@ -1302,7 +1301,6 @@ fn analyzeBodyInner(
.mul_add => try sema.zirMulAdd(block, inst),
.builtin_call => try sema.zirBuiltinCall(block, inst),
.@"resume" => try sema.zirResume(block, inst),
.@"await" => try sema.zirAwait(block, inst),
.for_len => try sema.zirForLen(block, inst),
.validate_array_init_ref_ty => try sema.zirValidateArrayInitRefTy(block, inst),
.opt_eu_base_ptr_init => try sema.zirOptEuBasePtrInit(block, inst),
@@ -1410,12 +1408,10 @@ fn analyzeBodyInner(
.wasm_memory_grow => try sema.zirWasmMemoryGrow( block, extended),
.prefetch => try sema.zirPrefetch( block, extended),
.error_cast => try sema.zirErrorCast( block, extended),
.await_nosuspend => try sema.zirAwaitNosuspend( block, extended),
.select => try sema.zirSelect( block, extended),
.int_from_error => try sema.zirIntFromError( block, extended),
.error_from_int => try sema.zirErrorFromInt( block, extended),
.reify => try sema.zirReify( block, extended, inst),
.builtin_async_call => try sema.zirBuiltinAsyncCall( block, extended),
.cmpxchg => try sema.zirCmpxchg( block, extended),
.c_va_arg => try sema.zirCVaArg( block, extended),
.c_va_copy => try sema.zirCVaCopy( block, extended),
@@ -7653,10 +7649,6 @@ fn analyzeCall(
const ip = &zcu.intern_pool;
const arena = sema.arena;
if (modifier == .async_kw) {
return sema.failWithUseOfAsync(block, call_src);
}
const maybe_func_inst = try sema.funcDeclSrcInst(callee);
const func_ret_ty_src: LazySrcLoc = if (maybe_func_inst) |fn_decl_inst| .{
.base_node_inst = fn_decl_inst,
@@ -8048,14 +8040,13 @@ fn analyzeCall(
}
const call_tag: Air.Inst.Tag = switch (modifier) {
.auto, .no_async => .call,
.auto, .no_suspend => .call,
.never_tail => .call_never_tail,
.never_inline => .call_never_inline,
.always_tail => .call_always_tail,
.always_inline,
.compile_time,
.async_kw,
=> unreachable,
};
@@ -22133,12 +22124,6 @@ fn zirFrameType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
return sema.failWithUseOfAsync(block, src);
}
fn zirFrameSize(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
const src = block.nodeOffset(inst_data.src_node);
return sema.failWithUseOfAsync(block, src);
}
fn zirIntFromFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const pt = sema.pt;
const zcu = pt.zcu;
@@ -24776,14 +24761,14 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
var modifier = try sema.interpretBuiltinType(block, modifier_src, modifier_val, std.builtin.CallModifier);
switch (modifier) {
// These can be upgraded to comptime or nosuspend calls.
.auto, .never_tail, .no_async => {
.auto, .never_tail, .no_suspend => {
if (block.isComptime()) {
if (modifier == .never_tail) {
return sema.fail(block, modifier_src, "unable to perform 'never_tail' call at compile-time", .{});
}
modifier = .compile_time;
} else if (extra.flags.is_nosuspend) {
modifier = .no_async;
modifier = .no_suspend;
}
},
// These can be upgraded to comptime. nosuspend bit can be safely ignored.
@@ -24801,14 +24786,6 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
modifier = .compile_time;
}
},
.async_kw => {
if (extra.flags.is_nosuspend) {
return sema.fail(block, modifier_src, "modifier 'async_kw' cannot be used inside nosuspend block", .{});
}
if (block.isComptime()) {
return sema.fail(block, modifier_src, "modifier 'async_kw' cannot be used in combination with comptime function call", .{});
}
},
.never_inline => {
if (block.isComptime()) {
return sema.fail(block, modifier_src, "unable to perform 'never_inline' call at compile-time", .{});
@@ -25797,40 +25774,12 @@ fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
});
}
fn zirBuiltinAsyncCall(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
const src = block.nodeOffset(extra.node);
return sema.failWithUseOfAsync(block, src);
}
fn zirResume(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
const src = block.nodeOffset(inst_data.src_node);
return sema.failWithUseOfAsync(block, src);
}
fn zirAwait(
sema: *Sema,
block: *Block,
inst: Zir.Inst.Index,
) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
const src = block.nodeOffset(inst_data.src_node);
return sema.failWithUseOfAsync(block, src);
}
fn zirAwaitNosuspend(
sema: *Sema,
block: *Block,
extended: Zir.Inst.Extended.InstData,
) CompileError!Air.Inst.Ref {
const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
const src = block.nodeOffset(extra.node);
return sema.failWithUseOfAsync(block, src);
}
fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const tracy = trace(@src());
defer tracy.end();
-4
View File
@@ -1443,12 +1443,8 @@ pub const SrcLoc = struct {
.field_access => tree.nodeData(node).node_and_token[1],
.call_one,
.call_one_comma,
.async_call_one,
.async_call_one_comma,
.call,
.call_comma,
.async_call,
.async_call_comma,
=> blk: {
const full = tree.fullCall(&buf, node).?;
break :blk tree.lastToken(full.ast.fn_expr);
+2 -2
View File
@@ -5273,7 +5273,7 @@ pub const FuncGen = struct {
switch (modifier) {
.auto, .always_tail => {},
.never_tail, .never_inline => try attributes.addFnAttr(.@"noinline", &o.builder),
.async_kw, .no_async, .always_inline, .compile_time => unreachable,
.no_suspend, .always_inline, .compile_time => unreachable,
}
const ret_ptr = if (!sret) null else blk: {
@@ -5488,7 +5488,7 @@ pub const FuncGen = struct {
.auto, .never_inline => .normal,
.never_tail => .notail,
.always_tail => .musttail,
.async_kw, .no_async, .always_inline, .compile_time => unreachable,
.no_suspend, .always_inline, .compile_time => unreachable,
},
toLlvmCallConvTag(fn_info.cc, target).?,
try attributes.finish(&o.builder),
-17
View File
@@ -261,14 +261,12 @@ const Writer = struct {
.tag_name,
.type_name,
.frame_type,
.frame_size,
.clz,
.ctz,
.pop_count,
.byte_swap,
.bit_reverse,
.@"resume",
.@"await",
.make_ptr_const,
.validate_deref,
.validate_const,
@@ -565,7 +563,6 @@ const Writer = struct {
.tuple_decl => try self.writeTupleDecl(stream, extended),
.await_nosuspend,
.c_undef,
.c_include,
.set_float_mode,
@@ -611,7 +608,6 @@ const Writer = struct {
try self.writeSrcNode(stream, inst_data.node);
},
.builtin_async_call => try self.writeBuiltinAsyncCall(stream, extended),
.cmpxchg => try self.writeCmpxchg(stream, extended),
.ptr_cast_full => try self.writePtrCastFull(stream, extended),
.ptr_cast_no_dest => try self.writePtrCastNoDest(stream, extended),
@@ -932,19 +928,6 @@ const Writer = struct {
try self.writeSrcNode(stream, extra.src_node);
}
fn writeBuiltinAsyncCall(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void {
const extra = self.code.extraData(Zir.Inst.AsyncCall, extended.operand).data;
try self.writeInstRef(stream, extra.frame_buffer);
try stream.writeAll(", ");
try self.writeInstRef(stream, extra.result_ptr);
try stream.writeAll(", ");
try self.writeInstRef(stream, extra.fn_ptr);
try stream.writeAll(", ");
try self.writeInstRef(stream, extra.args);
try stream.writeAll(") ");
try self.writeSrcNode(stream, extra.node);
}
fn writeParam(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_tok;
const extra = self.code.extraData(Zir.Inst.Param, inst_data.payload_index);
-2
View File
@@ -5,9 +5,7 @@ test {
_ = @import("behavior/align.zig");
_ = @import("behavior/alignof.zig");
_ = @import("behavior/array.zig");
_ = @import("behavior/async_fn.zig");
_ = @import("behavior/atomics.zig");
_ = @import("behavior/await_struct.zig");
_ = @import("behavior/basic.zig");
_ = @import("behavior/bit_shifting.zig");
_ = @import("behavior/bitcast.zig");
-24
View File
@@ -425,30 +425,6 @@ test "struct field explicit alignment" {
try expect(@intFromPtr(&node.massive_byte) % 64 == 0);
}
test "align(@alignOf(T)) T does not force resolution of T" {
if (true) return error.SkipZigTest; // TODO
const S = struct {
const A = struct {
a: *align(@alignOf(A)) A,
};
fn doTheTest() void {
suspend {
resume @frame();
}
_ = bar(@Frame(doTheTest));
}
fn bar(comptime T: type) *align(@alignOf(T)) T {
ok = true;
return undefined;
}
var ok = false;
};
_ = async S.doTheTest();
try expect(S.ok);
}
test "align(N) on functions" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
-1911
View File
@@ -1,1911 +0,0 @@
const std = @import("std");
const builtin = @import("builtin");
const assert = std.debug.assert;
const expect = std.testing.expect;
const expectEqual = std.testing.expectEqual;
const expectEqualStrings = std.testing.expectEqualStrings;
const expectError = std.testing.expectError;
var global_x: i32 = 1;
test "simple coroutine suspend and resume" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
var frame = async simpleAsyncFn();
try expect(global_x == 2);
resume frame;
try expect(global_x == 3);
const af: anyframe->void = &frame;
_ = af;
resume frame;
try expect(global_x == 4);
}
fn simpleAsyncFn() void {
global_x += 1;
suspend {}
global_x += 1;
suspend {}
global_x += 1;
}
var global_y: i32 = 1;
test "pass parameter to coroutine" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
var p = async simpleAsyncFnWithArg(2);
try expect(global_y == 3);
resume p;
try expect(global_y == 5);
}
fn simpleAsyncFnWithArg(delta: i32) void {
global_y += delta;
suspend {}
global_y += delta;
}
test "suspend at end of function" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S = struct {
var x: i32 = 1;
fn doTheTest() !void {
try expect(x == 1);
const p = async suspendAtEnd();
_ = p;
try expect(x == 2);
}
fn suspendAtEnd() void {
x += 1;
suspend {}
}
};
try S.doTheTest();
}
test "local variable in async function" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S = struct {
var x: i32 = 0;
fn doTheTest() !void {
try expect(x == 0);
var p = async add(1, 2);
try expect(x == 0);
resume p;
try expect(x == 0);
resume p;
try expect(x == 0);
resume p;
try expect(x == 3);
}
fn add(a: i32, b: i32) void {
var accum: i32 = 0;
suspend {}
accum += a;
suspend {}
accum += b;
suspend {}
x = accum;
}
};
try S.doTheTest();
}
test "calling an inferred async function" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S = struct {
var x: i32 = 1;
var other_frame: *@Frame(other) = undefined;
fn doTheTest() !void {
_ = async first();
try expect(x == 1);
resume other_frame.*;
try expect(x == 2);
}
fn first() void {
other();
}
fn other() void {
other_frame = @frame();
suspend {}
x += 1;
}
};
try S.doTheTest();
}
test "@frameSize" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
if (builtin.target.cpu.arch == .thumb or builtin.target.cpu.arch == .thumbeb)
return error.SkipZigTest;
const S = struct {
fn doTheTest() !void {
{
var ptr = @as(fn (i32) callconv(.@"async") void, @ptrCast(other));
_ = &ptr;
const size = @frameSize(ptr);
try expect(size == @sizeOf(@Frame(other)));
}
{
var ptr = @as(fn () callconv(.@"async") void, @ptrCast(first));
_ = &ptr;
const size = @frameSize(ptr);
try expect(size == @sizeOf(@Frame(first)));
}
}
fn first() void {
other(1);
}
fn other(param: i32) void {
_ = param;
var local: i32 = undefined;
_ = &local;
suspend {}
}
};
try S.doTheTest();
}
test "coroutine suspend, resume" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S = struct {
var frame: anyframe = undefined;
fn doTheTest() !void {
_ = async amain();
seq('d');
resume frame;
seq('h');
try expect(std.mem.eql(u8, &points, "abcdefgh"));
}
fn amain() void {
seq('a');
var f = async testAsyncSeq();
seq('c');
await f;
seq('g');
}
fn testAsyncSeq() void {
defer seq('f');
seq('b');
suspend {
frame = @frame();
}
seq('e');
}
var points = [_]u8{'x'} ** "abcdefgh".len;
var index: usize = 0;
fn seq(c: u8) void {
points[index] = c;
index += 1;
}
};
try S.doTheTest();
}
test "coroutine suspend with block" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const p = async testSuspendBlock();
_ = p;
try expect(!global_result);
resume a_promise;
try expect(global_result);
}
var a_promise: anyframe = undefined;
var global_result = false;
fn testSuspendBlock() callconv(.@"async") void {
suspend {
comptime assert(@TypeOf(@frame()) == *@Frame(testSuspendBlock)) catch unreachable;
a_promise = @frame();
}
// Test to make sure that @frame() works as advertised (issue #1296)
// var our_handle: anyframe = @frame();
expect(a_promise == @as(anyframe, @frame())) catch @panic("test failed");
global_result = true;
}
var await_a_promise: anyframe = undefined;
var await_final_result: i32 = 0;
test "coroutine await" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
await_seq('a');
var p = async await_amain();
_ = &p;
await_seq('f');
resume await_a_promise;
await_seq('i');
try expect(await_final_result == 1234);
try expect(std.mem.eql(u8, &await_points, "abcdefghi"));
}
fn await_amain() callconv(.@"async") void {
await_seq('b');
var p = async await_another();
await_seq('e');
await_final_result = await p;
await_seq('h');
}
fn await_another() callconv(.@"async") i32 {
await_seq('c');
suspend {
await_seq('d');
await_a_promise = @frame();
}
await_seq('g');
return 1234;
}
var await_points = [_]u8{0} ** "abcdefghi".len;
var await_seq_index: usize = 0;
fn await_seq(c: u8) void {
await_points[await_seq_index] = c;
await_seq_index += 1;
}
var early_final_result: i32 = 0;
test "coroutine await early return" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
early_seq('a');
var p = async early_amain();
_ = &p;
early_seq('f');
try expect(early_final_result == 1234);
try expect(std.mem.eql(u8, &early_points, "abcdef"));
}
fn early_amain() callconv(.@"async") void {
early_seq('b');
var p = async early_another();
early_seq('d');
early_final_result = await p;
early_seq('e');
}
fn early_another() callconv(.@"async") i32 {
early_seq('c');
return 1234;
}
var early_points = [_]u8{0} ** "abcdef".len;
var early_seq_index: usize = 0;
fn early_seq(c: u8) void {
early_points[early_seq_index] = c;
early_seq_index += 1;
}
test "async function with dot syntax" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S = struct {
var y: i32 = 1;
fn foo() callconv(.@"async") void {
y += 1;
suspend {}
}
};
const p = async S.foo();
_ = p;
try expect(S.y == 2);
}
test "async fn pointer in a struct field" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
var data: i32 = 1;
const Foo = struct {
bar: fn (*i32) callconv(.@"async") void,
};
var foo = Foo{ .bar = simpleAsyncFn2 };
_ = &foo;
var bytes: [64]u8 align(16) = undefined;
const f = @asyncCall(&bytes, {}, foo.bar, .{&data});
comptime assert(@TypeOf(f) == anyframe->void);
try expect(data == 2);
resume f;
try expect(data == 4);
_ = async doTheAwait(f);
try expect(data == 4);
}
fn doTheAwait(f: anyframe->void) void {
await f;
}
fn simpleAsyncFn2(y: *i32) callconv(.@"async") void {
defer y.* += 2;
y.* += 1;
suspend {}
}
test "@asyncCall with return type" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const Foo = struct {
bar: fn () callconv(.@"async") i32,
var global_frame: anyframe = undefined;
fn middle() callconv(.@"async") i32 {
return afunc();
}
fn afunc() i32 {
global_frame = @frame();
suspend {}
return 1234;
}
};
var foo = Foo{ .bar = Foo.middle };
_ = &foo;
var bytes: [150]u8 align(16) = undefined;
var aresult: i32 = 0;
_ = @asyncCall(&bytes, &aresult, foo.bar, .{});
try expect(aresult == 0);
resume Foo.global_frame;
try expect(aresult == 1234);
}
test "async fn with inferred error set" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S = struct {
var global_frame: anyframe = undefined;
fn doTheTest() !void {
var frame: [1]@Frame(middle) = undefined;
var fn_ptr = middle;
_ = &fn_ptr;
var result: @typeInfo(@typeInfo(@TypeOf(fn_ptr)).@"fn".return_type.?).error_union.error_set!void = undefined;
_ = @asyncCall(std.mem.sliceAsBytes(frame[0..]), &result, fn_ptr, .{});
resume global_frame;
try std.testing.expectError(error.Fail, result);
}
fn middle() callconv(.@"async") !void {
var f = async middle2();
return await f;
}
fn middle2() !void {
return failing();
}
fn failing() !void {
global_frame = @frame();
suspend {}
return error.Fail;
}
};
try S.doTheTest();
}
test "error return trace across suspend points - early return" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const p = nonFailing();
resume p;
const p2 = async printTrace(p);
_ = p2;
}
test "error return trace across suspend points - async return" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const p = nonFailing();
const p2 = async printTrace(p);
_ = p2;
resume p;
}
fn nonFailing() (anyframe->anyerror!void) {
const Static = struct {
var frame: @Frame(suspendThenFail) = undefined;
};
Static.frame = async suspendThenFail();
return &Static.frame;
}
fn suspendThenFail() callconv(.@"async") anyerror!void {
suspend {}
return error.Fail;
}
fn printTrace(p: anyframe->(anyerror!void)) callconv(.@"async") void {
(await p) catch |e| {
std.testing.expect(e == error.Fail) catch @panic("test failure");
if (@errorReturnTrace()) |trace| {
expect(trace.index == 1) catch @panic("test failure");
} else switch (builtin.mode) {
.Debug, .ReleaseSafe => @panic("expected return trace"),
.ReleaseFast, .ReleaseSmall => {},
}
};
}
test "break from suspend" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
var my_result: i32 = 1;
const p = async testBreakFromSuspend(&my_result);
_ = p;
try std.testing.expect(my_result == 2);
}
fn testBreakFromSuspend(my_result: *i32) callconv(.@"async") void {
suspend {
resume @frame();
}
my_result.* += 1;
suspend {}
my_result.* += 1;
}
test "heap allocated async function frame" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S = struct {
var x: i32 = 42;
fn doTheTest() !void {
const frame = try std.testing.allocator.create(@Frame(someFunc));
defer std.testing.allocator.destroy(frame);
try expect(x == 42);
frame.* = async someFunc();
try expect(x == 43);
resume frame;
try expect(x == 44);
}
fn someFunc() void {
x += 1;
suspend {}
x += 1;
}
};
try S.doTheTest();
}
test "async function call return value" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S = struct {
var frame: anyframe = undefined;
var pt = Point{ .x = 10, .y = 11 };
fn doTheTest() !void {
try expectEqual(pt.x, 10);
try expectEqual(pt.y, 11);
_ = async first();
try expectEqual(pt.x, 10);
try expectEqual(pt.y, 11);
resume frame;
try expectEqual(pt.x, 1);
try expectEqual(pt.y, 2);
}
fn first() void {
pt = second(1, 2);
}
fn second(x: i32, y: i32) Point {
return other(x, y);
}
fn other(x: i32, y: i32) Point {
frame = @frame();
suspend {}
return Point{
.x = x,
.y = y,
};
}
const Point = struct {
x: i32,
y: i32,
};
};
try S.doTheTest();
}
test "suspension points inside branching control flow" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S = struct {
var result: i32 = 10;
fn doTheTest() !void {
try expect(10 == result);
var frame = async func(true);
try expect(10 == result);
resume frame;
try expect(11 == result);
resume frame;
try expect(12 == result);
resume frame;
try expect(13 == result);
}
fn func(b: bool) void {
while (b) {
suspend {}
result += 1;
}
}
};
try S.doTheTest();
}
test "call async function which has struct return type" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S = struct {
var frame: anyframe = undefined;
fn doTheTest() void {
_ = async atest();
resume frame;
}
fn atest() void {
const result = func();
expect(result.x == 5) catch @panic("test failed");
expect(result.y == 6) catch @panic("test failed");
}
const Point = struct {
x: usize,
y: usize,
};
fn func() Point {
suspend {
frame = @frame();
}
return Point{
.x = 5,
.y = 6,
};
}
};
S.doTheTest();
}
test "pass string literal to async function" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S = struct {
var frame: anyframe = undefined;
var ok: bool = false;
fn doTheTest() !void {
_ = async hello("hello");
resume frame;
try expect(ok);
}
fn hello(msg: []const u8) void {
frame = @frame();
suspend {}
expectEqualStrings("hello", msg) catch @panic("test failed");
ok = true;
}
};
try S.doTheTest();
}
test "await inside an errdefer" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S = struct {
var frame: anyframe = undefined;
fn doTheTest() !void {
_ = async amainWrap();
resume frame;
}
fn amainWrap() !void {
var foo = async func();
errdefer await foo;
return error.Bad;
}
fn func() void {
frame = @frame();
suspend {}
}
};
try S.doTheTest();
}
test "try in an async function with error union and non-zero-bit payload" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S = struct {
var frame: anyframe = undefined;
var ok = false;
fn doTheTest() !void {
_ = async amain();
resume frame;
try expect(ok);
}
fn amain() void {
std.testing.expectError(error.Bad, theProblem()) catch @panic("test failed");
ok = true;
}
fn theProblem() ![]u8 {
frame = @frame();
suspend {}
const result = try other();
return result;
}
fn other() ![]u8 {
return error.Bad;
}
};
try S.doTheTest();
}
test "returning a const error from async function" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S = struct {
var frame: anyframe = undefined;
var ok = false;
fn doTheTest() !void {
_ = async amain();
resume frame;
try expect(ok);
}
fn amain() !void {
var download_frame = async fetchUrl(10, "a string");
const download_text = try await download_frame;
_ = download_text;
@panic("should not get here");
}
fn fetchUrl(unused: i32, url: []const u8) ![]u8 {
_ = unused;
_ = url;
frame = @frame();
suspend {}
ok = true;
return error.OutOfMemory;
}
};
try S.doTheTest();
}
test "async/await typical usage" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
inline for ([_]bool{ false, true }) |b1| {
inline for ([_]bool{ false, true }) |b2| {
inline for ([_]bool{ false, true }) |b3| {
inline for ([_]bool{ false, true }) |b4| {
testAsyncAwaitTypicalUsage(b1, b2, b3, b4).doTheTest();
}
}
}
}
}
fn testAsyncAwaitTypicalUsage(
comptime simulate_fail_download: bool,
comptime simulate_fail_file: bool,
comptime suspend_download: bool,
comptime suspend_file: bool,
) type {
return struct {
fn doTheTest() void {
_ = async amainWrap();
if (suspend_file) {
resume global_file_frame;
}
if (suspend_download) {
resume global_download_frame;
}
}
fn amainWrap() void {
if (amain()) |_| {
expect(!simulate_fail_download) catch @panic("test failure");
expect(!simulate_fail_file) catch @panic("test failure");
} else |e| switch (e) {
error.NoResponse => expect(simulate_fail_download) catch @panic("test failure"),
error.FileNotFound => expect(simulate_fail_file) catch @panic("test failure"),
else => @panic("test failure"),
}
}
fn amain() !void {
const allocator = std.testing.allocator;
var download_frame = async fetchUrl(allocator, "https://example.com/");
var download_awaited = false;
errdefer if (!download_awaited) {
if (await download_frame) |x| allocator.free(x) else |_| {}
};
var file_frame = async readFile(allocator, "something.txt");
var file_awaited = false;
errdefer if (!file_awaited) {
if (await file_frame) |x| allocator.free(x) else |_| {}
};
download_awaited = true;
const download_text = try await download_frame;
defer allocator.free(download_text);
file_awaited = true;
const file_text = try await file_frame;
defer allocator.free(file_text);
try expect(std.mem.eql(u8, "expected download text", download_text));
try expect(std.mem.eql(u8, "expected file text", file_text));
}
var global_download_frame: anyframe = undefined;
fn fetchUrl(allocator: std.mem.Allocator, url: []const u8) anyerror![]u8 {
_ = url;
const result = try allocator.dupe(u8, "expected download text");
errdefer allocator.free(result);
if (suspend_download) {
suspend {
global_download_frame = @frame();
}
}
if (simulate_fail_download) return error.NoResponse;
return result;
}
var global_file_frame: anyframe = undefined;
fn readFile(allocator: std.mem.Allocator, filename: []const u8) anyerror![]u8 {
_ = filename;
const result = try allocator.dupe(u8, "expected file text");
errdefer allocator.free(result);
if (suspend_file) {
suspend {
global_file_frame = @frame();
}
}
if (simulate_fail_file) return error.FileNotFound;
return result;
}
};
}
test "alignment of local variables in async functions" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S = struct {
fn doTheTest() !void {
var y: u8 = 123;
_ = &y;
var x: u8 align(128) = 1;
try expect(@intFromPtr(&x) % 128 == 0);
}
};
try S.doTheTest();
}
test "no reason to resolve frame still works" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
_ = async simpleNothing();
}
fn simpleNothing() void {
var x: i32 = 1234;
_ = &x;
}
test "async call a generic function" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S = struct {
fn doTheTest() !void {
var f = async func(i32, 2);
const result = await f;
try expect(result == 3);
}
fn func(comptime T: type, inc: T) T {
var x: T = 1;
suspend {
resume @frame();
}
x += inc;
return x;
}
};
_ = async S.doTheTest();
}
test "return from suspend block" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S = struct {
fn doTheTest() !void {
expect(func() == 1234) catch @panic("test failure");
}
fn func() i32 {
suspend {
return 1234;
}
}
};
_ = async S.doTheTest();
}
test "struct parameter to async function is copied to the frame" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S = struct {
const Point = struct {
x: i32,
y: i32,
};
var frame: anyframe = undefined;
fn doTheTest() void {
_ = async atest();
resume frame;
}
fn atest() void {
var f: @Frame(foo) = undefined;
bar(&f);
clobberStack(10);
}
fn clobberStack(x: i32) void {
if (x == 0) return;
clobberStack(x - 1);
var y: i32 = x;
_ = &y;
}
fn bar(f: *@Frame(foo)) void {
var pt = Point{ .x = 1, .y = 2 };
_ = &pt;
f.* = async foo(pt);
const result = await f;
expect(result == 1) catch @panic("test failure");
}
fn foo(point: Point) i32 {
suspend {
frame = @frame();
}
return point.x;
}
};
S.doTheTest();
}
test "cast fn to async fn when it is inferred to be async" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S = struct {
var frame: anyframe = undefined;
var ok = false;
fn doTheTest() void {
var ptr: fn () callconv(.@"async") i32 = undefined;
ptr = func;
var buf: [100]u8 align(16) = undefined;
var result: i32 = undefined;
const f = @asyncCall(&buf, &result, ptr, .{});
_ = await f;
expect(result == 1234) catch @panic("test failure");
ok = true;
}
fn func() i32 {
suspend {
frame = @frame();
}
return 1234;
}
};
_ = async S.doTheTest();
resume S.frame;
try expect(S.ok);
}
test "cast fn to async fn when it is inferred to be async, awaited directly" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S = struct {
var frame: anyframe = undefined;
var ok = false;
fn doTheTest() void {
var ptr: fn () callconv(.@"async") i32 = undefined;
ptr = func;
var buf: [100]u8 align(16) = undefined;
var result: i32 = undefined;
_ = await @asyncCall(&buf, &result, ptr, .{});
expect(result == 1234) catch @panic("test failure");
ok = true;
}
fn func() i32 {
suspend {
frame = @frame();
}
return 1234;
}
};
_ = async S.doTheTest();
resume S.frame;
try expect(S.ok);
}
test "await does not force async if callee is blocking" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S = struct {
fn simple() i32 {
return 1234;
}
};
var x = async S.simple();
try expect(await x == 1234);
}
test "recursive async function" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
try expect(recursiveAsyncFunctionTest(false).doTheTest() == 55);
try expect(recursiveAsyncFunctionTest(true).doTheTest() == 55);
}
fn recursiveAsyncFunctionTest(comptime suspending_implementation: bool) type {
return struct {
fn fib(allocator: std.mem.Allocator, x: u32) error{OutOfMemory}!u32 {
if (x <= 1) return x;
if (suspending_implementation) {
suspend {
resume @frame();
}
}
const f1 = try allocator.create(@Frame(fib));
defer allocator.destroy(f1);
const f2 = try allocator.create(@Frame(fib));
defer allocator.destroy(f2);
f1.* = async fib(allocator, x - 1);
var f1_awaited = false;
errdefer if (!f1_awaited) {
_ = await f1;
};
f2.* = async fib(allocator, x - 2);
var f2_awaited = false;
errdefer if (!f2_awaited) {
_ = await f2;
};
var sum: u32 = 0;
f1_awaited = true;
sum += try await f1;
f2_awaited = true;
sum += try await f2;
return sum;
}
fn doTheTest() u32 {
if (suspending_implementation) {
var result: u32 = undefined;
_ = async amain(&result);
return result;
} else {
return fib(std.testing.allocator, 10) catch unreachable;
}
}
fn amain(result: *u32) void {
var x = async fib(std.testing.allocator, 10);
result.* = (await x) catch unreachable;
}
};
}
test "@asyncCall with comptime-known function, but not awaited directly" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S = struct {
var global_frame: anyframe = undefined;
fn doTheTest() !void {
var frame: [1]@Frame(middle) = undefined;
var result: @typeInfo(@typeInfo(@TypeOf(middle)).@"fn".return_type.?).error_union.error_set!void = undefined;
_ = @asyncCall(std.mem.sliceAsBytes(frame[0..]), &result, middle, .{});
resume global_frame;
try std.testing.expectError(error.Fail, result);
}
fn middle() callconv(.@"async") !void {
var f = async middle2();
return await f;
}
fn middle2() !void {
return failing();
}
fn failing() !void {
global_frame = @frame();
suspend {}
return error.Fail;
}
};
try S.doTheTest();
}
test "@asyncCall with actual frame instead of byte buffer" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S = struct {
fn func() i32 {
suspend {}
return 1234;
}
};
var frame: @Frame(S.func) = undefined;
var result: i32 = undefined;
const ptr = @asyncCall(&frame, &result, S.func, .{});
resume ptr;
try expect(result == 1234);
}
test "@asyncCall using the result location inside the frame" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S = struct {
fn simple2(y: *i32) callconv(.@"async") i32 {
defer y.* += 2;
y.* += 1;
suspend {}
return 1234;
}
fn getAnswer(f: anyframe->i32, out: *i32) void {
out.* = await f;
}
};
var data: i32 = 1;
const Foo = struct {
bar: fn (*i32) callconv(.@"async") i32,
};
var foo = Foo{ .bar = S.simple2 };
_ = &foo;
var bytes: [64]u8 align(16) = undefined;
const f = @asyncCall(&bytes, {}, foo.bar, .{&data});
comptime assert(@TypeOf(f) == anyframe->i32);
try expect(data == 2);
resume f;
try expect(data == 4);
_ = async S.getAnswer(f, &data);
try expect(data == 1234);
}
test "@TypeOf an async function call of generic fn with error union type" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S = struct {
fn func(comptime x: anytype) anyerror!i32 {
const T = @TypeOf(async func(x));
comptime assert(T == @typeInfo(@TypeOf(@frame())).pointer.child);
return undefined;
}
};
_ = async S.func(i32);
}
test "using @TypeOf on a generic function call" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S = struct {
var global_frame: anyframe = undefined;
var global_ok = false;
var buf: [100]u8 align(16) = undefined;
fn amain(x: anytype) void {
if (x == 0) {
global_ok = true;
return;
}
suspend {
global_frame = @frame();
}
const F = @TypeOf(async amain(x - 1));
const frame = @as(*F, @ptrFromInt(@intFromPtr(&buf)));
return await @asyncCall(frame, {}, amain, .{x - 1});
}
};
_ = async S.amain(@as(u32, 1));
resume S.global_frame;
try expect(S.global_ok);
}
test "recursive call of await @asyncCall with struct return type" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S = struct {
var global_frame: anyframe = undefined;
var global_ok = false;
var buf: [100]u8 align(16) = undefined;
fn amain(x: anytype) Foo {
if (x == 0) {
global_ok = true;
return Foo{ .x = 1, .y = 2, .z = 3 };
}
suspend {
global_frame = @frame();
}
const F = @TypeOf(async amain(x - 1));
const frame = @as(*F, @ptrFromInt(@intFromPtr(&buf)));
return await @asyncCall(frame, {}, amain, .{x - 1});
}
const Foo = struct {
x: u64,
y: u64,
z: u64,
};
};
var res: S.Foo = undefined;
var frame: @TypeOf(async S.amain(@as(u32, 1))) = undefined;
_ = @asyncCall(&frame, &res, S.amain, .{@as(u32, 1)});
resume S.global_frame;
try expect(S.global_ok);
try expect(res.x == 1);
try expect(res.y == 2);
try expect(res.z == 3);
}
test "nosuspend function call" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S = struct {
fn doTheTest() !void {
const result = nosuspend add(50, 100);
try expect(result == 150);
}
fn add(a: i32, b: i32) i32 {
if (a > 100) {
suspend {}
}
return a + b;
}
};
try S.doTheTest();
}
test "await used in expression and awaiting fn with no suspend but async calling convention" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S = struct {
fn atest() void {
var f1 = async add(1, 2);
var f2 = async add(3, 4);
const sum = (await f1) + (await f2);
expect(sum == 10) catch @panic("test failure");
}
fn add(a: i32, b: i32) callconv(.@"async") i32 {
return a + b;
}
};
_ = async S.atest();
}
test "await used in expression after a fn call" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S = struct {
fn atest() void {
var f1 = async add(3, 4);
var sum: i32 = 0;
sum = foo() + await f1;
expect(sum == 8) catch @panic("test failure");
}
fn add(a: i32, b: i32) callconv(.@"async") i32 {
return a + b;
}
fn foo() i32 {
return 1;
}
};
_ = async S.atest();
}
test "async fn call used in expression after a fn call" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S = struct {
fn atest() void {
var sum: i32 = 0;
sum = foo() + add(3, 4);
expect(sum == 8) catch @panic("test failure");
}
fn add(a: i32, b: i32) callconv(.@"async") i32 {
return a + b;
}
fn foo() i32 {
return 1;
}
};
_ = async S.atest();
}
test "suspend in for loop" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S = struct {
var global_frame: ?anyframe = null;
fn doTheTest() void {
_ = async atest();
while (global_frame) |f| resume f;
}
fn atest() void {
expect(func(&[_]u8{ 1, 2, 3 }) == 6) catch @panic("test failure");
}
fn func(stuff: []const u8) u32 {
global_frame = @frame();
var sum: u32 = 0;
for (stuff) |x| {
suspend {}
sum += x;
}
global_frame = null;
return sum;
}
};
S.doTheTest();
}
test "suspend in while loop" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S = struct {
var global_frame: ?anyframe = null;
fn doTheTest() void {
_ = async atest();
while (global_frame) |f| resume f;
}
fn atest() void {
expect(optional(6) == 6) catch @panic("test failure");
expect(errunion(6) == 6) catch @panic("test failure");
}
fn optional(stuff: ?u32) u32 {
global_frame = @frame();
defer global_frame = null;
while (stuff) |val| {
suspend {}
return val;
}
return 0;
}
fn errunion(stuff: anyerror!u32) u32 {
global_frame = @frame();
defer global_frame = null;
while (stuff) |val| {
suspend {}
return val;
} else |err| {
err catch {};
return 0;
}
}
};
S.doTheTest();
}
test "correctly spill when returning the error union result of another async fn" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S = struct {
var global_frame: anyframe = undefined;
fn doTheTest() !void {
expect((atest() catch unreachable) == 1234) catch @panic("test failure");
}
fn atest() !i32 {
return fallible1();
}
fn fallible1() anyerror!i32 {
suspend {
global_frame = @frame();
}
return 1234;
}
};
_ = async S.doTheTest();
resume S.global_frame;
}
test "spill target expr in a for loop" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S = struct {
var global_frame: anyframe = undefined;
fn doTheTest() !void {
var foo = Foo{
.slice = &[_]i32{ 1, 2 },
};
expect(atest(&foo) == 3) catch @panic("test failure");
}
const Foo = struct {
slice: []const i32,
};
fn atest(foo: *Foo) i32 {
var sum: i32 = 0;
for (foo.slice) |x| {
suspend {
global_frame = @frame();
}
sum += x;
}
return sum;
}
};
_ = async S.doTheTest();
resume S.global_frame;
resume S.global_frame;
}
test "spill target expr in a for loop, with a var decl in the loop body" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S = struct {
var global_frame: anyframe = undefined;
fn doTheTest() !void {
var foo = Foo{
.slice = &[_]i32{ 1, 2 },
};
expect(atest(&foo) == 3) catch @panic("test failure");
}
const Foo = struct {
slice: []const i32,
};
fn atest(foo: *Foo) i32 {
var sum: i32 = 0;
for (foo.slice) |x| {
// Previously this var decl would prevent spills. This test makes sure
// the for loop spills still happen even though there is a VarDecl in scope
// before the suspend.
var anything = true;
_ = &anything;
suspend {
global_frame = @frame();
}
sum += x;
}
return sum;
}
};
_ = async S.doTheTest();
resume S.global_frame;
resume S.global_frame;
}
test "async call with @call" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S = struct {
var global_frame: anyframe = undefined;
fn doTheTest() void {
_ = @call(.{ .modifier = .async_kw }, atest, .{});
resume global_frame;
}
fn atest() void {
var frame = @call(.{ .modifier = .async_kw }, afoo, .{});
const res = await frame;
expect(res == 42) catch @panic("test failure");
}
fn afoo() i32 {
suspend {
global_frame = @frame();
}
return 42;
}
};
S.doTheTest();
}
test "async function passed 0-bit arg after non-0-bit arg" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S = struct {
var global_frame: anyframe = undefined;
var global_int: i32 = 0;
fn foo() void {
bar(1, .{}) catch unreachable;
}
fn bar(x: i32, args: anytype) anyerror!void {
_ = args;
global_frame = @frame();
suspend {}
global_int = x;
}
};
_ = async S.foo();
resume S.global_frame;
try expect(S.global_int == 1);
}
test "async function passed align(16) arg after align(8) arg" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S = struct {
var global_frame: anyframe = undefined;
var global_int: u128 = 0;
fn foo() void {
var a: u128 = 99;
_ = &a;
bar(10, .{a}) catch unreachable;
}
fn bar(x: u64, args: anytype) anyerror!void {
try expect(x == 10);
global_frame = @frame();
suspend {}
global_int = args[0];
}
};
_ = async S.foo();
resume S.global_frame;
try expect(S.global_int == 99);
}
test "async function call resolves target fn frame, comptime func" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S = struct {
var global_frame: anyframe = undefined;
var global_int: i32 = 9;
fn foo() anyerror!void {
const stack_size = 1000;
var stack_frame: [stack_size]u8 align(std.Target.stack_align) = undefined;
return await @asyncCall(&stack_frame, {}, bar, .{});
}
fn bar() anyerror!void {
global_frame = @frame();
suspend {}
global_int += 1;
}
};
_ = async S.foo();
resume S.global_frame;
try expect(S.global_int == 10);
}
test "async function call resolves target fn frame, runtime func" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S = struct {
var global_frame: anyframe = undefined;
var global_int: i32 = 9;
fn foo() anyerror!void {
const stack_size = 1000;
var stack_frame: [stack_size]u8 align(std.Target.stack_align) = undefined;
var func: fn () callconv(.@"async") anyerror!void = bar;
_ = &func;
return await @asyncCall(&stack_frame, {}, func, .{});
}
fn bar() anyerror!void {
global_frame = @frame();
suspend {}
global_int += 1;
}
};
_ = async S.foo();
resume S.global_frame;
try expect(S.global_int == 10);
}
test "properly spill optional payload capture value" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S = struct {
var global_frame: anyframe = undefined;
var global_int: usize = 2;
fn foo() void {
var opt: ?usize = 1234;
_ = &opt;
if (opt) |x| {
bar();
global_int += x;
}
}
fn bar() void {
global_frame = @frame();
suspend {}
global_int += 1;
}
};
_ = async S.foo();
resume S.global_frame;
try expect(S.global_int == 1237);
}
test "handle defer interfering with return value spill" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S = struct {
var global_frame1: anyframe = undefined;
var global_frame2: anyframe = undefined;
var finished = false;
var baz_happened = false;
fn doTheTest() !void {
_ = async testFoo();
resume global_frame1;
resume global_frame2;
try expect(baz_happened);
try expect(finished);
}
fn testFoo() void {
expectError(error.Bad, foo()) catch @panic("test failure");
finished = true;
}
fn foo() anyerror!void {
defer baz();
return bar() catch |err| return err;
}
fn bar() anyerror!void {
global_frame1 = @frame();
suspend {}
return error.Bad;
}
fn baz() void {
global_frame2 = @frame();
suspend {}
baz_happened = true;
}
};
try S.doTheTest();
}
test "take address of temporary async frame" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S = struct {
var global_frame: anyframe = undefined;
var finished = false;
fn doTheTest() !void {
_ = async asyncDoTheTest();
resume global_frame;
try expect(finished);
}
fn asyncDoTheTest() void {
expect(finishIt(&async foo(10)) == 1245) catch @panic("test failure");
finished = true;
}
fn foo(arg: i32) i32 {
global_frame = @frame();
suspend {}
return arg + 1234;
}
fn finishIt(frame: anyframe->i32) i32 {
return (await frame) + 1;
}
};
try S.doTheTest();
}
test "nosuspend await" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S = struct {
var finished = false;
fn doTheTest() !void {
var frame = async foo(false);
try expect(nosuspend await frame == 42);
finished = true;
}
fn foo(want_suspend: bool) i32 {
if (want_suspend) {
suspend {}
}
return 42;
}
};
try S.doTheTest();
try expect(S.finished);
}
test "nosuspend on function calls" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S0 = struct {
b: i32 = 42,
};
const S1 = struct {
fn c() S0 {
return S0{};
}
fn d() !S0 {
return S0{};
}
};
try expectEqual(@as(i32, 42), nosuspend S1.c().b);
try expectEqual(@as(i32, 42), (try nosuspend S1.d()).b);
}
test "nosuspend on async function calls" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S0 = struct {
b: i32 = 42,
};
const S1 = struct {
fn c() S0 {
return S0{};
}
fn d() !S0 {
return S0{};
}
};
var frame_c = nosuspend async S1.c();
try expectEqual(@as(i32, 42), (await frame_c).b);
var frame_d = nosuspend async S1.d();
try expectEqual(@as(i32, 42), (try await frame_d).b);
}
// test "resume nosuspend async function calls" {
// if (true) return error.SkipZigTest; // if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
// const S0 = struct {
// b: i32 = 42,
// };
// const S1 = struct {
// fn c() S0 {
// suspend {}
// return S0{};
// }
// fn d() !S0 {
// suspend {}
// return S0{};
// }
// };
// var frame_c = nosuspend async S1.c();
// resume frame_c;
// try expectEqual(@as(i32, 42), (await frame_c).b);
// var frame_d = nosuspend async S1.d();
// resume frame_d;
// try expectEqual(@as(i32, 42), (try await frame_d).b);
// }
test "nosuspend resume async function calls" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S0 = struct {
b: i32 = 42,
};
const S1 = struct {
fn c() S0 {
suspend {}
return S0{};
}
fn d() !S0 {
suspend {}
return S0{};
}
};
var frame_c = async S1.c();
nosuspend resume frame_c;
try expectEqual(@as(i32, 42), (await frame_c).b);
var frame_d = async S1.d();
nosuspend resume frame_d;
try expectEqual(@as(i32, 42), (try await frame_d).b);
}
test "avoid forcing frame alignment resolution implicit cast to *anyopaque" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const S = struct {
var x: ?*anyopaque = null;
fn foo() bool {
suspend {
x = @frame();
}
return true;
}
};
var frame = async S.foo();
resume @as(anyframe->bool, @ptrCast(@alignCast(S.x)));
try expect(nosuspend await frame);
}
test "@asyncCall with pass-by-value arguments" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const F0: u64 = 0xbeefbeefbeefbeef;
const F1: u64 = 0xf00df00df00df00d;
const F2: u64 = 0xcafecafecafecafe;
const S = struct {
pub const ST = struct { f0: usize, f1: usize };
pub const AT = [5]u8;
pub fn f(_fill0: u64, s: ST, _fill1: u64, a: AT, _fill2: u64) callconv(.@"async") void {
_ = s;
_ = a;
// Check that the array and struct arguments passed by value don't
// end up overflowing the adjacent fields in the frame structure.
expectEqual(F0, _fill0) catch @panic("test failure");
expectEqual(F1, _fill1) catch @panic("test failure");
expectEqual(F2, _fill2) catch @panic("test failure");
}
};
var buffer: [1024]u8 align(@alignOf(@Frame(S.f))) = undefined;
// The function pointer must not be comptime-known.
var t = S.f;
_ = &t;
var frame_ptr = @asyncCall(&buffer, {}, t, .{
F0,
.{ .f0 = 1, .f1 = 2 },
F1,
[_]u8{ 1, 2, 3, 4, 5 },
F2,
});
_ = &frame_ptr;
}
test "@asyncCall with arguments having non-standard alignment" {
if (true) return error.SkipZigTest; // TODO
if (builtin.os.tag == .wasi) return error.SkipZigTest; // TODO
const F0: u64 = 0xbeefbeef;
const F1: u64 = 0xf00df00df00df00d;
const S = struct {
pub fn f(_fill0: u32, s: struct { x: u64 align(16) }, _fill1: u64) callconv(.@"async") void {
_ = s;
// The compiler inserts extra alignment for s, check that the
// generated code picks the right slot for fill1.
expectEqual(F0, _fill0) catch @panic("test failure");
expectEqual(F1, _fill1) catch @panic("test failure");
}
};
var buffer: [1024]u8 align(@alignOf(@Frame(S.f))) = undefined;
// The function pointer must not be comptime-known.
var t = S.f;
_ = &t;
var frame_ptr = @asyncCall(&buffer, {}, t, .{ F0, undefined, F1 });
_ = &frame_ptr;
}
-47
View File
@@ -1,47 +0,0 @@
const std = @import("std");
const builtin = @import("builtin");
const expect = std.testing.expect;
const Foo = struct {
x: i32,
};
var await_a_promise: anyframe = undefined;
var await_final_result = Foo{ .x = 0 };
test "coroutine await struct" {
if (true) return error.SkipZigTest; // TODO
await_seq('a');
var p = async await_amain();
_ = &p;
await_seq('f');
resume await_a_promise;
await_seq('i');
try expect(await_final_result.x == 1234);
try expect(std.mem.eql(u8, &await_points, "abcdefghi"));
}
fn await_amain() callconv(.@"async") void {
await_seq('b');
var p = async await_another();
await_seq('e');
await_final_result = await p;
await_seq('h');
}
fn await_another() callconv(.@"async") Foo {
await_seq('c');
suspend {
await_seq('d');
await_a_promise = @frame();
}
await_seq('g');
return Foo{ .x = 1234 };
}
var await_points = [_]u8{0} ** "abcdefghi".len;
var await_seq_index: usize = 0;
fn await_seq(c: u8) void {
await_points[await_seq_index] = c;
await_seq_index += 1;
}
+3 -3
View File
@@ -37,7 +37,7 @@ test "basic invocations" {
comptime {
// comptime calls with supported modifiers
try expect(@call(.auto, foo, .{2}) == 1234);
try expect(@call(.no_async, foo, .{3}) == 1234);
try expect(@call(.no_suspend, foo, .{3}) == 1234);
try expect(@call(.always_tail, foo, .{4}) == 1234);
try expect(@call(.always_inline, foo, .{5}) == 1234);
}
@@ -45,7 +45,7 @@ test "basic invocations" {
const result = @call(.compile_time, foo, .{6}) == 1234;
comptime assert(result);
// runtime calls of comptime-known function
try expect(@call(.no_async, foo, .{7}) == 1234);
try expect(@call(.no_suspend, foo, .{7}) == 1234);
try expect(@call(.never_tail, foo, .{8}) == 1234);
try expect(@call(.never_inline, foo, .{9}) == 1234);
// CBE does not support attributes on runtime functions
@@ -53,7 +53,7 @@ test "basic invocations" {
// runtime calls of non comptime-known function
var alias_foo = &foo;
_ = &alias_foo;
try expect(@call(.no_async, alias_foo, .{10}) == 1234);
try expect(@call(.no_suspend, alias_foo, .{10}) == 1234);
try expect(@call(.never_tail, alias_foo, .{11}) == 1234);
try expect(@call(.never_inline, alias_foo, .{12}) == 1234);
}
@@ -1,13 +0,0 @@
export fn entry() void {
_ = async amain();
}
fn amain() callconv(.@"async") void {
var x: [@sizeOf(@Frame(amain))]u8 = undefined;
_ = &x;
}
// error
// backend=stage1
// target=native
//
// tmp.zig:4:1: error: cannot resolve '@Frame(amain)': function not fully analyzed yet
@@ -1,17 +0,0 @@
export fn entry() void {
_ = async amain();
}
fn amain() callconv(.@"async") void {
other();
}
fn other() void {
var x: [@sizeOf(@Frame(amain))]u8 = undefined;
_ = &x;
}
// error
// backend=stage1
// target=native
//
// tmp.zig:4:1: error: unable to determine async function frame of 'amain'
// tmp.zig:5:10: note: analysis of function 'other' depends on the frame
@@ -1,19 +0,0 @@
export fn a() void {
const f = async func();
resume f;
}
export fn b() void {
const f = async func();
var x: anyframe = &f;
_ = &x;
}
fn func() void {
suspend {}
}
// error
// backend=stage1
// target=native
//
// tmp.zig:3:12: error: expected type 'anyframe', found '*const @Frame(func)'
// tmp.zig:7:24: error: expected type 'anyframe', found '*const @Frame(func)'
@@ -1,18 +0,0 @@
export fn entry() void {
foo();
}
fn foo() void {
bar();
}
fn bar() void {
suspend {}
}
// error
// backend=stage1
// target=native
//
// tmp.zig:1:1: error: function with calling convention 'C' cannot be async
// tmp.zig:2:8: note: async function call here
// tmp.zig:5:8: note: async function call here
// tmp.zig:8:5: note: suspends here
@@ -1,36 +0,0 @@
var frame: ?anyframe = null;
export fn a() void {
_ = async rangeSum(10);
while (frame) |f| resume f;
}
fn rangeSum(x: i32) i32 {
suspend {
frame = @frame();
}
frame = null;
if (x == 0) return 0;
const child = rangeSumIndirect(x - 1);
return child + 1;
}
fn rangeSumIndirect(x: i32) i32 {
suspend {
frame = @frame();
}
frame = null;
if (x == 0) return 0;
const child = rangeSum(x - 1);
return child + 1;
}
// error
// backend=stage1
// target=native
//
// tmp.zig:8:1: error: '@Frame(rangeSum)' depends on itself
// tmp.zig:15:35: note: when analyzing type '@Frame(rangeSum)' here
// tmp.zig:28:25: note: when analyzing type '@Frame(rangeSumIndirect)' here
@@ -1,15 +0,0 @@
export fn entry() void {
var frame = async func();
var result = await frame;
_ = &result;
}
fn func() void {
suspend {}
}
// error
// backend=stage1
// target=native
//
// tmp.zig:1:1: error: function with calling convention 'C' cannot be async
// tmp.zig:3:18: note: await here is a suspend point
@@ -1,12 +0,0 @@
export fn entry() void {
_ = async amain();
}
fn amain() callconv(.@"async") void {
return error.ShouldBeCompileError;
}
// error
// backend=stage1
// target=native
//
// tmp.zig:5:17: error: expected type 'void', found 'error{ShouldBeCompileError}'
@@ -1,15 +0,0 @@
export fn entry() void {
_ = async amain();
}
fn amain() void {
var ptr = afunc;
_ = ptr();
_ = &ptr;
}
fn afunc() callconv(.@"async") void {}
// error
// backend=stage1
// target=native
//
// tmp.zig:6:12: error: function is not comptime-known; @asyncCall required
@@ -1,13 +0,0 @@
export fn entry() void {
var ptr = afunc;
_ = async ptr();
_ = &ptr;
}
fn afunc() callconv(.@"async") void {}
// error
// backend=stage1
// target=native
//
// tmp.zig:3:15: error: function is not comptime-known; @asyncCall required
@@ -1,16 +0,0 @@
export fn entry() void {
var frame: @Frame(foo) = undefined;
frame = async bar();
}
fn foo() void {
suspend {}
}
fn bar() void {
suspend {}
}
// error
// backend=stage1
// target=native
//
// tmp.zig:3:13: error: expected type '*@Frame(bar)', found '*@Frame(foo)'
@@ -1,16 +0,0 @@
export fn entry() void {
_ = async amain();
}
fn amain() i32 {
var frame: @Frame(foo) = undefined;
return await @asyncCall(&frame, false, foo, .{});
}
fn foo() i32 {
return 1234;
}
// error
// backend=stage1
// target=native
//
// tmp.zig:6:37: error: expected type '*i32', found 'bool'
@@ -1,15 +0,0 @@
export fn entry() void {
nosuspend {
const bar = async foo();
suspend {}
resume bar;
}
}
fn foo() void {}
// error
// backend=stage2
// target=native
//
// :4:9: error: suspend inside nosuspend block
// :2:5: note: nosuspend block here
@@ -1,15 +0,0 @@
export fn entry() void {
_ = async foo();
}
fn foo() void {
suspend {
suspend {}
}
}
// error
// backend=stage2
// target=native
//
// :6:9: error: cannot suspend inside suspend block
// :5:5: note: other suspend block here
@@ -1,26 +0,0 @@
const std = @import("std");
const builtin = @import("builtin");
pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
_ = message;
_ = stack_trace;
std.process.exit(0);
}
pub fn main() !void {
if (builtin.zig_backend == .stage1 and builtin.os.tag == .wasi) {
// TODO file a bug for this failure
std.process.exit(0); // skip the test
}
var bytes: [1]u8 align(16) = undefined;
var ptr = other;
_ = &ptr;
var frame = @asyncCall(&bytes, {}, ptr, .{});
_ = &frame;
return error.TestFailed;
}
fn other() callconv(.@"async") void {
suspend {}
}
// run
// backend=stage1
// target=native
-29
View File
@@ -1,29 +0,0 @@
const std = @import("std");
pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
_ = message;
_ = stack_trace;
std.process.exit(0);
}
var frame: anyframe = undefined;
pub fn main() !void {
_ = async amain();
resume frame;
return error.TestFailed;
}
fn amain() void {
var f = async func();
await f;
await f;
}
fn func() void {
suspend {
frame = @frame();
}
}
// run
// backend=stage1
// target=native
@@ -1,38 +0,0 @@
const std = @import("std");
pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
_ = message;
_ = stack_trace;
std.process.exit(0);
}
var failing_frame: @Frame(failing) = undefined;
pub fn main() !void {
const p = nonFailing();
resume p;
const p2 = async printTrace(p);
_ = p2;
return error.TestFailed;
}
fn nonFailing() anyframe->anyerror!void {
failing_frame = async failing();
return &failing_frame;
}
fn failing() anyerror!void {
suspend {}
return second();
}
fn second() callconv(.@"async") anyerror!void {
return error.Fail;
}
fn printTrace(p: anyframe->anyerror!void) void {
(await p) catch unreachable;
}
// run
// backend=stage1
// target=native
@@ -1,19 +0,0 @@
const std = @import("std");
pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
_ = message;
_ = stack_trace;
std.process.exit(0);
}
pub fn main() !void {
var p = async suspendOnce();
resume p; //ok
resume p; //bad
return error.TestFailed;
}
fn suspendOnce() void {
suspend {}
}
// run
// backend=stage1
// target=native
@@ -1,21 +0,0 @@
const std = @import("std");
pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
_ = message;
_ = stack_trace;
std.process.exit(0);
}
pub fn main() !void {
var frame = async first();
resume frame;
return error.TestFailed;
}
fn first() void {
other();
}
fn other() void {
suspend {}
}
// run
// backend=stage1
// target=native
@@ -1,22 +0,0 @@
const std = @import("std");
pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
_ = message;
_ = stack_trace;
std.process.exit(0);
}
pub fn main() !void {
var frame = async first();
resume frame;
return error.TestFailed;
}
fn first() void {
var frame = async other();
await frame;
}
fn other() void {
suspend {}
}
// run
// backend=stage1
// target=native
@@ -1,32 +0,0 @@
const std = @import("std");
pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
_ = message;
_ = stack_trace;
std.process.exit(0);
}
fn foo() void {
suspend {
global_frame = @frame();
}
var f = async bar(@frame());
_ = &f;
std.process.exit(1);
}
fn bar(frame: anyframe) void {
suspend {
resume frame;
}
std.process.exit(1);
}
var global_frame: anyframe = undefined;
pub fn main() !void {
_ = async foo();
resume global_frame;
std.process.exit(1);
}
// run
// backend=stage1
// target=native
@@ -1,27 +0,0 @@
const std = @import("std");
pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
_ = message;
_ = stack_trace;
std.process.exit(0);
}
fn foo() void {
var f = async bar(@frame());
_ = &f;
std.process.exit(1);
}
fn bar(frame: anyframe) void {
suspend {
resume frame;
}
std.process.exit(1);
}
pub fn main() !void {
_ = async foo();
return error.TestFailed;
}
// run
// backend=stage1
// target=native
-2
View File
@@ -710,8 +710,6 @@ fn tokenizeAndPrintRaw(
.keyword_align,
.keyword_and,
.keyword_asm,
.keyword_async,
.keyword_await,
.keyword_break,
.keyword_catch,
.keyword_comptime,
-2
View File
@@ -653,8 +653,6 @@ fn tokenizeAndPrint(arena: Allocator, out: anytype, raw_src: []const u8) !void {
.keyword_align,
.keyword_and,
.keyword_asm,
.keyword_async,
.keyword_await,
.keyword_break,
.keyword_catch,
.keyword_comptime,
-2
View File
@@ -50,8 +50,6 @@ zig_keywords = {
'anyframe',
'anytype',
'asm',
'async',
'await',
'break',
'callconv',
'catch',