Merge pull request 'Sema: implement switch for packed structs/unions' (#31464) from justusk/zig:packed-switch into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/31464
Reviewed-by: Andrew Kelley <andrew@ziglang.org>
This commit is contained in:
Andrew Kelley
2026-03-12 00:39:19 +01:00
12 changed files with 896 additions and 538 deletions
+20
View File
@@ -199,3 +199,23 @@ test "packed union with explicit backing integer" {
try U.check(.{ .raw = -2 });
try comptime U.check(.{ .raw = -2 });
}
test "packed union equality" {
const Foo = packed union {
a: u4,
b: i4,
};
const S = struct {
fn doTest(x: Foo, y: Foo) !void {
try expect(x == y);
try expect(!(x != y));
}
};
const x: Foo = .{ .a = 3 };
const y: Foo = .{ .b = 3 };
try S.doTest(x, y);
comptime try S.doTest(x, y);
}
+132
View File
@@ -1327,3 +1327,135 @@ test "single range switch prong capture" {
try S.doTheTest(2);
try comptime S.doTheTest(2);
}
test "switch on packed struct" {
const P = packed struct {
a: u1,
b: u1,
fn doTheTest(p: @This()) !void {
switch (p) {
.{ .a = 0, .b = 1 } => {},
else => return error.TestFailed,
}
switch (p) {
.{ .a = 0, .b = 1 } => {},
.{ .a = 0, .b = 0 },
.{ .a = 1, .b = 0 },
.{ .a = 1, .b = 1 },
=> return error.TestFailed,
}
switch (p) {
inline else => |val| {
if (val != @This(){ .a = 0, .b = 1 }) return error.TestFailed;
},
}
}
};
try P.doTheTest(.{ .a = 0, .b = 1 });
try comptime P.doTheTest(.{ .a = 0, .b = 1 });
}
test "switch on packed union" {
const P = packed union(u2) {
a: u2,
b: i2,
c: packed struct(u2) { x: u1, y: i1 },
fn doTheTest(p: @This()) !void {
switch (p) {
.{ .a = 1 } => {},
else => return error.TestFailed,
}
switch (p) {
.{ .a = 1 } => {},
.{ .a = 0 },
.{ .a = 2 },
.{ .a = 3 },
=> return error.TestFailed,
}
switch (p) {
.{ .a = 1 } => {},
.{ .a = 0 },
.{ .b = -2 },
.{ .b = -1 },
=> return error.TestFailed,
}
switch (p) {
.{ .c = .{ .x = 1, .y = 0 } } => {},
.{ .b = 0 },
.{ .a = 2 },
.{ .c = .{ .x = 1, .y = -1 } },
=> return error.TestFailed,
}
switch (p) {
inline else => |val| {
if (val != @This(){ .c = .{ .x = 1, .y = 0 } }) return error.TestFailed;
},
}
}
};
try P.doTheTest(.{ .a = 1 });
try comptime P.doTheTest(.{ .a = 1 });
}
test "switch on nested packed containers" {
if (builtin.object_format == .c) return error.SkipZigTest; // https://codeberg.org/ziglang/zig/issues/31467
const P = packed struct {
iu: u17,
is: i31,
b: bool,
e: enum(u5) { a = 5, b = 3, c = 12 },
un: packed union {
a: i9,
b: u9,
c: packed struct(u9) { a: i5, b: u4 },
},
p: packed struct(u9) { a: u3, b: u6 },
fn doTheTest(p: @This()) !void {
switch (p) {
.{
.iu = 72,
.is = 124,
.b = false,
.e = .c,
.un = .{ .b = 13 },
.p = .{ .a = 0, .b = 12 },
} => return error.TestFailed,
.{
.iu = 129,
.is = -162784612,
.b = true,
.e = .a,
.un = .{ .c = .{ .a = -3, .b = 9 } },
.p = .{ .a = 2, .b = 17 },
} => {},
else => return error.TestFailed,
}
}
};
try P.doTheTest(.{
.iu = 129,
.is = -162784612,
.b = true,
.e = .a,
.un = .{ .c = .{ .a = -3, .b = 9 } },
.p = .{ .a = 2, .b = 17 },
});
try comptime P.doTheTest(.{
.iu = 129,
.is = -162784612,
.b = true,
.e = .a,
.un = .{ .c = .{ .a = -3, .b = 9 } },
.p = .{ .a = 2, .b = 17 },
});
}
+55
View File
@@ -509,3 +509,58 @@ test "switch loop for error handling" {
try S.doTheTest();
try comptime S.doTheTest();
}
test "switch loop with packed structs" {
const P = packed struct {
a: u7,
b: u20,
fn doTheTest(p: @This()) !void {
const result = s: switch (p) {
.{ .a = 5, .b = 10 } => |x| x,
else => |x| continue :s .{ .a = x.a, .b = x.b + 1 },
};
try expect(result == @This(){ .a = 5, .b = 10 });
}
};
try P.doTheTest(.{ .a = 5, .b = 0 });
try comptime P.doTheTest(.{ .a = 5, .b = 0 });
}
test "switch loop with packed unions" {
const P = packed union {
a: u7,
b: i7,
fn doTheTest(p: @This()) !void {
const result = s: switch (p) {
.{ .a = 10 } => |x| x,
else => |x| continue :s .{ .b = @intCast(x.a + 1) },
};
try expect(result == @This(){ .b = 10 });
}
};
try P.doTheTest(.{ .a = 5 });
try comptime P.doTheTest(.{ .a = 5 });
}
test "switch loop with packed unions with OPV" {
const P = packed union {
a: u0,
b: i0,
fn doTheTest(p: @This()) !void {
var looped = false;
s: switch (p) {
.{ .b = 0 } => |x| {
comptime assert(x.a == 0);
if (looped) break :s;
looped = true;
continue :s .{ .a = 0 };
},
}
}
};
try P.doTheTest(.{ .a = 0 });
try comptime P.doTheTest(.{ .a = 0 });
}