Commit 03c363300f

mlugg <mlugg@mlugg.co.uk>
2024-09-11 10:01:05
AstGen: do not allow unlabeled `break` to exit a labeled switch
`break`ing from something which isn't a loop should always be opt-in. This was a bug in #21257.
1 parent 0001f91
Changed files (2)
lib
std
test
behavior
lib/std/zig/AstGen.zig
@@ -7811,9 +7811,7 @@ fn switchExpr(
     const switch_block = try parent_gz.makeBlockInst(switch_tag, node);
 
     if (switch_full.label_token) |label_token| {
-        block_scope.break_block = switch_block.toOptional();
         block_scope.continue_block = switch_block.toOptional();
-        // `break_result_info` already set above
         block_scope.continue_result_info = .{
             .rl = if (any_payload_is_ref)
                 .{ .ref_coerced_ty = raw_operand_ty_ref }
@@ -7825,6 +7823,8 @@ fn switchExpr(
             .token = label_token,
             .block_inst = switch_block,
         };
+        // `break` can target this via `label.block_inst`
+        // `break_result_info` already set by `setBreakResultInfo`
     }
 
     // We re-use this same scope for all cases, including the special prong, if any.
test/behavior/switch.zig
@@ -985,3 +985,20 @@ test "labeled switch with break" {
 
     comptime assert(comptime_val);
 }
+
+test "unlabeled break ignores switch" {
+    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
+
+    const result = while (true) {
+        _ = s: switch (@as(u32, 1)) {
+            1 => continue :s 123,
+            else => |x| break x,
+        };
+        comptime unreachable; // control flow never breaks from the switch
+    };
+    try expect(result == 123);
+}