commit 42dea36ce91db4d79711a8005ae9124bfb1364b3 (tree)
parent d840bb511839464c852699acce471efccdd67f3e
Author: Justus Klausecker <justus@klausecker.de>
Date: Tue, 30 Dec 2025 23:47:46 +0100
llvm: fix jump table gen for labeled switch with single `else` prong
Avoids a null unwrap if there are no cases with explicit values present
while trying to construct a jump table for a labeled switch statement.
Diffstat:
2 files changed, 55 insertions(+), 2 deletions(-)
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig
@@ -6432,7 +6432,7 @@ pub const FuncGen = struct {
// Don't worry about the size of the type -- it's irrelevant, because the prong values could be fairly dense.
// If they are, then we will construct a jump table.
- const min, const max = self.switchCaseItemRange(switch_br);
+ const min, const max = self.switchCaseItemRange(switch_br) orelse break :jmp_table null;
const min_int = min.getUnsignedInt(zcu) orelse break :jmp_table null;
const max_int = max.getUnsignedInt(zcu) orelse break :jmp_table null;
const table_len = max_int - min_int + 1;
@@ -6595,7 +6595,7 @@ pub const FuncGen = struct {
}
}
- fn switchCaseItemRange(self: *FuncGen, switch_br: Air.UnwrappedSwitch) [2]Value {
+ fn switchCaseItemRange(self: *FuncGen, switch_br: Air.UnwrappedSwitch) ?[2]Value {
const zcu = self.ng.pt.zcu;
var it = switch_br.iterateCases();
var min: ?Value = null;
@@ -6619,6 +6619,10 @@ pub const FuncGen = struct {
if (high) max = vals[1];
}
}
+ if (min == null) {
+ assert(max == null);
+ return null;
+ }
return .{ min.?, max.? };
}
diff --git a/test/behavior/switch_loop.zig b/test/behavior/switch_loop.zig
@@ -297,3 +297,52 @@ test "switch loop with discarded tag capture" {
S.doTheTest();
comptime S.doTheTest();
}
+
+test "switch loop with single catch-all prong" {
+ if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
+
+ const S = struct {
+ const E = enum { a, b, c };
+ const U = union(E) { a: u32, b: u16, c: u8 };
+
+ fn doTheTest() !void {
+ var x: usize = 0;
+ label: switch (E.a) {
+ else => {
+ x += 1;
+ if (x >= 5) continue :label .b;
+ if (x == 10) break :label;
+ continue :label .c;
+ },
+ }
+ try expect(x == 10);
+
+ label: switch (E.a) {
+ .a, .b, .c => {
+ x += 1;
+ if (x >= 15) continue :label .b;
+ if (x == 20) break :label;
+ continue :label .c;
+ },
+ }
+ try expect(x == 20);
+
+ label: switch (E.a) {
+ else => if (false) continue :label true,
+ }
+
+ const ok = label: switch (U{ .a = 123 }) {
+ else => |u| {
+ const y: u32 = switch (u) {
+ inline else => |y| y,
+ };
+ if (y == 456) break :label true;
+ continue :label .{ .b = 456 };
+ },
+ };
+ try expect(ok);
+ }
+ };
+ try S.doTheTest();
+ try comptime S.doTheTest();
+}