From be9b42d7074077eadf18e8d934c6f22051397a72 Mon Sep 17 00:00:00 2001 From: Justus Klausecker Date: Wed, 11 Mar 2026 16:30:12 +0100 Subject: [PATCH] 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. --- doc/langref.html.in | 7 +++++++ doc/langref/test_packed_union_equality.zig | 14 ++++++++++++++ src/Type.zig | 3 +-- src/codegen/llvm.zig | 2 +- test/behavior/packed-union.zig | 20 ++++++++++++++++++++ 5 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 doc/langref/test_packed_union_equality.zig diff --git a/doc/langref.html.in b/doc/langref.html.in index 7118d01c91..adac8f137f 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -2513,6 +2513,13 @@ or

A {#syntax#}packed union{#endsyntax#} has well-defined in-memory layout and is eligible to be in a {#link|packed struct#}.

All fields in a packed union must have the same {#link|@bitSizeOf#}.

+ +

+ Equating packed unions results in a comparison of the backing integer, + and only works for the {#syntax#}=={#endsyntax#} and {#syntax#}!={#endsyntax#} {#link|Operators#}. +

+ {#code|test_packed_union_equality.zig#} + {#header_close#} {#header_open|Anonymous Union Literals#} diff --git a/doc/langref/test_packed_union_equality.zig b/doc/langref/test_packed_union_equality.zig new file mode 100644 index 0000000000..5ba69110ab --- /dev/null +++ b/doc/langref/test_packed_union_equality.zig @@ -0,0 +1,14 @@ +const std = @import("std"); +const expectEqual = std.testing.expectEqual; + +test "packed union equality" { + const U = packed union { + a: u4, + b: i4, + }; + const x: U = .{ .a = 3 }; + const y: U = .{ .b = 3 }; + try expectEqual(x, y); +} + +// test diff --git a/src/Type.zig b/src/Type.zig index 841ca38b0e..475fb09edd 100644 --- a/src/Type.zig +++ b/src/Type.zig @@ -334,11 +334,10 @@ pub fn isSelfComparable(ty: Type, zcu: *const Zcu, is_equality_cmp: bool) bool { .undefined, .null, .error_union, - .@"union", .frame, => false, - .@"struct" => is_equality_cmp and ty.containerLayout(zcu) == .@"packed", + .@"struct", .@"union" => is_equality_cmp and ty.containerLayout(zcu) == .@"packed", .pointer => !ty.isSlice(zcu) and (is_equality_cmp or ty.isCPtr(zcu)), .optional => { if (!is_equality_cmp) return false; diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 0907396630..7661d2fe52 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -5750,7 +5750,7 @@ pub const FuncGen = struct { return phi.toValue(); }, .float => return self.buildFloatCmp(fast, op, operand_ty, .{ lhs, rhs }), - .@"struct" => scalar_ty.bitpackBackingInt(zcu), + .@"struct", .@"union" => scalar_ty.bitpackBackingInt(zcu), else => unreachable, }; const is_signed = int_ty.isSignedInt(zcu); diff --git a/test/behavior/packed-union.zig b/test/behavior/packed-union.zig index 330a4853b1..fec1426657 100644 --- a/test/behavior/packed-union.zig +++ b/test/behavior/packed-union.zig @@ -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); +}