feature. See also
. The project being documented here (as the example) is the Zig library itself.
AstGen.continueExpr
fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
File
Code
fn continueExpr(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;
if (opt_break_label == .none and opt_rhs != .none) {
return astgen.failNode(node, "cannot continue with operand without label", .{});
}
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 continue 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)) {
switch (gen_zir.continue_target) {
.none => {
return astgen.failNode(node, "continue outside of loop or labeled switch expression", .{});
},
.@"break" => if (opt_rhs != .none) {
return astgen.failNode(node, "cannot continue loop with operand", .{});
},
.switch_continue => if (opt_rhs == .none) {
return astgen.failNode(node, "cannot continue switch without operand", .{});
},
}
label.used = true;
label.used_for_continue = true;
break :labeled;
}
}
continue :find_scope gen_zir.parent.unwrap();
} else if (gen_zir.allow_unlabeled_control_flow) {
// provide a `continue` target or corresponds to a labeled
// `switch`, ignore it and continue to parent scopes.
switch (gen_zir.continue_target) {
.none, .switch_continue => {
continue :find_scope gen_zir.parent.unwrap();
},
.@"break" => {},
}
} else {
// unlabeled control flow, so we continue to its parent scopes.
continue :find_scope gen_zir.parent.unwrap();
}
switch (gen_zir.continue_target) {
.none => unreachable,
.@"break" => |block| {
try genDefers(parent_gz, scope, parent_scope, .normal_only);
const break_tag: Zir.Inst.Tag = if (gen_zir.is_inline)
.break_inline
else
.@"break";
if (break_tag == .break_inline) {
_ = try parent_gz.addUnNode(.check_comptime_control_flow, block.toRef(), node);
}
if (!gen_zir.is_comptime) {
_ = try parent_gz.addRestoreErrRetIndex(.{ .block = block }, .always, node);
}
_ = try parent_gz.addBreak(break_tag, block, .void_value);
return .unreachable_value;
},
.switch_continue => |switch_block| {
const rhs = opt_rhs.unwrap().?;
const operand = try reachableExpr(parent_gz, parent_scope, gen_zir.continue_result_info, rhs, node);
try genDefers(parent_gz, scope, parent_scope, .normal_only);
if (!gen_zir.is_comptime) {
_ = try parent_gz.addRestoreErrRetIndex(.{ .block = switch_block }, .always, node);
}
_ = try parent_gz.addBreakWithSrcNode(.switch_continue, switch_block, operand, rhs);
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, "continue expression outside loop", .{});
}
},
.top => unreachable,
}
}