feature. See also
. The project being documented here (as the example) is the Zig library itself.
AstGen.breakExpr
fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
File
Code
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;
find_scope: switch (parent_scope.unwrap()) {
.gen_zir => |gen_zir| {
const scope = &gen_zir.base;
if (gen_zir.cur_defer_node.unwrap()) |cur_defer_node| {
return astgen.failNodeNotes(node, "cannot break out of defer expression", .{}, &.{
try astgen.errNoteNode(
cur_defer_node,
"defer expression here",
.{},
),
});
}
if (opt_break_label.unwrap()) |break_label| labeled: {
if (gen_zir.label) |*label| {
if (try astgen.tokenIdentEql(label.token, break_label)) {
label.used = true;
break :labeled;
}
}
continue :find_scope gen_zir.parent.unwrap();
} else if (!gen_zir.allow_unlabeled_control_flow) {
// unlabeled control flow. Continue to parent scopes.
continue :find_scope gen_zir.parent.unwrap();
}
const break_tag: Zir.Inst.Tag = if (gen_zir.is_inline)
.break_inline
else
.@"break";
if (opt_rhs.unwrap()) |rhs| {
const operand = try reachableExpr(parent_gz, parent_scope, gen_zir.break_result_info, rhs, node);
try genDefers(parent_gz, scope, parent_scope, .normal_only);
if (!gen_zir.is_comptime) {
try restoreErrRetIndex(parent_gz, .{ .block = gen_zir.break_target }, gen_zir.break_result_info, rhs, operand);
}
switch (gen_zir.break_result_info.rl) {
.ptr => {
// we assume the result location is written, and we break with void.
_ = try parent_gz.addBreak(break_tag, gen_zir.break_target, .void_value);
},
.discard => {
_ = try parent_gz.addBreak(break_tag, gen_zir.break_target, .void_value);
},
else => {
_ = try parent_gz.addBreakWithSrcNode(break_tag, gen_zir.break_target, operand, rhs);
},
}
return .unreachable_value;
} else {
_ = try rvalue(parent_gz, gen_zir.break_result_info, .void_value, node);
try genDefers(parent_gz, scope, parent_scope, .normal_only);
if (!gen_zir.is_comptime)
_ = try parent_gz.addRestoreErrRetIndex(.{ .block = gen_zir.break_target }, .always, node);
_ = try parent_gz.addBreak(break_tag, gen_zir.break_target, .void_value);
return .unreachable_value;
}
},
.local_val => |local_val| continue :find_scope local_val.parent.unwrap(),
.local_ptr => |local_ptr| continue :find_scope local_ptr.parent.unwrap(),
.defer_normal, .defer_error => |defer_scope| continue :find_scope defer_scope.parent.unwrap(),
.namespace => {
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", .{});
}
},
.top => unreachable,
}
}