authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-10-06 17:25:00+03:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-10-07 11:04:02+03:00
log2315e1b41073a8d6a7f7fe3deb98ec98e45d72f6
tree1dc47e22eb261a98825051e31f691a39c1e87f97
parent29ae6515f3adc23df3d889acaf7ec62a2d01b707

safety: add safety check for hitting else branch on a corrupt enum value

Closes #7053

4 files changed, 91 insertions(+), 0 deletions(-)

src/Sema.zig+15
......@@ -9998,6 +9998,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
99989998 return sema.resolveBlockBody(block, src, &child_block, special.body, inst, merges);
99999999 }
1000010000
10001 const backend_supports_is_named_enum = sema.mod.comp.bin_file.options.use_llvm;
10002
1000110003 if (scalar_cases_len + multi_cases_len == 0 and !special.is_inline) {
1000210004 if (empty_enum) {
1000310005 return Air.Inst.Ref.void_value;
......@@ -10008,6 +10010,12 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
1000810010 if (err_set and try sema.maybeErrorUnwrap(block, special.body, operand)) {
1000910011 return Air.Inst.Ref.unreachable_value;
1001010012 }
10013 if (backend_supports_is_named_enum and block.wantSafety() and operand_ty.zigTypeTag() == .Enum and
10014 (!operand_ty.isNonexhaustiveEnum() or union_originally))
10015 {
10016 const ok = try block.addUnOp(.is_named_enum_value, operand);
10017 try sema.addSafetyCheck(block, ok, .corrupt_switch);
10018 }
1001110019 return sema.resolveBlockBody(block, src, &child_block, special.body, inst, merges);
1001210020 }
1001310021
......@@ -10465,6 +10473,13 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
1046510473 case_block.wip_capture_scope = wip_captures.scope;
1046610474 case_block.inline_case_capture = .none;
1046710475
10476 if (backend_supports_is_named_enum and special.body.len != 0 and block.wantSafety() and
10477 operand_ty.zigTypeTag() == .Enum and (!operand_ty.isNonexhaustiveEnum() or union_originally))
10478 {
10479 const ok = try case_block.addUnOp(.is_named_enum_value, operand);
10480 try sema.addSafetyCheck(&case_block, ok, .corrupt_switch);
10481 }
10482
1046810483 const analyze_body = if (union_originally and !special.is_inline)
1046910484 for (seen_enum_fields) |seen_field, index| {
1047010485 if (seen_field != null) continue;
test/cases/safety/switch else on corrupt enum value - one prong.zig created+24
......@@ -0,0 +1,24 @@
1const std = @import("std");
2
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
4 _ = stack_trace;
5 if (std.mem.eql(u8, message, "switch on corrupt value")) {
6 std.process.exit(0);
7 }
8 std.process.exit(1);
9}
10const E = enum(u32) {
11 one = 1,
12 two = 2,
13};
14pub fn main() !void {
15 var a: E = undefined;
16 @ptrCast(*u32, &a).* = 255;
17 switch (a) {
18 .one => @panic("one"),
19 else => @panic("else"),
20 }
21}
22// run
23// backend=llvm
24// target=native
test/cases/safety/switch else on corrupt enum value - union.zig created+29
......@@ -0,0 +1,29 @@
1const std = @import("std");
2
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
4 _ = stack_trace;
5 if (std.mem.eql(u8, message, "switch on corrupt value")) {
6 std.process.exit(0);
7 }
8 std.process.exit(1);
9}
10const E = enum(u16) {
11 one = 1,
12 two = 2,
13 _,
14};
15const U = union(E) {
16 one: u16,
17 two: u16,
18};
19pub fn main() !void {
20 var a: U = undefined;
21 @ptrCast(*align(@alignOf(U)) u32, &a).* = 0xFFFF_FFFF;
22 switch (a) {
23 .one => @panic("one"),
24 else => @panic("else"),
25 }
26}
27// run
28// backend=llvm
29// target=native
test/cases/safety/switch else on corrupt enum value.zig created+23
......@@ -0,0 +1,23 @@
1const std = @import("std");
2
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
4 _ = stack_trace;
5 if (std.mem.eql(u8, message, "switch on corrupt value")) {
6 std.process.exit(0);
7 }
8 std.process.exit(1);
9}
10const E = enum(u32) {
11 one = 1,
12 two = 2,
13};
14pub fn main() !void {
15 var a: E = undefined;
16 @ptrCast(*u32, &a).* = 255;
17 switch (a) {
18 else => @panic("else"),
19 }
20}
21// run
22// backend=llvm
23// target=native