Sema: fix switch that covers full integer range

This commit is contained in:
Andrew Kelley 2021-10-25 15:52:21 -07:00
parent 8509e7111d
commit 97dc5f6eb5
3 changed files with 57 additions and 46 deletions

View File

@ -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(

View File

@ -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,
};
}

View File

@ -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();