commit bce7e7a52ba4dd9b46184925547b40ecedee63b6 (tree)
parent 39ca03e5156219c23b3b64d9fa682947ea3b37fa
Author: Justus Klausecker <justus@klausecker.de>
Date: Fri, 9 Jan 2026 08:04:26 +0100
AstGen: Re-allow labeled `break` from loop `else` block targeting its label
This fixes a regression from a couple of commits ago; breaking from the
`else` block of a loop to the loop's tag should be allowed when explicitly
targeting the label by name.
Diffstat:
5 files changed, 61 insertions(+), 12 deletions(-)
diff --git a/lib/std/zig/AstGen.zig b/lib/std/zig/AstGen.zig
@@ -2276,7 +2276,7 @@ fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index)
if (try astgen.tokenIdentEql(label.token, break_label)) {
switch (gen_zir.continue_target) {
.none => {
- return astgen.failNode(node, "continue cannot target labeled block", .{});
+ 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", .{});
@@ -6803,12 +6803,11 @@ fn whileExpr(
break :s &else_scope.base;
}
};
- // Remove label and forbid unlabeled control flow to this scope so that
- // `continue` and `break` control flow apply to outer loops; not this one.
- loop_scope.label = null;
+ // Disallow unlabeled control flow to this scope so that bare `continue`
+ // and `break` control flow apply to outer loops; not this one.
+ // Also disallow `continue` targeting the loop label.
loop_scope.allow_unlabeled_control_flow = false;
- loop_scope.continue_target = undefined;
- loop_scope.break_target = undefined;
+ loop_scope.continue_target = .none;
const else_result = try fullBodyExpr(&else_scope, sub_scope, loop_scope.break_result_info, else_node, .allow_branch_hint);
if (is_statement) {
_ = try addEnsureResult(&else_scope, else_result, else_node);
@@ -7093,12 +7092,11 @@ fn forExpr(
if (for_full.ast.else_expr.unwrap()) |else_node| {
const sub_scope = &else_scope.base;
- // Remove label and forbid unlabeled control flow to this scope so that
- // `continue` and `break` control flow apply to outer loops; not this one.
- loop_scope.label = null;
+ // Disallow unlabeled control flow to this scope so that bare `continue`
+ // and `break` control flow apply to outer loops; not this one.
+ // Also disallow `continue` targeting the loop label.
loop_scope.allow_unlabeled_control_flow = false;
- loop_scope.continue_target = undefined;
- loop_scope.break_target = undefined;
+ loop_scope.continue_target = .none;
const else_result = try fullBodyExpr(&else_scope, sub_scope, loop_scope.break_result_info, else_node, .allow_branch_hint);
if (is_statement) {
_ = try addEnsureResult(&else_scope, else_result, else_node);
diff --git a/test/behavior/for.zig b/test/behavior/for.zig
@@ -524,3 +524,20 @@ test "for loop 0 length range" {
comptime unreachable;
}
}
+
+test "labeled break from else prong" {
+ const S = struct {
+ fn doTheTest(x: u32) !void {
+ var y: u32 = 0;
+ const ok = label: while (y < x) : (y += 1) {
+ if (y == 10) break :label false;
+ } else {
+ break :label true;
+ };
+ try expect(ok);
+ }
+ };
+
+ try S.doTheTest(5);
+ try comptime S.doTheTest(5);
+}
diff --git a/test/behavior/while.zig b/test/behavior/while.zig
@@ -399,3 +399,20 @@ test "breaking from a loop in an if statement" {
} else 2;
_ = opt;
}
+
+test "labeled break from else prong" {
+ const S = struct {
+ fn doTheTest(x: u32) !void {
+ var y: u32 = 0;
+ const ok = label: while (y < x) : (y += 1) {
+ if (y == 10) break :label false;
+ } else {
+ break :label true;
+ };
+ try expect(ok);
+ }
+ };
+
+ try S.doTheTest(5);
+ try comptime S.doTheTest(5);
+}
diff --git a/test/cases/compile_errors/continue_loop_from_else_block.zig b/test/cases/compile_errors/continue_loop_from_else_block.zig
@@ -0,0 +1,17 @@
+export fn entry1() void {
+ var x: u32 = 0;
+ result: while (x < 5) : (x += 1) {} else {
+ continue :result;
+ }
+}
+
+export fn entry2() void {
+ result: for (0..5) |_| {} else {
+ continue :result;
+ }
+}
+
+// error
+//
+// :4:9: error: continue outside of loop or labeled switch expression
+// :10:9: error: continue outside of loop or labeled switch expression
diff --git a/test/cases/compile_errors/labeled_block_continue.zig b/test/cases/compile_errors/labeled_block_continue.zig
@@ -7,4 +7,4 @@ export fn foo() void {
// error
//
-// :3:9: error: continue cannot target labeled block
+// :3:9: error: continue outside of loop or labeled switch expression