| author | |
| committer | |
| log | e131a2c8e20de13256954cbb38ca3502cdfca07b |
| tree | 4734479898bf5a21d931f52d84039136351e17ee |
| parent | ba1331090c19662dc0eff4d38f80df6ec58c675a |
| signature |
7 files changed, 93 insertions(+), 1 deletions(-)
doc/langref.html.in+7| ... | ... | @@ -2190,6 +2190,7 @@ or |
| 2190 | 2190 | <li>An {#link|enum#} field uses exactly the bit width of its integer tag type.</li> |
| 2191 | 2191 | <li>A {#link|packed union#} field uses exactly the bit width of the union field with |
| 2192 | 2192 | the largest bit width.</li> |
| 2193 | <li>Packed structs support equality operators.</li> | |
| 2193 | 2194 | </ul> |
| 2194 | 2195 | <p> |
| 2195 | 2196 | This means that a {#syntax#}packed struct{#endsyntax#} can participate |
| ... | ... | @@ -2240,6 +2241,12 @@ or |
| 2240 | 2241 | </p> |
| 2241 | 2242 | {#code|test_aligned_struct_fields.zig#} |
| 2242 | 2243 | |
| 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 | ||
| 2243 | 2250 | <p> |
| 2244 | 2251 | Using packed structs with {#link|volatile#} is problematic, and may be a compile error in the future. |
| 2245 | 2252 | For details on this subscribe to |
doc/langref/test_packed_struct_equality.zig created+14| ... | ... | @@ -0,0 +1,14 @@ |
| 1 | const std = @import("std"); | |
| 2 | const expect = std.testing.expect; | |
| 3 | ||
| 4 | test "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 { |
| 39 | 39 | }; |
| 40 | 40 | } |
| 41 | 41 | |
| 42 | /// Asserts the type is resolved. | |
| 42 | 43 | pub fn isSelfComparable(ty: Type, zcu: *const Zcu, is_equality_cmp: bool) bool { |
| 43 | 44 | return switch (ty.zigTypeTag(zcu)) { |
| 44 | 45 | .int, |
| ... | ... | @@ -62,7 +63,6 @@ pub fn isSelfComparable(ty: Type, zcu: *const Zcu, is_equality_cmp: bool) bool { |
| 62 | 63 | |
| 63 | 64 | .noreturn, |
| 64 | 65 | .array, |
| 65 | .@"struct", | |
| 66 | 66 | .undefined, |
| 67 | 67 | .null, |
| 68 | 68 | .error_union, |
| ... | ... | @@ -70,6 +70,7 @@ pub fn isSelfComparable(ty: Type, zcu: *const Zcu, is_equality_cmp: bool) bool { |
| 70 | 70 | .frame, |
| 71 | 71 | => false, |
| 72 | 72 | |
| 73 | .@"struct" => is_equality_cmp and ty.containerLayout(zcu) == .@"packed", | |
| 73 | 74 | .pointer => !ty.isSlice(zcu) and (is_equality_cmp or ty.isCPtr(zcu)), |
| 74 | 75 | .optional => { |
| 75 | 76 | 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 { |
| 5162 | 5162 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 5163 | 5163 | const pt = func.pt; |
| 5164 | 5164 | const zcu = pt.zcu; |
| 5165 | const ip = &zcu.intern_pool; | |
| 5165 | 5166 | |
| 5166 | 5167 | const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: { |
| 5167 | 5168 | 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 { |
| 5173 | 5174 | .pointer, |
| 5174 | 5175 | .error_set, |
| 5175 | 5176 | .optional, |
| 5177 | .@"struct", | |
| 5176 | 5178 | => { |
| 5177 | 5179 | const int_ty = switch (lhs_ty.zigTypeTag(zcu)) { |
| 5178 | 5180 | .@"enum" => lhs_ty.intTagType(zcu), |
| ... | ... | @@ -5190,6 +5192,12 @@ fn airCmp(func: *Func, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { |
| 5190 | 5192 | return func.fail("TODO riscv cmp non-pointer optionals", .{}); |
| 5191 | 5193 | } |
| 5192 | 5194 | }, |
| 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 | }, | |
| 5193 | 5201 | else => unreachable, |
| 5194 | 5202 | }; |
| 5195 | 5203 |
src/codegen/llvm.zig+7| ... | ... | @@ -6032,6 +6032,7 @@ pub const FuncGen = struct { |
| 6032 | 6032 | const o = self.ng.object; |
| 6033 | 6033 | const pt = o.pt; |
| 6034 | 6034 | const zcu = pt.zcu; |
| 6035 | const ip = &zcu.intern_pool; | |
| 6035 | 6036 | const scalar_ty = operand_ty.scalarType(zcu); |
| 6036 | 6037 | const int_ty = switch (scalar_ty.zigTypeTag(zcu)) { |
| 6037 | 6038 | .@"enum" => scalar_ty.intTagType(zcu), |
| ... | ... | @@ -6110,6 +6111,12 @@ pub const FuncGen = struct { |
| 6110 | 6111 | return phi.toValue(); |
| 6111 | 6112 | }, |
| 6112 | 6113 | .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 | }, | |
| 6113 | 6120 | else => unreachable, |
| 6114 | 6121 | }; |
| 6115 | 6122 | const is_signed = int_ty.isSignedInt(zcu); |
test/behavior/packed-struct.zig+20| ... | ... | @@ -1297,3 +1297,23 @@ test "packed struct contains optional pointer" { |
| 1297 | 1297 | } = .{}; |
| 1298 | 1298 | try expect(foo.a == null); |
| 1299 | 1299 | } |
| 1300 | ||
| 1301 | test "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 @@ |
| 1 | const x: Foo = .{}; | |
| 2 | const y: Foo = .{}; | |
| 3 | ||
| 4 | export fn a() void { | |
| 5 | _ = x > y; | |
| 6 | } | |
| 7 | ||
| 8 | export fn b() void { | |
| 9 | _ = x < y; | |
| 10 | } | |
| 11 | ||
| 12 | export fn c() void { | |
| 13 | _ = x >= y; | |
| 14 | } | |
| 15 | export fn d() void { | |
| 16 | _ = x <= y; | |
| 17 | } | |
| 18 | ||
| 19 | const 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 |