authorgravatar for justus@klausecker.deJustus Klausecker <justus@klausecker.de> 2025-12-30 23:47:46+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-01-11 11:37:16+00:00
log42dea36ce91db4d79711a8005ae9124bfb1364b3
tree27c1ecb5ab0a23c0e9978e5588b84ec1cea412f4
parentd840bb511839464c852699acce471efccdd67f3e
signaturelock-open Commit is signed but in an unrecognized format.

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.

2 files changed, 55 insertions(+), 2 deletions(-)

src/codegen/llvm.zig+6-2
......@@ -6432,7 +6432,7 @@ pub const FuncGen = struct {
64326432
64336433 // Don't worry about the size of the type -- it's irrelevant, because the prong values could be fairly dense.
64346434 // If they are, then we will construct a jump table.
6435 const min, const max = self.switchCaseItemRange(switch_br);
6435 const min, const max = self.switchCaseItemRange(switch_br) orelse break :jmp_table null;
64366436 const min_int = min.getUnsignedInt(zcu) orelse break :jmp_table null;
64376437 const max_int = max.getUnsignedInt(zcu) orelse break :jmp_table null;
64386438 const table_len = max_int - min_int + 1;
......@@ -6595,7 +6595,7 @@ pub const FuncGen = struct {
65956595 }
65966596 }
65976597
6598 fn switchCaseItemRange(self: *FuncGen, switch_br: Air.UnwrappedSwitch) [2]Value {
6598 fn switchCaseItemRange(self: *FuncGen, switch_br: Air.UnwrappedSwitch) ?[2]Value {
65996599 const zcu = self.ng.pt.zcu;
66006600 var it = switch_br.iterateCases();
66016601 var min: ?Value = null;
......@@ -6619,6 +6619,10 @@ pub const FuncGen = struct {
66196619 if (high) max = vals[1];
66206620 }
66216621 }
6622 if (min == null) {
6623 assert(max == null);
6624 return null;
6625 }
66226626 return .{ min.?, max.? };
66236627 }
66246628
test/behavior/switch_loop.zig+49
......@@ -297,3 +297,52 @@ test "switch loop with discarded tag capture" {
297297 S.doTheTest();
298298 comptime S.doTheTest();
299299}
300
301test "switch loop with single catch-all prong" {
302 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
303
304 const S = struct {
305 const E = enum { a, b, c };
306 const U = union(E) { a: u32, b: u16, c: u8 };
307
308 fn doTheTest() !void {
309 var x: usize = 0;
310 label: switch (E.a) {
311 else => {
312 x += 1;
313 if (x >= 5) continue :label .b;
314 if (x == 10) break :label;
315 continue :label .c;
316 },
317 }
318 try expect(x == 10);
319
320 label: switch (E.a) {
321 .a, .b, .c => {
322 x += 1;
323 if (x >= 15) continue :label .b;
324 if (x == 20) break :label;
325 continue :label .c;
326 },
327 }
328 try expect(x == 20);
329
330 label: switch (E.a) {
331 else => if (false) continue :label true,
332 }
333
334 const ok = label: switch (U{ .a = 123 }) {
335 else => |u| {
336 const y: u32 = switch (u) {
337 inline else => |y| y,
338 };
339 if (y == 456) break :label true;
340 continue :label .{ .b = 456 };
341 },
342 };
343 try expect(ok);
344 }
345 };
346 try S.doTheTest();
347 try comptime S.doTheTest();
348}