authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-25 15:52:21-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-25 15:52:21-07:00
log97dc5f6eb531c91e8bd23a5589cae64e0a4561e8
tree297dba8e3be1668c81e75f9e7afc30e999c71e9d
parent8509e7111d80a07e778aa2a57d58d2bea6945014

Sema: fix switch that covers full integer range


3 files changed, 57 insertions(+), 46 deletions(-)

src/Sema.zig+20-9
...@@ -396,6 +396,14 @@ pub const Block = struct {...@@ -396,6 +396,14 @@ pub const Block = struct {
396 return result_index;396 return result_index;
397 }397 }
398398
399 fn addUnreachable(block: *Block, src: LazySrcLoc, safety_check: bool) !void {
400 if (safety_check and block.wantSafety()) {
401 _ = try block.sema.safetyPanic(block, src, .unreach);
402 } else {
403 _ = try block.addNoOp(.unreach);
404 }
405 }
406
399 pub fn startAnonDecl(block: *Block) !WipAnonDecl {407 pub fn startAnonDecl(block: *Block) !WipAnonDecl {
400 return WipAnonDecl{408 return WipAnonDecl{
401 .block = block,409 .block = block,
...@@ -6371,14 +6379,22 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -6371,14 +6379,22 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
6371 }6379 }
63726380
6373 var final_else_body: []const Air.Inst.Index = &.{};6381 var final_else_body: []const Air.Inst.Index = &.{};
6374 if (special.body.len != 0) {6382 if (special.body.len != 0 or !is_first) {
6375 var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, child_block.wip_capture_scope);6383 var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, child_block.wip_capture_scope);
6376 defer wip_captures.deinit();6384 defer wip_captures.deinit();
63776385
6378 case_block.instructions.shrinkRetainingCapacity(0);6386 case_block.instructions.shrinkRetainingCapacity(0);
6379 case_block.wip_capture_scope = wip_captures.scope;6387 case_block.wip_capture_scope = wip_captures.scope;
63806388
6381 _ = try sema.analyzeBody(&case_block, special.body);6389 if (special.body.len != 0) {
6390 _ = try sema.analyzeBody(&case_block, special.body);
6391 } else {
6392 // We still need a terminator in this block, but we have proven
6393 // that it is unreachable.
6394 // TODO this should be a special safety panic other than unreachable, something
6395 // like "panic: switch operand had corrupt value not allowed by the type"
6396 try case_block.addUnreachable(src, true);
6397 }
63826398
6383 try wip_captures.finalize();6399 try wip_captures.finalize();
63846400
...@@ -8963,15 +8979,10 @@ fn zirUnreachable(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -8963,15 +8979,10 @@ fn zirUnreachable(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
89638979
8964 const inst_data = sema.code.instructions.items(.data)[inst].@"unreachable";8980 const inst_data = sema.code.instructions.items(.data)[inst].@"unreachable";
8965 const src = inst_data.src();8981 const src = inst_data.src();
8966 const safety_check = inst_data.safety;
8967 try sema.requireRuntimeBlock(block, src);8982 try sema.requireRuntimeBlock(block, src);
8968 // TODO Add compile error for @optimizeFor occurring too late in a scope.8983 // TODO Add compile error for @optimizeFor occurring too late in a scope.
8969 if (safety_check and block.wantSafety()) {8984 try block.addUnreachable(src, inst_data.safety);
8970 return sema.safetyPanic(block, src, .unreach);8985 return always_noreturn;
8971 } else {
8972 _ = try block.addNoOp(.unreach);
8973 return always_noreturn;
8974 }
8975}8986}
89768987
8977fn zirRetErrValue(8988fn zirRetErrValue(
test/behavior/switch.zig+37
...@@ -262,3 +262,40 @@ fn testSwitchEnumPtrCapture() !void {...@@ -262,3 +262,40 @@ fn testSwitchEnumPtrCapture() !void {
262 else => unreachable,262 else => unreachable,
263 }263 }
264}264}
265
266test "switch handles all cases of number" {
267 try testSwitchHandleAllCases();
268 comptime try testSwitchHandleAllCases();
269}
270
271fn testSwitchHandleAllCases() !void {
272 try expect(testSwitchHandleAllCasesExhaustive(0) == 3);
273 try expect(testSwitchHandleAllCasesExhaustive(1) == 2);
274 try expect(testSwitchHandleAllCasesExhaustive(2) == 1);
275 try expect(testSwitchHandleAllCasesExhaustive(3) == 0);
276
277 try expect(testSwitchHandleAllCasesRange(100) == 0);
278 try expect(testSwitchHandleAllCasesRange(200) == 1);
279 try expect(testSwitchHandleAllCasesRange(201) == 2);
280 try expect(testSwitchHandleAllCasesRange(202) == 4);
281 try expect(testSwitchHandleAllCasesRange(230) == 3);
282}
283
284fn testSwitchHandleAllCasesExhaustive(x: u2) u2 {
285 return switch (x) {
286 0 => @as(u2, 3),
287 1 => 2,
288 2 => 1,
289 3 => 0,
290 };
291}
292
293fn testSwitchHandleAllCasesRange(x: u8) u8 {
294 return switch (x) {
295 0...100 => @as(u8, 0),
296 101...200 => 1,
297 201, 203 => 2,
298 202 => 4,
299 204...255 => 3,
300 };
301}
test/behavior/switch_stage1.zig-37
...@@ -3,43 +3,6 @@ const expect = std.testing.expect;...@@ -3,43 +3,6 @@ const expect = std.testing.expect;
3const expectError = std.testing.expectError;3const expectError = std.testing.expectError;
4const expectEqual = std.testing.expectEqual;4const expectEqual = std.testing.expectEqual;
55
6test "switch handles all cases of number" {
7 try testSwitchHandleAllCases();
8 comptime try testSwitchHandleAllCases();
9}
10
11fn testSwitchHandleAllCases() !void {
12 try expect(testSwitchHandleAllCasesExhaustive(0) == 3);
13 try expect(testSwitchHandleAllCasesExhaustive(1) == 2);
14 try expect(testSwitchHandleAllCasesExhaustive(2) == 1);
15 try expect(testSwitchHandleAllCasesExhaustive(3) == 0);
16
17 try expect(testSwitchHandleAllCasesRange(100) == 0);
18 try expect(testSwitchHandleAllCasesRange(200) == 1);
19 try expect(testSwitchHandleAllCasesRange(201) == 2);
20 try expect(testSwitchHandleAllCasesRange(202) == 4);
21 try expect(testSwitchHandleAllCasesRange(230) == 3);
22}
23
24fn testSwitchHandleAllCasesExhaustive(x: u2) u2 {
25 return switch (x) {
26 0 => @as(u2, 3),
27 1 => 2,
28 2 => 1,
29 3 => 0,
30 };
31}
32
33fn testSwitchHandleAllCasesRange(x: u8) u8 {
34 return switch (x) {
35 0...100 => @as(u8, 0),
36 101...200 => 1,
37 201, 203 => 2,
38 202 => 4,
39 204...255 => 3,
40 };
41}
42
43test "switch all prongs unreachable" {6test "switch all prongs unreachable" {
44 try testAllProngsUnreachable();7 try testAllProngsUnreachable();
45 comptime try testAllProngsUnreachable();8 comptime try testAllProngsUnreachable();