authorgravatar for justus@klausecker.deJustus Klausecker <justus@klausecker.de> 2026-03-11 16:30:12+01:00
committergravatar for justus@klausecker.deJustus Klausecker <justus@klausecker.de> 2026-03-11 16:44:08+01:00
logbe9b42d7074077eadf18e8d934c6f22051397a72
tree10f8788abd89af5dedcd9c3a371ab9f32b30f5e7
parentb4ffb402c082605c4b324e88120306fc8fb3cf32

compiler: allow equality comparisons for packed unions

This was already possible in practice by just wrapping a packed union into a packed struct. Now it's also possible without doing that.

5 files changed, 43 insertions(+), 3 deletions(-)

doc/langref.html.in+7
......@@ -2513,6 +2513,13 @@ or
25132513 <p>A {#syntax#}packed union{#endsyntax#} has well-defined in-memory layout and is eligible
25142514 to be in a {#link|packed struct#}.</p>
25152515 <p>All fields in a packed union must have the same {#link|@bitSizeOf#}.</p>
2516
2517 <p>
2518 Equating packed unions results in a comparison of the backing integer,
2519 and only works for the {#syntax#}=={#endsyntax#} and {#syntax#}!={#endsyntax#} {#link|Operators#}.
2520 </p>
2521 {#code|test_packed_union_equality.zig#}
2522
25162523 {#header_close#}
25172524
25182525 {#header_open|Anonymous Union Literals#}
doc/langref/test_packed_union_equality.zig created+14
......@@ -0,0 +1,14 @@
1const std = @import("std");
2const expectEqual = std.testing.expectEqual;
3
4test "packed union equality" {
5 const U = packed union {
6 a: u4,
7 b: i4,
8 };
9 const x: U = .{ .a = 3 };
10 const y: U = .{ .b = 3 };
11 try expectEqual(x, y);
12}
13
14// test
src/Type.zig+1-2
......@@ -334,11 +334,10 @@ pub fn isSelfComparable(ty: Type, zcu: *const Zcu, is_equality_cmp: bool) bool {
334334 .undefined,
335335 .null,
336336 .error_union,
337 .@"union",
338337 .frame,
339338 => false,
340339
341 .@"struct" => is_equality_cmp and ty.containerLayout(zcu) == .@"packed",
340 .@"struct", .@"union" => is_equality_cmp and ty.containerLayout(zcu) == .@"packed",
342341 .pointer => !ty.isSlice(zcu) and (is_equality_cmp or ty.isCPtr(zcu)),
343342 .optional => {
344343 if (!is_equality_cmp) return false;
src/codegen/llvm.zig+1-1
......@@ -5750,7 +5750,7 @@ pub const FuncGen = struct {
57505750 return phi.toValue();
57515751 },
57525752 .float => return self.buildFloatCmp(fast, op, operand_ty, .{ lhs, rhs }),
5753 .@"struct" => scalar_ty.bitpackBackingInt(zcu),
5753 .@"struct", .@"union" => scalar_ty.bitpackBackingInt(zcu),
57545754 else => unreachable,
57555755 };
57565756 const is_signed = int_ty.isSignedInt(zcu);
test/behavior/packed-union.zig+20
......@@ -199,3 +199,23 @@ test "packed union with explicit backing integer" {
199199 try U.check(.{ .raw = -2 });
200200 try comptime U.check(.{ .raw = -2 });
201201}
202
203test "packed union equality" {
204 const Foo = packed union {
205 a: u4,
206 b: i4,
207 };
208
209 const S = struct {
210 fn doTest(x: Foo, y: Foo) !void {
211 try expect(x == y);
212 try expect(!(x != y));
213 }
214 };
215
216 const x: Foo = .{ .a = 3 };
217 const y: Foo = .{ .b = 3 };
218
219 try S.doTest(x, y);
220 comptime try S.doTest(x, y);
221}