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 number Diff line number Diff 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 Down Expand 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 Down Expand 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 Down Expand 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 Down Expand 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 Down Expand 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 number Diff line number Diff 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 Down Expand 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 Down Expand 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 Down Expand 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 Down Expand 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 Down Expand 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 Down Expand 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 Down Expand 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 Down Expand 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 Down Expand 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 number Diff line number Diff 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 Down Expand 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 Down Expand 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 Down Expand 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 Down Expand 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 Down Expand 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 Down Expand 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