Commit e131a2c8e2
Changed files (7)
src
test
behavior
cases
compile_errors
doc/langref/test_packed_struct_equality.zig
@@ -0,0 +1,14 @@
+const std = @import("std");
+const expect = std.testing.expect;
+
+test "packed struct equality" {
+ const S = packed struct {
+ a: u4,
+ b: u4,
+ };
+ const x: S = .{ .a = 1, .b = 2 };
+ const y: S = .{ .b = 2, .a = 1 };
+ try expect(x == y);
+}
+
+// test
doc/langref.html.in
@@ -2190,6 +2190,7 @@ or
<li>An {#link|enum#} field uses exactly the bit width of its integer tag type.</li>
<li>A {#link|packed union#} field uses exactly the bit width of the union field with
the largest bit width.</li>
+ <li>Packed structs support equality operators.</li>
</ul>
<p>
This means that a {#syntax#}packed struct{#endsyntax#} can participate
@@ -2240,6 +2241,12 @@ or
</p>
{#code|test_aligned_struct_fields.zig#}
+ <p>
+ Equating packed structs results in a comparison of the backing integer,
+ and only works for the `==` and `!=` operators.
+ </p>
+ {#code|test_packed_struct_equality.zig#}
+
<p>
Using packed structs with {#link|volatile#} is problematic, and may be a compile error in the future.
For details on this subscribe to
src/arch/riscv64/CodeGen.zig
@@ -5162,6 +5162,7 @@ fn airCmp(func: *Func, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
const pt = func.pt;
const zcu = pt.zcu;
+ const ip = &zcu.intern_pool;
const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: {
const lhs_ty = func.typeOf(bin_op.lhs);
@@ -5173,6 +5174,7 @@ fn airCmp(func: *Func, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
.pointer,
.error_set,
.optional,
+ .@"struct",
=> {
const int_ty = switch (lhs_ty.zigTypeTag(zcu)) {
.@"enum" => lhs_ty.intTagType(zcu),
@@ -5190,6 +5192,12 @@ fn airCmp(func: *Func, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
return func.fail("TODO riscv cmp non-pointer optionals", .{});
}
},
+ .@"struct" => blk: {
+ const struct_obj = ip.loadStructType(lhs_ty.toIntern());
+ assert(struct_obj.layout == .@"packed");
+ const backing_index = struct_obj.backingIntTypeUnordered(ip);
+ break :blk Type.fromInterned(backing_index);
+ },
else => unreachable,
};
src/codegen/llvm.zig
@@ -6032,6 +6032,7 @@ pub const FuncGen = struct {
const o = self.ng.object;
const pt = o.pt;
const zcu = pt.zcu;
+ const ip = &zcu.intern_pool;
const scalar_ty = operand_ty.scalarType(zcu);
const int_ty = switch (scalar_ty.zigTypeTag(zcu)) {
.@"enum" => scalar_ty.intTagType(zcu),
@@ -6110,6 +6111,12 @@ pub const FuncGen = struct {
return phi.toValue();
},
.float => return self.buildFloatCmp(fast, op, operand_ty, .{ lhs, rhs }),
+ .@"struct" => blk: {
+ const struct_obj = ip.loadStructType(scalar_ty.toIntern());
+ assert(struct_obj.layout == .@"packed");
+ const backing_index = struct_obj.backingIntTypeUnordered(ip);
+ break :blk Type.fromInterned(backing_index);
+ },
else => unreachable,
};
const is_signed = int_ty.isSignedInt(zcu);
src/Type.zig
@@ -39,6 +39,7 @@ pub fn baseZigTypeTag(self: Type, mod: *Zcu) std.builtin.TypeId {
};
}
+/// Asserts the type is resolved.
pub fn isSelfComparable(ty: Type, zcu: *const Zcu, is_equality_cmp: bool) bool {
return switch (ty.zigTypeTag(zcu)) {
.int,
@@ -62,7 +63,6 @@ pub fn isSelfComparable(ty: Type, zcu: *const Zcu, is_equality_cmp: bool) bool {
.noreturn,
.array,
- .@"struct",
.undefined,
.null,
.error_union,
@@ -70,6 +70,7 @@ pub fn isSelfComparable(ty: Type, zcu: *const Zcu, is_equality_cmp: bool) bool {
.frame,
=> false,
+ .@"struct" => 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;
test/behavior/packed-struct.zig
@@ -1297,3 +1297,23 @@ test "packed struct contains optional pointer" {
} = .{};
try expect(foo.a == null);
}
+
+test "packed struct equality" {
+ const Foo = packed struct {
+ a: u4,
+ b: u4,
+ };
+
+ const S = struct {
+ fn doTest(x: Foo, y: Foo) !void {
+ try expect(x == y);
+ try expect(!(x != y));
+ }
+ };
+
+ const x: Foo = .{ .a = 1, .b = 2 };
+ const y: Foo = .{ .b = 2, .a = 1 };
+
+ try S.doTest(x, y);
+ comptime try S.doTest(x, y);
+}
test/cases/compile_errors/packed_struct_comparison.zig
@@ -0,0 +1,35 @@
+const x: Foo = .{};
+const y: Foo = .{};
+
+export fn a() void {
+ _ = x > y;
+}
+
+export fn b() void {
+ _ = x < y;
+}
+
+export fn c() void {
+ _ = x >= y;
+}
+export fn d() void {
+ _ = x <= y;
+}
+
+const Foo = packed struct {
+ a: u4 = 10,
+ b: u4 = 5,
+};
+
+// error
+// backend=stage2
+// target=native
+//
+// :5:11: error: operator > not allowed for type 'tmp.Foo'
+// :19:20: note: struct declared here
+// :9:11: error: operator < not allowed for type 'tmp.Foo'
+// :19:20: note: struct declared here
+// :13:11: error: operator >= not allowed for type 'tmp.Foo'
+// :19:20: note: struct declared here
+// :16:11: error: operator <= not allowed for type 'tmp.Foo'
+// :19:20: note: struct declared here