Skip to content

Commit

Permalink
a first draft of ziglang#8220
Browse files Browse the repository at this point in the history
  • Loading branch information
EzequielRamis committed Mar 27, 2024
1 parent 9dac8db commit eadc6ae
Show file tree
Hide file tree
Showing 29 changed files with 900 additions and 202 deletions.
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

0 comments on commit eadc6ae

Please sign in to comment.