mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 13:54:21 +00:00
implement packed struct equality (#21679)
This commit is contained in:
parent
ba1331090c
commit
e131a2c8e2
7 changed files with 93 additions and 1 deletions
|
|
@ -2190,6 +2190,7 @@ or
|
||||||
<li>An {#link|enum#} field uses exactly the bit width of its integer tag type.</li>
|
<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
|
<li>A {#link|packed union#} field uses exactly the bit width of the union field with
|
||||||
the largest bit width.</li>
|
the largest bit width.</li>
|
||||||
|
<li>Packed structs support equality operators.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>
|
<p>
|
||||||
This means that a {#syntax#}packed struct{#endsyntax#} can participate
|
This means that a {#syntax#}packed struct{#endsyntax#} can participate
|
||||||
|
|
@ -2240,6 +2241,12 @@ or
|
||||||
</p>
|
</p>
|
||||||
{#code|test_aligned_struct_fields.zig#}
|
{#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>
|
<p>
|
||||||
Using packed structs with {#link|volatile#} is problematic, and may be a compile error in the future.
|
Using packed structs with {#link|volatile#} is problematic, and may be a compile error in the future.
|
||||||
For details on this subscribe to
|
For details on this subscribe to
|
||||||
|
|
|
||||||
14
doc/langref/test_packed_struct_equality.zig
Normal file
14
doc/langref/test_packed_struct_equality.zig
Normal file
|
|
@ -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
|
||||||
|
|
@ -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 {
|
pub fn isSelfComparable(ty: Type, zcu: *const Zcu, is_equality_cmp: bool) bool {
|
||||||
return switch (ty.zigTypeTag(zcu)) {
|
return switch (ty.zigTypeTag(zcu)) {
|
||||||
.int,
|
.int,
|
||||||
|
|
@ -62,7 +63,6 @@ pub fn isSelfComparable(ty: Type, zcu: *const Zcu, is_equality_cmp: bool) bool {
|
||||||
|
|
||||||
.noreturn,
|
.noreturn,
|
||||||
.array,
|
.array,
|
||||||
.@"struct",
|
|
||||||
.undefined,
|
.undefined,
|
||||||
.null,
|
.null,
|
||||||
.error_union,
|
.error_union,
|
||||||
|
|
@ -70,6 +70,7 @@ pub fn isSelfComparable(ty: Type, zcu: *const Zcu, is_equality_cmp: bool) bool {
|
||||||
.frame,
|
.frame,
|
||||||
=> false,
|
=> false,
|
||||||
|
|
||||||
|
.@"struct" => is_equality_cmp and ty.containerLayout(zcu) == .@"packed",
|
||||||
.pointer => !ty.isSlice(zcu) and (is_equality_cmp or ty.isCPtr(zcu)),
|
.pointer => !ty.isSlice(zcu) and (is_equality_cmp or ty.isCPtr(zcu)),
|
||||||
.optional => {
|
.optional => {
|
||||||
if (!is_equality_cmp) return false;
|
if (!is_equality_cmp) return false;
|
||||||
|
|
|
||||||
|
|
@ -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 bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
|
||||||
const pt = func.pt;
|
const pt = func.pt;
|
||||||
const zcu = pt.zcu;
|
const zcu = pt.zcu;
|
||||||
|
const ip = &zcu.intern_pool;
|
||||||
|
|
||||||
const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: {
|
const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: {
|
||||||
const lhs_ty = func.typeOf(bin_op.lhs);
|
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,
|
.pointer,
|
||||||
.error_set,
|
.error_set,
|
||||||
.optional,
|
.optional,
|
||||||
|
.@"struct",
|
||||||
=> {
|
=> {
|
||||||
const int_ty = switch (lhs_ty.zigTypeTag(zcu)) {
|
const int_ty = switch (lhs_ty.zigTypeTag(zcu)) {
|
||||||
.@"enum" => lhs_ty.intTagType(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", .{});
|
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,
|
else => unreachable,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6032,6 +6032,7 @@ pub const FuncGen = struct {
|
||||||
const o = self.ng.object;
|
const o = self.ng.object;
|
||||||
const pt = o.pt;
|
const pt = o.pt;
|
||||||
const zcu = pt.zcu;
|
const zcu = pt.zcu;
|
||||||
|
const ip = &zcu.intern_pool;
|
||||||
const scalar_ty = operand_ty.scalarType(zcu);
|
const scalar_ty = operand_ty.scalarType(zcu);
|
||||||
const int_ty = switch (scalar_ty.zigTypeTag(zcu)) {
|
const int_ty = switch (scalar_ty.zigTypeTag(zcu)) {
|
||||||
.@"enum" => scalar_ty.intTagType(zcu),
|
.@"enum" => scalar_ty.intTagType(zcu),
|
||||||
|
|
@ -6110,6 +6111,12 @@ pub const FuncGen = struct {
|
||||||
return phi.toValue();
|
return phi.toValue();
|
||||||
},
|
},
|
||||||
.float => return self.buildFloatCmp(fast, op, operand_ty, .{ lhs, rhs }),
|
.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,
|
else => unreachable,
|
||||||
};
|
};
|
||||||
const is_signed = int_ty.isSignedInt(zcu);
|
const is_signed = int_ty.isSignedInt(zcu);
|
||||||
|
|
|
||||||
|
|
@ -1297,3 +1297,23 @@ test "packed struct contains optional pointer" {
|
||||||
} = .{};
|
} = .{};
|
||||||
try expect(foo.a == null);
|
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);
|
||||||
|
}
|
||||||
|
|
|
||||||
35
test/cases/compile_errors/packed_struct_comparison.zig
Normal file
35
test/cases/compile_errors/packed_struct_comparison.zig
Normal file
|
|
@ -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
|
||||||
Loading…
Add table
Reference in a new issue