From 42dea36ce91db4d79711a8005ae9124bfb1364b3 Mon Sep 17 00:00:00 2001 From: Justus Klausecker Date: Tue, 30 Dec 2025 23:47:46 +0100 Subject: [PATCH] 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. --- src/codegen/llvm.zig | 8 ++++-- test/behavior/switch_loop.zig | 49 +++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index fca89ea4fc..358d4ac469 100644 --- 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 index 0df3383342..5c973d9e1b 100644 --- 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(); +}