Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 45 additions & 9 deletions lib/std/zig/Ast.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -1197,14 +1197,7 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex {
n = extra.sentinel;
},

.@"continue" => {
if (datas[n].lhs != 0) {
return datas[n].lhs + end_offset;
} else {
return main_tokens[n] + end_offset;
}
},
.@"break" => {
.@"continue", .@"break" => {
if (datas[n].rhs != 0) {
n = datas[n].rhs;
} else if (datas[n].lhs != 0) {
Expand DownExpand Up@@ -1908,6 +1901,15 @@ pub fn taggedUnionEnumTag(tree: Ast, node: Node.Index) full.ContainerDecl {
});
}

pub fn switchFull(tree: Ast, node: Node.Index) full.Switch {
const data = &tree.nodes.items(.data)[node];
return tree.fullSwitchComponents(.{
.switch_token = tree.nodes.items(.main_token)[node],
.condition = data.lhs,
.sub_range = data.rhs,
});
}

pub fn switchCaseOne(tree: Ast, node: Node.Index) full.SwitchCase {
const data = &tree.nodes.items(.data)[node];
const values: *[1]Node.Index = &data.lhs;
Expand DownExpand Up@@ -2217,6 +2219,21 @@ fn fullContainerDeclComponents(tree: Ast, info: full.ContainerDecl.Components) f
return result;
}

fn fullSwitchComponents(tree: Ast, info: full.Switch.Components) full.Switch {
const token_tags = tree.tokens.items(.tag);
const tok_i = info.switch_token -| 1;
var result: full.Switch = .{
.ast = info,
.label_token = null,
};
if (token_tags[tok_i] == .colon and
token_tags[tok_i -| 1] == .identifier)
{
result.label_token = tok_i - 1;
}
return result;
}

fn fullSwitchCaseComponents(tree: Ast, info: full.SwitchCase.Components, node: Node.Index) full.SwitchCase {
const token_tags = tree.tokens.items(.tag);
const node_tags = tree.nodes.items(.tag);
Expand DownExpand Up@@ -2488,6 +2505,13 @@ pub fn fullContainerDecl(tree: Ast, buffer: *[2]Ast.Node.Index, node: Node.Index
};
}

pub fn fullSwitch(tree: Ast, node: Node.Index) ?full.Switch {
return switch (tree.nodes.items(.tag)[node]) {
.@"switch", .switch_comma => tree.switchFull(node),
else => null,
};
}

pub fn fullSwitchCase(tree: Ast, node: Node.Index) ?full.SwitchCase {
return switch (tree.nodes.items(.tag)[node]) {
.switch_case_one, .switch_case_inline_one => tree.switchCaseOne(node),
Expand DownExpand Up@@ -2840,6 +2864,17 @@ pub const full = struct {
};
};

pub const Switch = struct {
ast: Components,
label_token: ?TokenIndex,

pub const Components = struct {
switch_token: TokenIndex,
condition: Node.Index,
sub_range: Node.Index,
};
};

pub const SwitchCase = struct {
inline_token: ?TokenIndex,
/// Points to the first token after the `|`. Will either be an identifier or
Expand DownExpand Up@@ -3294,7 +3329,8 @@ pub const Node = struct {
@"suspend",
/// `resume lhs`. rhs is unused.
@"resume",
/// `continue`. lhs is token index of label if any. rhs is unused.
/// `continue :lhs rhs`
/// both lhs and rhs may be omitted.
@"continue",
/// `break :lhs rhs`
/// both lhs and rhs may be omitted.
Expand Down
59 changes: 49 additions & 10 deletions lib/std/zig/AstGen.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -1138,7 +1138,7 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
.error_set_decl => return errorSetDecl(gz, ri, node),
.array_access => return arrayAccess(gz, scope, ri, node),
.@"comptime" => return comptimeExprAst(gz, scope, ri, node),
.@"switch", .switch_comma => return switchExpr(gz, scope, ri.br(), node),
.@"switch", .switch_comma => return switchExpr(gz, scope, ri.br(), node, tree.fullSwitch(node).?),

.@"nosuspend" => return nosuspendExpr(gz, scope, ri, node),
.@"suspend" => return suspendExpr(gz, scope, node),
Expand DownExpand Up@@ -2226,6 +2226,7 @@ fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index)
const tree = astgen.tree;
const node_datas = tree.nodes.items(.data);
const break_label = node_datas[node].lhs;
const rhs = node_datas[node].rhs;

// Look for the label in the scope.
var scope = parent_scope;
Expand All@@ -2250,6 +2251,17 @@ fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index)
if (break_label != 0) blk: {
if (gen_zir.label) |*label| {
if (try astgen.tokenIdentEql(label.token, break_label)) {
const maybe_switch_tag = astgen.instructions.items(.tag)[@intFromEnum(label.block_inst)];
if (rhs != 0) switch (maybe_switch_tag) {
.switch_block, .switch_block_ref => {
gen_zir.any_dispatch = true;
},
else => return astgen.failNode(node, "cannot continue with operand", .{}),
} else switch (maybe_switch_tag) {
.switch_block, .switch_block_ref => return astgen.failNode(node, "cannot continue switch without operand", .{}),
else => {},
}

label.used = true;
break :blk;
}
Expand All@@ -2259,6 +2271,17 @@ fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index)
continue;
}

if (rhs != 0) {
const operand = try reachableExpr(parent_gz, parent_scope, gen_zir.break_result_info, rhs, node);

// As our last action before the continue, "pop" the error trace if needed
if (!gen_zir.is_comptime)
_ = try parent_gz.addRestoreErrRetIndex(.{ .block = continue_block }, .always, node);

_ = try parent_gz.addBreakWithSrcNode(.switch_continue, continue_block, operand, rhs);
return Zir.Inst.Ref.unreachable_value;
}

const break_tag: Zir.Inst.Tag = if (gen_zir.is_inline)
.break_inline
else
Expand DownExpand Up@@ -2842,6 +2865,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
.panic,
.trap,
.check_comptime_control_flow,
.switch_continue,
=> {
noreturn_src_node = statement;
break :b true;
Expand DownExpand Up@@ -7568,7 +7592,8 @@ fn switchExpr(
parent_gz: *GenZir,
scope: *Scope,
ri: ResultInfo,
switch_node: Ast.Node.Index,
node: Ast.Node.Index,
switch_full: Ast.full.Switch,
) InnerError!Zir.Inst.Ref {
const astgen = parent_gz.astgen;
const gpa = astgen.gpa;
Expand All@@ -7577,14 +7602,14 @@ fn switchExpr(
const node_tags = tree.nodes.items(.tag);
const main_tokens = tree.nodes.items(.main_token);
const token_tags = tree.tokens.items(.tag);
const operand_node = node_datas[switch_node].lhs;
const extra = tree.extraData(node_datas[switch_node].rhs, Ast.Node.SubRange);
const operand_node = node_datas[node].lhs;
const extra = tree.extraData(node_datas[node].rhs, Ast.Node.SubRange);
const case_nodes = tree.extra_data[extra.start..extra.end];

const need_rl = astgen.nodes_need_rl.contains(switch_node);
const need_rl = astgen.nodes_need_rl.contains(node);
const block_ri: ResultInfo = if (need_rl) ri else .{
.rl = switch (ri.rl) {
.ptr => .{ .ty = (try ri.rl.resultType(parent_gz, switch_node)).? },
.ptr => .{ .ty = (try ri.rl.resultType(parent_gz, node)).? },
.inferred_ptr => .none,
else => ri.rl,
},
Expand All@@ -7595,6 +7620,10 @@ fn switchExpr(
const LocTag = @typeInfo(ResultInfo.Loc).Union.tag_type.?;
const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl);

if (switch_full.label_token) |label_token| {
try astgen.checkLabelRedefinition(scope, label_token);
}

// We perform two passes over the AST. This first pass is to collect information
// for the following variables, make note of the special prong AST node index,
// and bail out with a compile error if there are multiple special prongs present.
Expand DownExpand Up@@ -7636,7 +7665,7 @@ fn switchExpr(
);
} else if (underscore_src) |some_underscore| {
return astgen.failNodeNotes(
switch_node,
node,
"else and '_' prong in switch expression",
.{},
&[_]u32{
Expand DownExpand Up@@ -7677,7 +7706,7 @@ fn switchExpr(
);
} else if (else_src) |some_else| {
return astgen.failNodeNotes(
switch_node,
node,
"else and '_' prong in switch expression",
.{},
&[_]u32{
Expand DownExpand Up@@ -7747,7 +7776,15 @@ fn switchExpr(
try emitDbgStmtForceCurrentIndex(parent_gz, operand_lc);
// This gets added to the parent block later, after the item expressions.
const switch_tag: Zir.Inst.Tag = if (any_payload_is_ref) .switch_block_ref else .switch_block;
const switch_block = try parent_gz.makeBlockInst(switch_tag, switch_node);
const switch_block = try parent_gz.makeBlockInst(switch_tag, node);

block_scope.continue_block = switch_block.toOptional();
if (switch_full.label_token) |label_token| {
block_scope.label = .{
.token = label_token,
.block_inst = switch_block,
};
}

// We re-use this same scope for all cases, including the special prong, if any.
var case_scope = parent_gz.makeSubBlock(&block_scope.base);
Expand DownExpand Up@@ -7969,6 +8006,7 @@ fn switchExpr(
.has_under = special_prong == .under,
.any_has_tag_capture = any_has_tag_capture,
.scalar_cases_len = @intCast(scalar_cases_len),
.any_dispatch = block_scope.any_dispatch,
},
});

Expand DownExpand Up@@ -8004,7 +8042,7 @@ fn switchExpr(
}

if (need_result_rvalue) {
return rvalue(parent_gz, ri, switch_block.toRef(), switch_node);
return rvalue(parent_gz, ri, switch_block.toRef(), node);
} else {
return switch_block.toRef();
}
Expand DownExpand Up@@ -11872,6 +11910,7 @@ const GenZir = struct {
cur_defer_node: Ast.Node.Index = 0,
// Set if this GenZir is a defer or it is inside a defer.
any_defer_node: Ast.Node.Index = 0,
any_dispatch: bool = false,

const unstacked_top = std.math.maxInt(usize);
/// Call unstack before adding any new instructions to containing GenZir.
Expand Down
26 changes: 20 additions & 6 deletions lib/std/zig/Parse.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -924,7 +924,6 @@ fn expectContainerField(p: *Parse) !Node.Index {
/// / KEYWORD_errdefer Payload? BlockExprStatement
/// / IfStatement
/// / LabeledStatement
/// / SwitchExpr
/// / VarDeclExprStatement
fn expectStatement(p: *Parse, allow_defer_var: bool) Error!Node.Index {
if (p.eatToken(.keyword_comptime)) |comptime_token| {
Expand DownExpand Up@@ -995,7 +994,6 @@ fn expectStatement(p: *Parse, allow_defer_var: bool) Error!Node.Index {
.rhs = try p.expectBlockExprStatement(),
},
}),
.keyword_switch => return p.expectSwitchExpr(),
.keyword_if => return p.expectIfStatement(),
.keyword_enum, .keyword_struct, .keyword_union => {
const identifier = p.tok_i + 1;
Expand DownExpand Up@@ -1238,7 +1236,7 @@ fn expectIfStatement(p: *Parse) !Node.Index {
});
}

/// LabeledStatement <- BlockLabel? (Block / LoopStatement)
/// LabeledStatement <- BlockLabel? (Block / LoopStatement / SwitchExpr)
fn parseLabeledStatement(p: *Parse) !Node.Index {
const label_token = p.parseBlockLabel();
const block = try p.parseBlock();
Expand All@@ -1247,6 +1245,9 @@ fn parseLabeledStatement(p: *Parse) !Node.Index {
const loop_stmt = try p.parseLoopStatement();
if (loop_stmt != 0) return loop_stmt;

const switch_expr = try p.parseSwitchExpr();
if (switch_expr != 0) return switch_expr;

if (label_token != 0) {
const after_colon = p.tok_i;
const node = try p.parseTypeExpr();
Expand DownExpand Up@@ -2072,7 +2073,7 @@ fn expectTypeExpr(p: *Parse) Error!Node.Index {
/// / KEYWORD_break BreakLabel? Expr?
/// / KEYWORD_comptime Expr
/// / KEYWORD_nosuspend Expr
/// / KEYWORD_continue BreakLabel?
/// / KEYWORD_continue BreakLabel? Expr?
/// / KEYWORD_resume Expr
/// / KEYWORD_return Expr?
/// / BlockLabel? LoopExpr
Expand All@@ -2098,7 +2099,7 @@ fn parsePrimaryExpr(p: *Parse) !Node.Index {
.main_token = p.nextToken(),
.data = .{
.lhs = try p.parseBreakLabel(),
.rhs = undefined,
.rhs = try p.parseExpr(),
},
});
},
Expand DownExpand Up@@ -2627,7 +2628,6 @@ fn parseSuffixExpr(p: *Parse) !Node.Index {
/// / KEYWORD_anyframe
/// / KEYWORD_unreachable
/// / STRINGLITERAL
/// / SwitchExpr
///
/// ContainerDecl <- (KEYWORD_extern / KEYWORD_packed)? ContainerDeclAuto
///
Expand All@@ -2647,6 +2647,7 @@ fn parseSuffixExpr(p: *Parse) !Node.Index {
/// LabeledTypeExpr
/// <- BlockLabel Block
/// / BlockLabel? LoopTypeExpr
/// / BlockLabel? SwitchExpr
///
/// LoopTypeExpr <- KEYWORD_inline? (ForTypeExpr / WhileTypeExpr)
fn parsePrimaryTypeExpr(p: *Parse) !Node.Index {
Expand DownExpand Up@@ -2753,6 +2754,10 @@ fn parsePrimaryTypeExpr(p: *Parse) !Node.Index {
p.tok_i += 2;
return p.parseWhileTypeExpr();
},
.keyword_switch => {
p.tok_i += 2;
return p.expectSwitchExpr();
},
.l_brace => {
p.tok_i += 2;
return p.parseBlock();
Expand DownExpand Up@@ -3029,8 +3034,17 @@ fn parseWhileTypeExpr(p: *Parse) !Node.Index {
}

/// SwitchExpr <- KEYWORD_switch LPAREN Expr RPAREN LBRACE SwitchProngList RBRACE
fn parseSwitchExpr(p: *Parse) !Node.Index {
const switch_token = p.eatToken(.keyword_switch) orelse return null_node;
return p.expectSwitchSuffix(switch_token);
}

fn expectSwitchExpr(p: *Parse) !Node.Index {
const switch_token = p.assertToken(.keyword_switch);
return p.expectSwitchSuffix(switch_token);
}

fn expectSwitchSuffix(p: *Parse, switch_token: TokenIndex) !Node.Index {
_ = try p.expectToken(.l_paren);
const expr_node = try p.expectExpr();
_ = try p.expectToken(.r_paren);
Expand Down
Loading