Add support for both '_' and 'else' prongs at the same time in switch statements

If both are used, 'else' handles named members and '_' handles
unnamed members. In this case the 'else' prong will be unrolled
to an explicit case containing all remaining named values.
This commit is contained in:
Justus Klausecker
2025-07-10 01:58:02 +02:00
parent 1d9b1c0212
commit ba549a7d67
11 changed files with 770 additions and 364 deletions

View File

@@ -1075,26 +1075,50 @@ test "switch on 8-bit mod result" {
}
test "switch on non-exhaustive enum" {
const E = enum(u32) {
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; // TODO
const E = enum(u4) {
a,
b,
c,
_,
fn doTheTest(e: @This()) !void {
switch (e) {
.a, .b => {},
else => return error.TestFailed,
}
switch (e) {
.a, .b => {},
.c => return error.TestFailed,
_ => return error.TestFailed,
}
switch (e) {
.a, .b => {},
.c, _ => return error.TestFailed,
}
switch (e) {
.a => {},
.b, .c, _ => return error.TestFailed,
}
switch (e) {
.b => return error.TestFailed,
else => {},
_ => return error.TestFailed,
}
switch (e) {
else => {},
_ => return error.TestFailed,
}
switch (e) {
inline else => {},
_ => return error.TestFailed,
}
}
};
var e: E = .a;
_ = &e;
switch (e) {
.a, .b => {},
else => return error.TestFailed,
}
switch (e) {
.a, .b => {},
.c => return error.TestFailed,
_ => return error.TestFailed,
}
switch (e) {
.a, .b => {},
.c, _ => return error.TestFailed,
}
try E.doTheTest(e);
try comptime E.doTheTest(.a);
}

View File

@@ -249,3 +249,27 @@ test "switch loop on larger than pointer integer" {
}
try expect(entry == 3);
}
test "switch loop on non-exhaustive enum" {
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_spirv) return error.SkipZigTest; // TODO
const S = struct {
const E = enum(u8) { a, b, c, _ };
fn doTheTest() !void {
var start: E = undefined;
start = .a;
const result: u32 = s: switch (start) {
.a => continue :s .c,
else => continue :s @enumFromInt(123),
.b, _ => |x| break :s @intFromEnum(x),
};
try expect(result == 123);
}
};
try S.doTheTest();
try comptime S.doTheTest();
}

View File

@@ -0,0 +1,27 @@
const E = enum(u8) {
a,
b,
_,
};
export fn f(e: E) void {
switch (e) {
.a => {},
inline _ => {},
}
}
export fn g(e: E) void {
switch (e) {
.a => {},
else => {},
inline _ => {},
}
}
// error
// backend=stage2
// target=native
//
// :10:16: error: cannot inline '_' prong
// :18:16: error: cannot inline '_' prong

View File

@@ -0,0 +1,18 @@
const E = enum(u8) {
a,
b,
_,
};
export fn f(e: E) void {
switch (e) {
.a, .b, _ => {},
else => {},
}
}
// error
// backend=stage2
// target=native
//
// :10:14: error: unreachable else prong; all explicit cases already handled

View File

@@ -37,7 +37,7 @@ pub export fn entry3() void {
// :12:5: error: switch must handle all possibilities
// :3:5: note: unhandled enumeration value: 'b'
// :1:11: note: enum 'tmp.E' declared here
// :19:5: error: switch on non-exhaustive enum must include 'else' or '_' prong
// :19:5: error: switch on non-exhaustive enum must include 'else' or '_' prong or both
// :26:5: error: '_' prong only allowed when switching on non-exhaustive enums
// :29:9: note: '_' prong here
// :26:5: note: consider using 'else'