authorgravatar for david@vortan.devDavid Rubin <david@vortan.dev> 2024-10-12 20:59:12-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-10-12 20:59:12-07:00
loge131a2c8e20de13256954cbb38ca3502cdfca07b
tree4734479898bf5a21d931f52d84039136351e17ee
parentba1331090c19662dc0eff4d38f80df6ec58c675a
signaturebadge-check Signed by PGP key B5690EEEBB952194

implement packed struct equality (#21679)


7 files changed, 93 insertions(+), 1 deletions(-)

doc/langref.html.in+7
......@@ -2190,6 +2190,7 @@ or
21902190 <li>An {#link|enum#} field uses exactly the bit width of its integer tag type.</li>
21912191 <li>A {#link|packed union#} field uses exactly the bit width of the union field with
21922192 the largest bit width.</li>
2193 <li>Packed structs support equality operators.</li>
21932194 </ul>
21942195 <p>
21952196 This means that a {#syntax#}packed struct{#endsyntax#} can participate
......@@ -2240,6 +2241,12 @@ or
22402241 </p>
22412242 {#code|test_aligned_struct_fields.zig#}
22422243
2244 <p>
2245 Equating packed structs results in a comparison of the backing integer,
2246 and only works for the `==` and `!=` operators.
2247 </p>
2248 {#code|test_packed_struct_equality.zig#}
2249
22432250 <p>
22442251 Using packed structs with {#link|volatile#} is problematic, and may be a compile error in the future.
22452252 For details on this subscribe to
doc/langref/test_packed_struct_equality.zig created+14
......@@ -0,0 +1,14 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4test "packed struct equality" {
5 const S = packed struct {
6 a: u4,
7 b: u4,
8 };
9 const x: S = .{ .a = 1, .b = 2 };
10 const y: S = .{ .b = 2, .a = 1 };
11 try expect(x == y);
12}
13
14// test
src/Type.zig+2-1
......@@ -39,6 +39,7 @@ pub fn baseZigTypeTag(self: Type, mod: *Zcu) std.builtin.TypeId {
3939 };
4040}
4141
42/// Asserts the type is resolved.
4243pub fn isSelfComparable(ty: Type, zcu: *const Zcu, is_equality_cmp: bool) bool {
4344 return switch (ty.zigTypeTag(zcu)) {
4445 .int,
......@@ -62,7 +63,6 @@ pub fn isSelfComparable(ty: Type, zcu: *const Zcu, is_equality_cmp: bool) bool {
6263
6364 .noreturn,
6465 .array,
65 .@"struct",
6666 .undefined,
6767 .null,
6868 .error_union,
......@@ -70,6 +70,7 @@ pub fn isSelfComparable(ty: Type, zcu: *const Zcu, is_equality_cmp: bool) bool {
7070 .frame,
7171 => false,
7272
73 .@"struct" => is_equality_cmp and ty.containerLayout(zcu) == .@"packed",
7374 .pointer => !ty.isSlice(zcu) and (is_equality_cmp or ty.isCPtr(zcu)),
7475 .optional => {
7576 if (!is_equality_cmp) return false;
src/arch/riscv64/CodeGen.zig+8
......@@ -5162,6 +5162,7 @@ fn airCmp(func: *Func, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
51625162 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
51635163 const pt = func.pt;
51645164 const zcu = pt.zcu;
5165 const ip = &zcu.intern_pool;
51655166
51665167 const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: {
51675168 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 {
51735174 .pointer,
51745175 .error_set,
51755176 .optional,
5177 .@"struct",
51765178 => {
51775179 const int_ty = switch (lhs_ty.zigTypeTag(zcu)) {
51785180 .@"enum" => lhs_ty.intTagType(zcu),
......@@ -5190,6 +5192,12 @@ fn airCmp(func: *Func, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
51905192 return func.fail("TODO riscv cmp non-pointer optionals", .{});
51915193 }
51925194 },
5195 .@"struct" => blk: {
5196 const struct_obj = ip.loadStructType(lhs_ty.toIntern());
5197 assert(struct_obj.layout == .@"packed");
5198 const backing_index = struct_obj.backingIntTypeUnordered(ip);
5199 break :blk Type.fromInterned(backing_index);
5200 },
51935201 else => unreachable,
51945202 };
51955203
src/codegen/llvm.zig+7
......@@ -6032,6 +6032,7 @@ pub const FuncGen = struct {
60326032 const o = self.ng.object;
60336033 const pt = o.pt;
60346034 const zcu = pt.zcu;
6035 const ip = &zcu.intern_pool;
60356036 const scalar_ty = operand_ty.scalarType(zcu);
60366037 const int_ty = switch (scalar_ty.zigTypeTag(zcu)) {
60376038 .@"enum" => scalar_ty.intTagType(zcu),
......@@ -6110,6 +6111,12 @@ pub const FuncGen = struct {
61106111 return phi.toValue();
61116112 },
61126113 .float => return self.buildFloatCmp(fast, op, operand_ty, .{ lhs, rhs }),
6114 .@"struct" => blk: {
6115 const struct_obj = ip.loadStructType(scalar_ty.toIntern());
6116 assert(struct_obj.layout == .@"packed");
6117 const backing_index = struct_obj.backingIntTypeUnordered(ip);
6118 break :blk Type.fromInterned(backing_index);
6119 },
61136120 else => unreachable,
61146121 };
61156122 const is_signed = int_ty.isSignedInt(zcu);
test/behavior/packed-struct.zig+20
......@@ -1297,3 +1297,23 @@ test "packed struct contains optional pointer" {
12971297 } = .{};
12981298 try expect(foo.a == null);
12991299}
1300
1301test "packed struct equality" {
1302 const Foo = packed struct {
1303 a: u4,
1304 b: u4,
1305 };
1306
1307 const S = struct {
1308 fn doTest(x: Foo, y: Foo) !void {
1309 try expect(x == y);
1310 try expect(!(x != y));
1311 }
1312 };
1313
1314 const x: Foo = .{ .a = 1, .b = 2 };
1315 const y: Foo = .{ .b = 2, .a = 1 };
1316
1317 try S.doTest(x, y);
1318 comptime try S.doTest(x, y);
1319}
test/cases/compile_errors/packed_struct_comparison.zig created+35
......@@ -0,0 +1,35 @@
1const x: Foo = .{};
2const y: Foo = .{};
3
4export fn a() void {
5 _ = x > y;
6}
7
8export fn b() void {
9 _ = x < y;
10}
11
12export fn c() void {
13 _ = x >= y;
14}
15export fn d() void {
16 _ = x <= y;
17}
18
19const Foo = packed struct {
20 a: u4 = 10,
21 b: u4 = 5,
22};
23
24// error
25// backend=stage2
26// target=native
27//
28// :5:11: error: operator > not allowed for type 'tmp.Foo'
29// :19:20: note: struct declared here
30// :9:11: error: operator < not allowed for type 'tmp.Foo'
31// :19:20: note: struct declared here
32// :13:11: error: operator >= not allowed for type 'tmp.Foo'
33// :19:20: note: struct declared here
34// :16:11: error: operator <= not allowed for type 'tmp.Foo'
35// :19:20: note: struct declared here