mirror of
https://github.com/ziglang/zig.git
synced 2026-02-21 16:54:52 +00:00
Merge pull request #4700 from Vexu/noasync
Implement new noasync syntax
This commit is contained in:
commit
638d5c3aca
@ -431,6 +431,7 @@ pub const Node = struct {
|
||||
ContainerDecl,
|
||||
Asm,
|
||||
Comptime,
|
||||
Noasync,
|
||||
Block,
|
||||
|
||||
// Misc
|
||||
@ -1078,6 +1079,29 @@ pub const Node = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub const Noasync = struct {
|
||||
base: Node = Node{ .id = .Noasync },
|
||||
noasync_token: TokenIndex,
|
||||
expr: *Node,
|
||||
|
||||
pub fn iterate(self: *Noasync, index: usize) ?*Node {
|
||||
var i = index;
|
||||
|
||||
if (i < 1) return self.expr;
|
||||
i -= 1;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn firstToken(self: *const Noasync) TokenIndex {
|
||||
return self.noasync_token;
|
||||
}
|
||||
|
||||
pub fn lastToken(self: *const Noasync) TokenIndex {
|
||||
return self.expr.lastToken();
|
||||
}
|
||||
};
|
||||
|
||||
pub const Payload = struct {
|
||||
base: Node = Node{ .id = .Payload },
|
||||
lpipe: TokenIndex,
|
||||
@ -1560,9 +1584,7 @@ pub const Node = struct {
|
||||
pub const Op = union(enum) {
|
||||
AddressOf,
|
||||
ArrayType: ArrayInfo,
|
||||
Await: struct {
|
||||
noasync_token: ?TokenIndex = null,
|
||||
},
|
||||
Await,
|
||||
BitNot,
|
||||
BoolNot,
|
||||
Cancel,
|
||||
|
||||
@ -462,6 +462,7 @@ fn parseContainerField(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*No
|
||||
/// Statement
|
||||
/// <- KEYWORD_comptime? VarDecl
|
||||
/// / KEYWORD_comptime BlockExprStatement
|
||||
/// / KEYWORD_noasync BlockExprStatement
|
||||
/// / KEYWORD_suspend (SEMICOLON / BlockExprStatement)
|
||||
/// / KEYWORD_defer BlockExprStatement
|
||||
/// / KEYWORD_errdefer BlockExprStatement
|
||||
@ -493,6 +494,19 @@ fn parseStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) Error!?*No
|
||||
return &node.base;
|
||||
}
|
||||
|
||||
if (eatToken(it, .Keyword_noasync)) |noasync_token| {
|
||||
const block_expr = try expectNode(arena, it, tree, parseBlockExprStatement, .{
|
||||
.ExpectedBlockOrAssignment = .{ .token = it.index },
|
||||
});
|
||||
|
||||
const node = try arena.create(Node.Noasync);
|
||||
node.* = .{
|
||||
.noasync_token = noasync_token,
|
||||
.expr = block_expr,
|
||||
};
|
||||
return &node.base;
|
||||
}
|
||||
|
||||
if (eatToken(it, .Keyword_suspend)) |suspend_token| {
|
||||
const semicolon = eatToken(it, .Semicolon);
|
||||
|
||||
@ -856,6 +870,7 @@ fn parsePrefixExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
|
||||
/// / IfExpr
|
||||
/// / KEYWORD_break BreakLabel? Expr?
|
||||
/// / KEYWORD_comptime Expr
|
||||
/// / KEYWORD_noasync Expr
|
||||
/// / KEYWORD_continue BreakLabel?
|
||||
/// / KEYWORD_resume Expr
|
||||
/// / KEYWORD_return Expr?
|
||||
@ -870,7 +885,7 @@ fn parsePrimaryExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
|
||||
const label = try parseBreakLabel(arena, it, tree);
|
||||
const expr_node = try parseExpr(arena, it, tree);
|
||||
const node = try arena.create(Node.ControlFlowExpression);
|
||||
node.* = Node.ControlFlowExpression{
|
||||
node.* = .{
|
||||
.ltoken = token,
|
||||
.kind = Node.ControlFlowExpression.Kind{ .Break = label },
|
||||
.rhs = expr_node,
|
||||
@ -883,7 +898,7 @@ fn parsePrimaryExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
|
||||
.ExpectedExpr = AstError.ExpectedExpr{ .token = it.index },
|
||||
});
|
||||
const node = try arena.create(Node.Comptime);
|
||||
node.* = Node.Comptime{
|
||||
node.* = .{
|
||||
.doc_comments = null,
|
||||
.comptime_token = token,
|
||||
.expr = expr_node,
|
||||
@ -891,10 +906,22 @@ fn parsePrimaryExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
|
||||
return &node.base;
|
||||
}
|
||||
|
||||
if (eatToken(it, .Keyword_noasync)) |token| {
|
||||
const expr_node = try expectNode(arena, it, tree, parseExpr, AstError{
|
||||
.ExpectedExpr = AstError.ExpectedExpr{ .token = it.index },
|
||||
});
|
||||
const node = try arena.create(Node.Noasync);
|
||||
node.* = .{
|
||||
.noasync_token = token,
|
||||
.expr = expr_node,
|
||||
};
|
||||
return &node.base;
|
||||
}
|
||||
|
||||
if (eatToken(it, .Keyword_continue)) |token| {
|
||||
const label = try parseBreakLabel(arena, it, tree);
|
||||
const node = try arena.create(Node.ControlFlowExpression);
|
||||
node.* = Node.ControlFlowExpression{
|
||||
node.* = .{
|
||||
.ltoken = token,
|
||||
.kind = Node.ControlFlowExpression.Kind{ .Continue = label },
|
||||
.rhs = null,
|
||||
@ -907,7 +934,7 @@ fn parsePrimaryExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
|
||||
.ExpectedExpr = AstError.ExpectedExpr{ .token = it.index },
|
||||
});
|
||||
const node = try arena.create(Node.PrefixOp);
|
||||
node.* = Node.PrefixOp{
|
||||
node.* = .{
|
||||
.op_token = token,
|
||||
.op = Node.PrefixOp.Op.Resume,
|
||||
.rhs = expr_node,
|
||||
@ -918,7 +945,7 @@ fn parsePrimaryExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
|
||||
if (eatToken(it, .Keyword_return)) |token| {
|
||||
const expr_node = try parseExpr(arena, it, tree);
|
||||
const node = try arena.create(Node.ControlFlowExpression);
|
||||
node.* = Node.ControlFlowExpression{
|
||||
node.* = .{
|
||||
.ltoken = token,
|
||||
.kind = Node.ControlFlowExpression.Kind.Return,
|
||||
.rhs = expr_node,
|
||||
@ -1126,19 +1153,18 @@ fn parseErrorUnionExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*No
|
||||
|
||||
/// SuffixExpr
|
||||
/// <- KEYWORD_async PrimaryTypeExpr SuffixOp* FnCallArguments
|
||||
/// / KEYWORD_noasync PrimaryTypeExpr SuffixOp* FnCallArguments
|
||||
/// / PrimaryTypeExpr (SuffixOp / FnCallArguments)*
|
||||
fn parseSuffixExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
|
||||
const maybe_async = eatAnnotatedToken(it, .Keyword_async) orelse eatAnnotatedToken(it, .Keyword_noasync);
|
||||
const maybe_async = eatToken(it, .Keyword_async);
|
||||
if (maybe_async) |async_token| {
|
||||
const token_fn = eatToken(it, .Keyword_fn);
|
||||
if (async_token.ptr.id == .Keyword_async and token_fn != null) {
|
||||
if (token_fn != null) {
|
||||
// HACK: If we see the keyword `fn`, then we assume that
|
||||
// we are parsing an async fn proto, and not a call.
|
||||
// We therefore put back all tokens consumed by the async
|
||||
// prefix...
|
||||
putBackToken(it, token_fn.?);
|
||||
putBackToken(it, async_token.index);
|
||||
putBackToken(it, async_token);
|
||||
return parsePrimaryTypeExpr(arena, it, tree);
|
||||
}
|
||||
// TODO: Implement hack for parsing `async fn ...` in ast_parse_suffix_expr
|
||||
@ -1167,7 +1193,7 @@ fn parseSuffixExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
|
||||
.op = Node.SuffixOp.Op{
|
||||
.Call = Node.SuffixOp.Op.Call{
|
||||
.params = params.list,
|
||||
.async_token = async_token.index,
|
||||
.async_token = async_token,
|
||||
},
|
||||
},
|
||||
.rtoken = params.rparen,
|
||||
@ -1224,6 +1250,7 @@ fn parseSuffixExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
|
||||
/// / IfTypeExpr
|
||||
/// / INTEGER
|
||||
/// / KEYWORD_comptime TypeExpr
|
||||
/// / KEYWORD_noasync TypeExpr
|
||||
/// / KEYWORD_error DOT IDENTIFIER
|
||||
/// / KEYWORD_false
|
||||
/// / KEYWORD_null
|
||||
@ -1255,13 +1282,22 @@ fn parsePrimaryTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*N
|
||||
if (eatToken(it, .Keyword_comptime)) |token| {
|
||||
const expr = (try parseTypeExpr(arena, it, tree)) orelse return null;
|
||||
const node = try arena.create(Node.Comptime);
|
||||
node.* = Node.Comptime{
|
||||
node.* = .{
|
||||
.doc_comments = null,
|
||||
.comptime_token = token,
|
||||
.expr = expr,
|
||||
};
|
||||
return &node.base;
|
||||
}
|
||||
if (eatToken(it, .Keyword_noasync)) |token| {
|
||||
const expr = (try parseTypeExpr(arena, it, tree)) orelse return null;
|
||||
const node = try arena.create(Node.Noasync);
|
||||
node.* = .{
|
||||
.noasync_token = token,
|
||||
.expr = expr,
|
||||
};
|
||||
return &node.base;
|
||||
}
|
||||
if (eatToken(it, .Keyword_error)) |token| {
|
||||
const period = try expectToken(it, tree, .Period);
|
||||
const identifier = try expectNode(arena, it, tree, parseIdentifier, AstError{
|
||||
@ -1269,7 +1305,7 @@ fn parsePrimaryTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*N
|
||||
});
|
||||
const global_error_set = try createLiteral(arena, Node.ErrorType, token);
|
||||
const node = try arena.create(Node.InfixOp);
|
||||
node.* = Node.InfixOp{
|
||||
node.* = .{
|
||||
.op_token = period,
|
||||
.lhs = global_error_set,
|
||||
.op = Node.InfixOp.Op.Period,
|
||||
@ -1281,7 +1317,7 @@ fn parsePrimaryTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*N
|
||||
if (eatToken(it, .Keyword_null)) |token| return createLiteral(arena, Node.NullLiteral, token);
|
||||
if (eatToken(it, .Keyword_anyframe)) |token| {
|
||||
const node = try arena.create(Node.AnyFrameType);
|
||||
node.* = Node.AnyFrameType{
|
||||
node.* = .{
|
||||
.anyframe_token = token,
|
||||
.result = null,
|
||||
};
|
||||
@ -2180,18 +2216,6 @@ fn parsePrefixOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
|
||||
.Ampersand => ops{ .AddressOf = {} },
|
||||
.Keyword_try => ops{ .Try = {} },
|
||||
.Keyword_await => ops{ .Await = .{} },
|
||||
.Keyword_noasync => if (eatToken(it, .Keyword_await)) |await_tok| {
|
||||
const node = try arena.create(Node.PrefixOp);
|
||||
node.* = Node.PrefixOp{
|
||||
.op_token = await_tok,
|
||||
.op = .{ .Await = .{ .noasync_token = token.index } },
|
||||
.rhs = undefined, // set by caller
|
||||
};
|
||||
return &node.base;
|
||||
} else {
|
||||
putBackToken(it, token.index);
|
||||
return null;
|
||||
},
|
||||
else => {
|
||||
putBackToken(it, token.index);
|
||||
return null;
|
||||
|
||||
@ -390,6 +390,12 @@ fn renderExpression(
|
||||
try renderToken(tree, stream, comptime_node.comptime_token, indent, start_col, Space.Space);
|
||||
return renderExpression(allocator, stream, tree, indent, start_col, comptime_node.expr, space);
|
||||
},
|
||||
.Noasync => {
|
||||
const noasync_node = @fieldParentPtr(ast.Node.Noasync, "base", base);
|
||||
|
||||
try renderToken(tree, stream, noasync_node.noasync_token, indent, start_col, Space.Space);
|
||||
return renderExpression(allocator, stream, tree, indent, start_col, noasync_node.expr, space);
|
||||
},
|
||||
|
||||
.Suspend => {
|
||||
const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", base);
|
||||
@ -590,9 +596,6 @@ fn renderExpression(
|
||||
},
|
||||
|
||||
.Await => |await_info| {
|
||||
if (await_info.noasync_token) |tok| {
|
||||
try renderToken(tree, stream, tok, indent, start_col, Space.Space);
|
||||
}
|
||||
try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.Space);
|
||||
},
|
||||
}
|
||||
|
||||
@ -651,6 +651,7 @@ enum NodeType {
|
||||
NodeTypeSwitchProng,
|
||||
NodeTypeSwitchRange,
|
||||
NodeTypeCompTime,
|
||||
NodeTypeNoAsync,
|
||||
NodeTypeBreak,
|
||||
NodeTypeContinue,
|
||||
NodeTypeAsmExpr,
|
||||
@ -991,6 +992,10 @@ struct AstNodeCompTime {
|
||||
AstNode *expr;
|
||||
};
|
||||
|
||||
struct AstNodeNoAsync {
|
||||
AstNode *expr;
|
||||
};
|
||||
|
||||
struct AsmOutput {
|
||||
Buf *asm_symbolic_name;
|
||||
Buf *constraint;
|
||||
@ -1148,7 +1153,6 @@ struct AstNodeErrorType {
|
||||
};
|
||||
|
||||
struct AstNodeAwaitExpr {
|
||||
Token *noasync_token;
|
||||
AstNode *expr;
|
||||
};
|
||||
|
||||
@ -1199,6 +1203,7 @@ struct AstNode {
|
||||
AstNodeSwitchProng switch_prong;
|
||||
AstNodeSwitchRange switch_range;
|
||||
AstNodeCompTime comptime_expr;
|
||||
AstNodeNoAsync noasync_expr;
|
||||
AstNodeAsmExpr asm_expr;
|
||||
AstNodeFieldAccessExpr field_access_expr;
|
||||
AstNodePtrDerefExpr ptr_deref_expr;
|
||||
@ -2325,6 +2330,7 @@ enum ScopeId {
|
||||
ScopeIdRuntime,
|
||||
ScopeIdTypeOf,
|
||||
ScopeIdExpr,
|
||||
ScopeIdNoAsync,
|
||||
};
|
||||
|
||||
struct Scope {
|
||||
@ -2457,6 +2463,11 @@ struct ScopeCompTime {
|
||||
Scope base;
|
||||
};
|
||||
|
||||
// This scope is created for a noasync expression.
|
||||
// NodeTypeNoAsync
|
||||
struct ScopeNoAsync {
|
||||
Scope base;
|
||||
};
|
||||
|
||||
// This scope is created for a function definition.
|
||||
// NodeTypeFnDef
|
||||
|
||||
@ -106,6 +106,7 @@ static ScopeExpr *find_expr_scope(Scope *scope) {
|
||||
case ScopeIdDecls:
|
||||
case ScopeIdFnDef:
|
||||
case ScopeIdCompTime:
|
||||
case ScopeIdNoAsync:
|
||||
case ScopeIdVarDecl:
|
||||
case ScopeIdCImport:
|
||||
case ScopeIdSuspend:
|
||||
@ -226,6 +227,12 @@ Scope *create_comptime_scope(CodeGen *g, AstNode *node, Scope *parent) {
|
||||
return &scope->base;
|
||||
}
|
||||
|
||||
Scope *create_noasync_scope(CodeGen *g, AstNode *node, Scope *parent) {
|
||||
ScopeNoAsync *scope = heap::c_allocator.create<ScopeNoAsync>();
|
||||
init_scope(g, &scope->base, ScopeIdNoAsync, node, parent);
|
||||
return &scope->base;
|
||||
}
|
||||
|
||||
Scope *create_typeof_scope(CodeGen *g, AstNode *node, Scope *parent) {
|
||||
ScopeTypeOf *scope = heap::c_allocator.create<ScopeTypeOf>();
|
||||
init_scope(g, &scope->base, ScopeIdTypeOf, node, parent);
|
||||
@ -3755,6 +3762,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {
|
||||
case NodeTypeCompTime:
|
||||
preview_comptime_decl(g, node, decls_scope);
|
||||
break;
|
||||
case NodeTypeNoAsync:
|
||||
case NodeTypeParamDecl:
|
||||
case NodeTypeReturnExpr:
|
||||
case NodeTypeDefer:
|
||||
@ -6176,6 +6184,7 @@ static void mark_suspension_point(Scope *scope) {
|
||||
case ScopeIdDecls:
|
||||
case ScopeIdFnDef:
|
||||
case ScopeIdCompTime:
|
||||
case ScopeIdNoAsync:
|
||||
case ScopeIdCImport:
|
||||
case ScopeIdSuspend:
|
||||
case ScopeIdTypeOf:
|
||||
|
||||
@ -125,6 +125,7 @@ ScopeLoop *create_loop_scope(CodeGen *g, AstNode *node, Scope *parent);
|
||||
ScopeSuspend *create_suspend_scope(CodeGen *g, AstNode *node, Scope *parent);
|
||||
ScopeFnDef *create_fndef_scope(CodeGen *g, AstNode *node, Scope *parent, ZigFn *fn_entry);
|
||||
Scope *create_comptime_scope(CodeGen *g, AstNode *node, Scope *parent);
|
||||
Scope *create_noasync_scope(CodeGen *g, AstNode *node, Scope *parent);
|
||||
Scope *create_runtime_scope(CodeGen *g, AstNode *node, Scope *parent, IrInstSrc *is_comptime);
|
||||
Scope *create_typeof_scope(CodeGen *g, AstNode *node, Scope *parent);
|
||||
ScopeExpr *create_expr_scope(CodeGen *g, AstNode *node, Scope *parent);
|
||||
|
||||
@ -220,6 +220,8 @@ static const char *node_type_str(NodeType node_type) {
|
||||
return "SwitchRange";
|
||||
case NodeTypeCompTime:
|
||||
return "CompTime";
|
||||
case NodeTypeNoAsync:
|
||||
return "NoAsync";
|
||||
case NodeTypeBreak:
|
||||
return "Break";
|
||||
case NodeTypeContinue:
|
||||
@ -1091,6 +1093,12 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
|
||||
render_node_grouped(ar, node->data.comptime_expr.expr);
|
||||
break;
|
||||
}
|
||||
case NodeTypeNoAsync:
|
||||
{
|
||||
fprintf(ar->f, "noasync ");
|
||||
render_node_grouped(ar, node->data.noasync_expr.expr);
|
||||
break;
|
||||
}
|
||||
case NodeTypeForExpr:
|
||||
{
|
||||
if (node->data.for_expr.name != nullptr) {
|
||||
|
||||
@ -687,6 +687,7 @@ static ZigLLVMDIScope *get_di_scope(CodeGen *g, Scope *scope) {
|
||||
case ScopeIdLoop:
|
||||
case ScopeIdSuspend:
|
||||
case ScopeIdCompTime:
|
||||
case ScopeIdNoAsync:
|
||||
case ScopeIdRuntime:
|
||||
case ScopeIdTypeOf:
|
||||
case ScopeIdExpr:
|
||||
@ -968,7 +969,7 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) {
|
||||
case PanicMsgIdResumedFnPendingAwait:
|
||||
return buf_create_from_str("resumed an async function which can only be awaited");
|
||||
case PanicMsgIdBadNoAsyncCall:
|
||||
return buf_create_from_str("async function called with noasync suspended");
|
||||
return buf_create_from_str("async function called in noasync scope suspended");
|
||||
case PanicMsgIdResumeNotSuspendedFn:
|
||||
return buf_create_from_str("resumed a non-suspended function");
|
||||
case PanicMsgIdBadSentinel:
|
||||
@ -3934,6 +3935,7 @@ static void render_async_var_decls(CodeGen *g, Scope *scope) {
|
||||
case ScopeIdLoop:
|
||||
case ScopeIdSuspend:
|
||||
case ScopeIdCompTime:
|
||||
case ScopeIdNoAsync:
|
||||
case ScopeIdRuntime:
|
||||
case ScopeIdTypeOf:
|
||||
case ScopeIdExpr:
|
||||
|
||||
48
src/ir.cpp
48
src/ir.cpp
@ -4978,6 +4978,7 @@ static void ir_count_defers(IrBuilderSrc *irb, Scope *inner_scope, Scope *outer_
|
||||
case ScopeIdLoop:
|
||||
case ScopeIdSuspend:
|
||||
case ScopeIdCompTime:
|
||||
case ScopeIdNoAsync:
|
||||
case ScopeIdRuntime:
|
||||
case ScopeIdTypeOf:
|
||||
case ScopeIdExpr:
|
||||
@ -5033,6 +5034,7 @@ static bool ir_gen_defers_for_block(IrBuilderSrc *irb, Scope *inner_scope, Scope
|
||||
case ScopeIdLoop:
|
||||
case ScopeIdSuspend:
|
||||
case ScopeIdCompTime:
|
||||
case ScopeIdNoAsync:
|
||||
case ScopeIdRuntime:
|
||||
case ScopeIdTypeOf:
|
||||
case ScopeIdExpr:
|
||||
@ -7307,6 +7309,18 @@ static IrInstSrc *ir_gen_builtin_fn_call(IrBuilderSrc *irb, Scope *scope, AstNod
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
static ScopeNoAsync *get_scope_noasync(Scope *scope) {
|
||||
while (scope) {
|
||||
if (scope->id == ScopeIdNoAsync)
|
||||
return (ScopeNoAsync *)scope;
|
||||
if (scope->id == ScopeIdFnDef)
|
||||
return nullptr;
|
||||
|
||||
scope = scope->parent;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static IrInstSrc *ir_gen_fn_call(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval,
|
||||
ResultLoc *result_loc)
|
||||
{
|
||||
@ -7315,8 +7329,19 @@ static IrInstSrc *ir_gen_fn_call(IrBuilderSrc *irb, Scope *scope, AstNode *node,
|
||||
if (node->data.fn_call_expr.modifier == CallModifierBuiltin)
|
||||
return ir_gen_builtin_fn_call(irb, scope, node, lval, result_loc);
|
||||
|
||||
bool is_noasync = get_scope_noasync(scope) != nullptr;
|
||||
CallModifier modifier = node->data.fn_call_expr.modifier;
|
||||
if (is_noasync) {
|
||||
if (modifier == CallModifierAsync) {
|
||||
add_node_error(irb->codegen, node,
|
||||
buf_sprintf("async call in noasync scope"));
|
||||
return irb->codegen->invalid_inst_src;
|
||||
}
|
||||
modifier = CallModifierNoAsync;
|
||||
}
|
||||
|
||||
AstNode *fn_ref_node = node->data.fn_call_expr.fn_ref_expr;
|
||||
return ir_gen_fn_call_with_args(irb, scope, node, fn_ref_node, node->data.fn_call_expr.modifier,
|
||||
return ir_gen_fn_call_with_args(irb, scope, node, fn_ref_node, modifier,
|
||||
nullptr, node->data.fn_call_expr.params.items, node->data.fn_call_expr.params.length, lval, result_loc);
|
||||
}
|
||||
|
||||
@ -9170,6 +9195,14 @@ static IrInstSrc *ir_gen_comptime(IrBuilderSrc *irb, Scope *parent_scope, AstNod
|
||||
return ir_gen_node_extra(irb, node->data.comptime_expr.expr, child_scope, lval, nullptr);
|
||||
}
|
||||
|
||||
static IrInstSrc *ir_gen_noasync(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node, LVal lval) {
|
||||
assert(node->type == NodeTypeNoAsync);
|
||||
|
||||
Scope *child_scope = create_noasync_scope(irb->codegen, node, parent_scope);
|
||||
// purposefully pass null for result_loc and let EndExpr handle it
|
||||
return ir_gen_node_extra(irb, node->data.comptime_expr.expr, child_scope, lval, nullptr);
|
||||
}
|
||||
|
||||
static IrInstSrc *ir_gen_return_from_block(IrBuilderSrc *irb, Scope *break_scope, AstNode *node, ScopeBlock *block_scope) {
|
||||
IrInstSrc *is_comptime;
|
||||
if (ir_should_inline(irb->exec, break_scope)) {
|
||||
@ -9750,6 +9783,10 @@ static IrInstSrc *ir_gen_fn_proto(IrBuilderSrc *irb, Scope *parent_scope, AstNod
|
||||
|
||||
static IrInstSrc *ir_gen_resume(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
|
||||
assert(node->type == NodeTypeResume);
|
||||
if (get_scope_noasync(scope) != nullptr) {
|
||||
add_node_error(irb->codegen, node, buf_sprintf("resume in noasync scope"));
|
||||
return irb->codegen->invalid_inst_src;
|
||||
}
|
||||
|
||||
IrInstSrc *target_inst = ir_gen_node_extra(irb, node->data.resume_expr.expr, scope, LValPtr, nullptr);
|
||||
if (target_inst == irb->codegen->invalid_inst_src)
|
||||
@ -9763,7 +9800,7 @@ static IrInstSrc *ir_gen_await_expr(IrBuilderSrc *irb, Scope *scope, AstNode *no
|
||||
{
|
||||
assert(node->type == NodeTypeAwaitExpr);
|
||||
|
||||
bool is_noasync = node->data.await_expr.noasync_token != nullptr;
|
||||
bool is_noasync = get_scope_noasync(scope) != nullptr;
|
||||
|
||||
AstNode *expr_node = node->data.await_expr.expr;
|
||||
if (expr_node->type == NodeTypeFnCallExpr && expr_node->data.fn_call_expr.modifier == CallModifierBuiltin) {
|
||||
@ -9809,6 +9846,11 @@ static IrInstSrc *ir_gen_suspend(IrBuilderSrc *irb, Scope *parent_scope, AstNode
|
||||
add_node_error(irb->codegen, node, buf_sprintf("suspend outside function definition"));
|
||||
return irb->codegen->invalid_inst_src;
|
||||
}
|
||||
if (get_scope_noasync(parent_scope) != nullptr) {
|
||||
add_node_error(irb->codegen, node, buf_sprintf("suspend in noasync scope"));
|
||||
return irb->codegen->invalid_inst_src;
|
||||
}
|
||||
|
||||
ScopeSuspend *existing_suspend_scope = get_scope_suspend(parent_scope);
|
||||
if (existing_suspend_scope) {
|
||||
if (!existing_suspend_scope->reported_err) {
|
||||
@ -9938,6 +9980,8 @@ static IrInstSrc *ir_gen_node_raw(IrBuilderSrc *irb, AstNode *node, Scope *scope
|
||||
return ir_gen_switch_expr(irb, scope, node, lval, result_loc);
|
||||
case NodeTypeCompTime:
|
||||
return ir_expr_wrap(irb, scope, ir_gen_comptime(irb, scope, node, lval), result_loc);
|
||||
case NodeTypeNoAsync:
|
||||
return ir_expr_wrap(irb, scope, ir_gen_noasync(irb, scope, node, lval), result_loc);
|
||||
case NodeTypeErrorType:
|
||||
return ir_lval_wrap(irb, scope, ir_gen_error_type(irb, scope, node), lval, result_loc);
|
||||
case NodeTypeBreak:
|
||||
|
||||
@ -876,6 +876,7 @@ static AstNode *ast_parse_container_field(ParseContext *pc) {
|
||||
// Statement
|
||||
// <- KEYWORD_comptime? VarDecl
|
||||
// / KEYWORD_comptime BlockExprStatement
|
||||
// / KEYWORD_noasync BlockExprStatement
|
||||
// / KEYWORD_suspend (SEMICOLON / BlockExprStatement)
|
||||
// / KEYWORD_defer BlockExprStatement
|
||||
// / KEYWORD_errdefer BlockExprStatement
|
||||
@ -899,6 +900,14 @@ static AstNode *ast_parse_statement(ParseContext *pc) {
|
||||
return res;
|
||||
}
|
||||
|
||||
Token *noasync = eat_token_if(pc, TokenIdKeywordNoAsync);
|
||||
if (noasync != nullptr) {
|
||||
AstNode *statement = ast_expect(pc, ast_parse_block_expr_statement);
|
||||
AstNode *res = ast_create_node(pc, NodeTypeNoAsync, noasync);
|
||||
res->data.noasync_expr.expr = statement;
|
||||
return res;
|
||||
}
|
||||
|
||||
Token *suspend = eat_token_if(pc, TokenIdKeywordSuspend);
|
||||
if (suspend != nullptr) {
|
||||
AstNode *statement = nullptr;
|
||||
@ -1237,6 +1246,7 @@ static AstNode *ast_parse_prefix_expr(ParseContext *pc) {
|
||||
// / IfExpr
|
||||
// / KEYWORD_break BreakLabel? Expr?
|
||||
// / KEYWORD_comptime Expr
|
||||
// / KEYWORD_noasync Expr
|
||||
// / KEYWORD_continue BreakLabel?
|
||||
// / KEYWORD_resume Expr
|
||||
// / KEYWORD_return Expr?
|
||||
@ -1271,6 +1281,14 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc) {
|
||||
return res;
|
||||
}
|
||||
|
||||
Token *noasync = eat_token_if(pc, TokenIdKeywordNoAsync);
|
||||
if (noasync != nullptr) {
|
||||
AstNode *expr = ast_expect(pc, ast_parse_expr);
|
||||
AstNode *res = ast_create_node(pc, NodeTypeNoAsync, noasync);
|
||||
res->data.noasync_expr.expr = expr;
|
||||
return res;
|
||||
}
|
||||
|
||||
Token *continue_token = eat_token_if(pc, TokenIdKeywordContinue);
|
||||
if (continue_token != nullptr) {
|
||||
Token *label = ast_parse_break_label(pc);
|
||||
@ -1459,13 +1477,11 @@ static AstNode *ast_parse_error_union_expr(ParseContext *pc) {
|
||||
|
||||
// SuffixExpr
|
||||
// <- KEYWORD_async PrimaryTypeExpr SuffixOp* FnCallArguments
|
||||
// / KEYWORD_noasync PrimaryTypeExpr SuffixOp* FnCallArguments
|
||||
// / PrimaryTypeExpr (SuffixOp / FnCallArguments)*
|
||||
static AstNode *ast_parse_suffix_expr(ParseContext *pc) {
|
||||
Token *async_token = eat_token(pc);
|
||||
bool is_async = async_token->id == TokenIdKeywordAsync;
|
||||
if (is_async || async_token->id == TokenIdKeywordNoAsync) {
|
||||
if (is_async && eat_token_if(pc, TokenIdKeywordFn) != nullptr) {
|
||||
Token *async_token = eat_token_if(pc, TokenIdKeywordAsync);
|
||||
if (async_token) {
|
||||
if (eat_token_if(pc, TokenIdKeywordFn) != nullptr) {
|
||||
// HACK: If we see the keyword `fn`, then we assume that
|
||||
// we are parsing an async fn proto, and not a call.
|
||||
// We therefore put back all tokens consumed by the async
|
||||
@ -1515,13 +1531,12 @@ static AstNode *ast_parse_suffix_expr(ParseContext *pc) {
|
||||
assert(args->type == NodeTypeFnCallExpr);
|
||||
|
||||
AstNode *res = ast_create_node(pc, NodeTypeFnCallExpr, async_token);
|
||||
res->data.fn_call_expr.modifier = is_async ? CallModifierAsync : CallModifierNoAsync;
|
||||
res->data.fn_call_expr.modifier = CallModifierAsync;
|
||||
res->data.fn_call_expr.seen = false;
|
||||
res->data.fn_call_expr.fn_ref_expr = child;
|
||||
res->data.fn_call_expr.params = args->data.fn_call_expr.params;
|
||||
return res;
|
||||
}
|
||||
put_back_token(pc);
|
||||
|
||||
AstNode *res = ast_parse_primary_type_expr(pc);
|
||||
if (res == nullptr)
|
||||
@ -1582,6 +1597,7 @@ static AstNode *ast_parse_suffix_expr(ParseContext *pc) {
|
||||
// / IfTypeExpr
|
||||
// / INTEGER
|
||||
// / KEYWORD_comptime TypeExpr
|
||||
// / KEYWORD_noasync TypeExpr
|
||||
// / KEYWORD_error DOT IDENTIFIER
|
||||
// / KEYWORD_false
|
||||
// / KEYWORD_null
|
||||
@ -1683,6 +1699,14 @@ static AstNode *ast_parse_primary_type_expr(ParseContext *pc) {
|
||||
return res;
|
||||
}
|
||||
|
||||
Token *noasync = eat_token_if(pc, TokenIdKeywordNoAsync);
|
||||
if (noasync != nullptr) {
|
||||
AstNode *expr = ast_expect(pc, ast_parse_type_expr);
|
||||
AstNode *res = ast_create_node(pc, NodeTypeNoAsync, noasync);
|
||||
res->data.noasync_expr.expr = expr;
|
||||
return res;
|
||||
}
|
||||
|
||||
Token *error = eat_token_if(pc, TokenIdKeywordError);
|
||||
if (error != nullptr) {
|
||||
Token *dot = expect_token(pc, TokenIdDot);
|
||||
@ -2599,14 +2623,10 @@ static AstNode *ast_parse_prefix_op(ParseContext *pc) {
|
||||
return res;
|
||||
}
|
||||
|
||||
Token *noasync_token = eat_token_if(pc, TokenIdKeywordNoAsync);
|
||||
Token *await = eat_token_if(pc, TokenIdKeywordAwait);
|
||||
if (await != nullptr) {
|
||||
AstNode *res = ast_create_node(pc, NodeTypeAwaitExpr, await);
|
||||
res->data.await_expr.noasync_token = noasync_token;
|
||||
return res;
|
||||
} else if (noasync_token != nullptr) {
|
||||
put_back_token(pc);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
@ -3125,6 +3145,9 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
|
||||
case NodeTypeCompTime:
|
||||
visit_field(&node->data.comptime_expr.expr, visit, context);
|
||||
break;
|
||||
case NodeTypeNoAsync:
|
||||
visit_field(&node->data.comptime_expr.expr, visit, context);
|
||||
break;
|
||||
case NodeTypeBreak:
|
||||
// none
|
||||
break;
|
||||
|
||||
@ -2,6 +2,21 @@ const tests = @import("tests.zig");
|
||||
const std = @import("std");
|
||||
|
||||
pub fn addCases(cases: *tests.CompileErrorContext) void {
|
||||
cases.addTest("combination of noasync and async",
|
||||
\\export fn entry() void {
|
||||
\\ noasync {
|
||||
\\ const bar = async foo();
|
||||
\\ suspend;
|
||||
\\ resume bar;
|
||||
\\ }
|
||||
\\}
|
||||
\\fn foo() void {}
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:3:21: error: async call in noasync scope",
|
||||
"tmp.zig:4:9: error: suspend in noasync scope",
|
||||
"tmp.zig:5:9: error: resume in noasync scope",
|
||||
});
|
||||
|
||||
cases.addTest("@TypeOf with no arguments",
|
||||
\\export fn entry() void {
|
||||
\\ _ = @TypeOf();
|
||||
|
||||
@ -1531,3 +1531,19 @@ test "noasync await" {
|
||||
S.doTheTest();
|
||||
expect(S.finished);
|
||||
}
|
||||
|
||||
test "noasync on function calls" {
|
||||
const S0 = struct {
|
||||
b: i32 = 42,
|
||||
};
|
||||
const S1 = struct {
|
||||
fn c() S0 {
|
||||
return S0{};
|
||||
}
|
||||
fn d() !S0 {
|
||||
return S0{};
|
||||
}
|
||||
};
|
||||
expectEqual(@as(i32, 42), noasync S1.c().b);
|
||||
expectEqual(@as(i32, 42), (try noasync S1.d()).b);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user