Commit 97dc5f6eb5

Andrew Kelley <andrew@ziglang.org>
2021-10-26 00:52:21
Sema: fix switch that covers full integer range
1 parent 8509e71
Changed files (3)
src/Sema.zig
@@ -396,6 +396,14 @@ pub const Block = struct {
         return result_index;
     }
 
+    fn addUnreachable(block: *Block, src: LazySrcLoc, safety_check: bool) !void {
+        if (safety_check and block.wantSafety()) {
+            _ = try block.sema.safetyPanic(block, src, .unreach);
+        } else {
+            _ = try block.addNoOp(.unreach);
+        }
+    }
+
     pub fn startAnonDecl(block: *Block) !WipAnonDecl {
         return WipAnonDecl{
             .block = block,
@@ -6371,14 +6379,22 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
     }
 
     var final_else_body: []const Air.Inst.Index = &.{};
-    if (special.body.len != 0) {
+    if (special.body.len != 0 or !is_first) {
         var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, child_block.wip_capture_scope);
         defer wip_captures.deinit();
 
         case_block.instructions.shrinkRetainingCapacity(0);
         case_block.wip_capture_scope = wip_captures.scope;
 
-        _ = try sema.analyzeBody(&case_block, special.body);
+        if (special.body.len != 0) {
+            _ = try sema.analyzeBody(&case_block, special.body);
+        } else {
+            // We still need a terminator in this block, but we have proven
+            // that it is unreachable.
+            // TODO this should be a special safety panic other than unreachable, something
+            // like "panic: switch operand had corrupt value not allowed by the type"
+            try case_block.addUnreachable(src, true);
+        }
 
         try wip_captures.finalize();
 
@@ -8963,15 +8979,10 @@ fn zirUnreachable(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
 
     const inst_data = sema.code.instructions.items(.data)[inst].@"unreachable";
     const src = inst_data.src();
-    const safety_check = inst_data.safety;
     try sema.requireRuntimeBlock(block, src);
     // TODO Add compile error for @optimizeFor occurring too late in a scope.
-    if (safety_check and block.wantSafety()) {
-        return sema.safetyPanic(block, src, .unreach);
-    } else {
-        _ = try block.addNoOp(.unreach);
-        return always_noreturn;
-    }
+    try block.addUnreachable(src, inst_data.safety);
+    return always_noreturn;
 }
 
 fn zirRetErrValue(
test/behavior/switch.zig
@@ -262,3 +262,40 @@ fn testSwitchEnumPtrCapture() !void {
         else => unreachable,
     }
 }
+
+test "switch handles all cases of number" {
+    try testSwitchHandleAllCases();
+    comptime try testSwitchHandleAllCases();
+}
+
+fn testSwitchHandleAllCases() !void {
+    try expect(testSwitchHandleAllCasesExhaustive(0) == 3);
+    try expect(testSwitchHandleAllCasesExhaustive(1) == 2);
+    try expect(testSwitchHandleAllCasesExhaustive(2) == 1);
+    try expect(testSwitchHandleAllCasesExhaustive(3) == 0);
+
+    try expect(testSwitchHandleAllCasesRange(100) == 0);
+    try expect(testSwitchHandleAllCasesRange(200) == 1);
+    try expect(testSwitchHandleAllCasesRange(201) == 2);
+    try expect(testSwitchHandleAllCasesRange(202) == 4);
+    try expect(testSwitchHandleAllCasesRange(230) == 3);
+}
+
+fn testSwitchHandleAllCasesExhaustive(x: u2) u2 {
+    return switch (x) {
+        0 => @as(u2, 3),
+        1 => 2,
+        2 => 1,
+        3 => 0,
+    };
+}
+
+fn testSwitchHandleAllCasesRange(x: u8) u8 {
+    return switch (x) {
+        0...100 => @as(u8, 0),
+        101...200 => 1,
+        201, 203 => 2,
+        202 => 4,
+        204...255 => 3,
+    };
+}
test/behavior/switch_stage1.zig
@@ -3,43 +3,6 @@ const expect = std.testing.expect;
 const expectError = std.testing.expectError;
 const expectEqual = std.testing.expectEqual;
 
-test "switch handles all cases of number" {
-    try testSwitchHandleAllCases();
-    comptime try testSwitchHandleAllCases();
-}
-
-fn testSwitchHandleAllCases() !void {
-    try expect(testSwitchHandleAllCasesExhaustive(0) == 3);
-    try expect(testSwitchHandleAllCasesExhaustive(1) == 2);
-    try expect(testSwitchHandleAllCasesExhaustive(2) == 1);
-    try expect(testSwitchHandleAllCasesExhaustive(3) == 0);
-
-    try expect(testSwitchHandleAllCasesRange(100) == 0);
-    try expect(testSwitchHandleAllCasesRange(200) == 1);
-    try expect(testSwitchHandleAllCasesRange(201) == 2);
-    try expect(testSwitchHandleAllCasesRange(202) == 4);
-    try expect(testSwitchHandleAllCasesRange(230) == 3);
-}
-
-fn testSwitchHandleAllCasesExhaustive(x: u2) u2 {
-    return switch (x) {
-        0 => @as(u2, 3),
-        1 => 2,
-        2 => 1,
-        3 => 0,
-    };
-}
-
-fn testSwitchHandleAllCasesRange(x: u8) u8 {
-    return switch (x) {
-        0...100 => @as(u8, 0),
-        101...200 => 1,
-        201, 203 => 2,
-        202 => 4,
-        204...255 => 3,
-    };
-}
-
 test "switch all prongs unreachable" {
     try testAllProngsUnreachable();
     comptime try testAllProngsUnreachable();