breakExpr
Function parameters
Parameters
- parent_gz:*GenZir
- parent_scope:*Scope
- node:Ast.Node.Index
Functions in this namespace
Functions
Source
Implementation
fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
const astgen = parent_gz.astgen;
const tree = astgen.tree;
const opt_break_label, const opt_rhs = tree.nodeData(node).opt_token_and_opt_node;
// Look for the label in the scope.
var scope = parent_scope;
while (true) {
switch (scope.tag) {
.gen_zir => {
const block_gz = scope.cast(GenZir).?;
if (block_gz.cur_defer_node.unwrap()) |cur_defer_node| {
// We are breaking out of a `defer` block.
return astgen.failNodeNotes(node, "cannot break out of defer expression", .{}, &.{
try astgen.errNoteNode(
cur_defer_node,
"defer expression here",
.{},
),
});
}
const block_inst = blk: {
if (opt_break_label.unwrap()) |break_label| {
if (block_gz.label) |*label| {
if (try astgen.tokenIdentEql(label.token, break_label)) {
label.used = true;
break :blk label.block_inst;
}
}
} else if (block_gz.break_block.unwrap()) |i| {
break :blk i;
}
// If not the target, start over with the parent
scope = block_gz.parent;
continue;
};
// If we made it here, this block is the target of the break expr
const break_tag: Zir.Inst.Tag = if (block_gz.is_inline)
.break_inline
else
.@"break";
const rhs = opt_rhs.unwrap() orelse {
_ = try rvalue(parent_gz, block_gz.break_result_info, .void_value, node);
try genDefers(parent_gz, scope, parent_scope, .normal_only);
// As our last action before the break, "pop" the error trace if needed
if (!block_gz.is_comptime)
_ = try parent_gz.addRestoreErrRetIndex(.{ .block = block_inst }, .always, node);
_ = try parent_gz.addBreak(break_tag, block_inst, .void_value);
return Zir.Inst.Ref.unreachable_value;
};
const operand = try reachableExpr(parent_gz, parent_scope, block_gz.break_result_info, rhs, node);
try genDefers(parent_gz, scope, parent_scope, .normal_only);
// As our last action before the break, "pop" the error trace if needed
if (!block_gz.is_comptime)
try restoreErrRetIndex(parent_gz, .{ .block = block_inst }, block_gz.break_result_info, rhs, operand);
switch (block_gz.break_result_info.rl) {
.ptr => {
// In this case we don't have any mechanism to intercept it;
// we assume the result location is written, and we break with void.
_ = try parent_gz.addBreak(break_tag, block_inst, .void_value);
},
.discard => {
_ = try parent_gz.addBreak(break_tag, block_inst, .void_value);
},
else => {
_ = try parent_gz.addBreakWithSrcNode(break_tag, block_inst, operand, rhs);
},
}
return Zir.Inst.Ref.unreachable_value;
},
.local_val => scope = scope.cast(Scope.LocalVal).?.parent,
.local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
.namespace => break,
.defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
.top => unreachable,
}
}
if (opt_break_label.unwrap()) |break_label| {
const label_name = try astgen.identifierTokenString(break_label);
return astgen.failTok(break_label, "label not found: '{s}'", .{label_name});
} else {
return astgen.failNode(node, "break expression outside loop", .{});
}
}