authorgravatar for jhc@dismail.deJimmi Holst Christensen <jhc@dismail.de> 2022-01-08 13:57:22+01:00
committergravatar for jhc@dismail.deJimmi Holst Christensen <jhc@dismail.de> 2022-01-08 14:01:28+01:00
log5cbb35abd06f7975c17b0a188b7a1b88e08dbd1e
tree8d1333ace9bceffc955f651c54dead2d2c15b5c6
parentd8d5e2d4b959f9627853a9f1248609929f0f0de4

Implement bitOffsetOf

This also refactors getting struct field offsets into two iterators. This will be useful when implementing bitCast at comptime on structs.

5 files changed, 277 insertions(+), 174 deletions(-)

src/Sema.zig+25-5
...@@ -10993,12 +10993,16 @@ fn zirShrExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -10993,12 +10993,16 @@ fn zirShrExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
10993}10993}
1099410994
10995fn zirBitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {10995fn zirBitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
10996 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;10996 const offset = try bitOffsetOf(sema, block, inst);
10997 const src = inst_data.src();10997 return sema.addIntUnsigned(Type.comptime_int, offset);
10998 return sema.fail(block, src, "TODO: Sema.zirBitOffsetOf", .{});
10999}10998}
1100010999
11001fn zirOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {11000fn zirOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
11001 const offset = try bitOffsetOf(sema, block, inst);
11002 return sema.addIntUnsigned(Type.comptime_int, offset / 8);
11003}
11004
11005fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u64 {
11002 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;11006 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
11003 sema.src = .{ .node_offset_bin_op = inst_data.src_node };11007 sema.src = .{ .node_offset_bin_op = inst_data.src_node };
11004 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };11008 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
...@@ -11028,8 +11032,24 @@ fn zirOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -11028,8 +11032,24 @@ fn zirOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
11028 };11032 };
1102911033
11030 const target = sema.mod.getTarget();11034 const target = sema.mod.getTarget();
11031 const offset = ty.structFieldOffset(index, target);11035 const layout = ty.containerLayout();
11032 return sema.addIntUnsigned(Type.comptime_int, offset);11036 if (layout == .Packed) {
11037 var it = ty.iteratePackedStructOffsets(target);
11038 while (it.next()) |field_offset| {
11039 if (field_offset.field == index) {
11040 return (field_offset.offset * 8) + field_offset.running_bits;
11041 }
11042 }
11043 } else {
11044 var it = ty.iterateStructOffsets(target);
11045 while (it.next()) |field_offset| {
11046 if (field_offset.field == index) {
11047 return field_offset.offset * 8;
11048 }
11049 }
11050 }
11051
11052 unreachable;
11033}11053}
1103411054
11035/// Returns `true` if the type was a comptime_int.11055/// Returns `true` if the type was a comptime_int.
src/type.zig+137-55
...@@ -2922,6 +2922,15 @@ pub const Type = extern union {...@@ -2922,6 +2922,15 @@ pub const Type = extern union {
2922 }2922 }
2923 }2923 }
29242924
2925 pub fn containerLayout(ty: Type) std.builtin.TypeInfo.ContainerLayout {
2926 return switch (ty.tag()) {
2927 .@"struct" => ty.castTag(.@"struct").?.data.layout,
2928 .@"union" => ty.castTag(.@"union").?.data.layout,
2929 .union_tagged => ty.castTag(.union_tagged).?.data.layout,
2930 else => unreachable,
2931 };
2932 }
2933
2925 /// Asserts that the type is an error union.2934 /// Asserts that the type is an error union.
2926 pub fn errorUnionPayload(self: Type) Type {2935 pub fn errorUnionPayload(self: Type) Type {
2927 return switch (self.tag()) {2936 return switch (self.tag()) {
...@@ -3765,6 +3774,116 @@ pub const Type = extern union {...@@ -3765,6 +3774,116 @@ pub const Type = extern union {
3765 }3774 }
3766 }3775 }
37673776
3777 pub const PackedFieldOffset = struct {
3778 field: usize,
3779 offset: u64,
3780 running_bits: u16,
3781 };
3782
3783 pub const PackedStructOffsetIterator = struct {
3784 field: usize = 0,
3785 offset: u64 = 0,
3786 big_align: u32 = 0,
3787 running_bits: u16 = 0,
3788 struct_obj: *Module.Struct,
3789 target: Target,
3790
3791 pub fn next(it: *PackedStructOffsetIterator) ?PackedFieldOffset {
3792 comptime assert(Type.packed_struct_layout_version == 1);
3793 if (it.struct_obj.fields.count() <= it.field)
3794 return null;
3795
3796 const field = it.struct_obj.fields.values()[it.field];
3797 defer it.field += 1;
3798 if (!field.ty.hasCodeGenBits()) {
3799 return PackedFieldOffset{
3800 .field = it.field,
3801 .offset = it.offset,
3802 .running_bits = it.running_bits,
3803 };
3804 }
3805
3806 const field_align = field.packedAlignment();
3807 if (field_align == 0) {
3808 defer it.running_bits += @intCast(u16, field.ty.bitSize(it.target));
3809 return PackedFieldOffset{
3810 .field = it.field,
3811 .offset = it.offset,
3812 .running_bits = it.running_bits,
3813 };
3814 } else {
3815 it.big_align = @maximum(it.big_align, field_align);
3816
3817 if (it.running_bits != 0) {
3818 var int_payload: Payload.Bits = .{
3819 .base = .{ .tag = .int_unsigned },
3820 .data = it.running_bits,
3821 };
3822 const int_ty: Type = .{ .ptr_otherwise = &int_payload.base };
3823 const int_align = int_ty.abiAlignment(it.target);
3824 it.big_align = @maximum(it.big_align, int_align);
3825 it.offset = std.mem.alignForwardGeneric(u64, it.offset, int_align);
3826 it.offset += int_ty.abiSize(it.target);
3827 it.running_bits = 0;
3828 }
3829 it.offset = std.mem.alignForwardGeneric(u64, it.offset, field_align);
3830 defer it.offset += field.ty.abiSize(it.target);
3831 return PackedFieldOffset{
3832 .field = it.field,
3833 .offset = it.offset,
3834 .running_bits = it.running_bits,
3835 };
3836 }
3837 }
3838 };
3839
3840 /// Get an iterator that iterates over all the struct field, returning the field and
3841 /// offset of that field. Asserts that the type is a none packed struct.
3842 pub fn iteratePackedStructOffsets(ty: Type, target: Target) PackedStructOffsetIterator {
3843 const struct_obj = ty.castTag(.@"struct").?.data;
3844 assert(struct_obj.haveLayout());
3845 assert(struct_obj.layout == .Packed);
3846 return .{ .struct_obj = struct_obj, .target = target };
3847 }
3848
3849 pub const FieldOffset = struct {
3850 field: usize,
3851 offset: u64,
3852 };
3853
3854 pub const StructOffsetIterator = struct {
3855 field: usize = 0,
3856 offset: u64 = 0,
3857 big_align: u32 = 0,
3858 struct_obj: *Module.Struct,
3859 target: Target,
3860
3861 pub fn next(it: *StructOffsetIterator) ?FieldOffset {
3862 if (it.struct_obj.fields.count() <= it.field)
3863 return null;
3864
3865 const field = it.struct_obj.fields.values()[it.field];
3866 defer it.field += 1;
3867 if (!field.ty.hasCodeGenBits())
3868 return FieldOffset{ .field = it.field, .offset = it.offset };
3869
3870 const field_align = field.normalAlignment(it.target);
3871 it.big_align = @maximum(it.big_align, field_align);
3872 it.offset = std.mem.alignForwardGeneric(u64, it.offset, field_align);
3873 defer it.offset += field.ty.abiSize(it.target);
3874 return FieldOffset{ .field = it.field, .offset = it.offset };
3875 }
3876 };
3877
3878 /// Get an iterator that iterates over all the struct field, returning the field and
3879 /// offset of that field. Asserts that the type is a none packed struct.
3880 pub fn iterateStructOffsets(ty: Type, target: Target) StructOffsetIterator {
3881 const struct_obj = ty.castTag(.@"struct").?.data;
3882 assert(struct_obj.haveLayout());
3883 assert(struct_obj.layout != .Packed);
3884 return .{ .struct_obj = struct_obj, .target = target };
3885 }
3886
3768 /// Supports structs and unions.3887 /// Supports structs and unions.
3769 /// For packed structs, it returns the byte offset of the containing integer.3888 /// For packed structs, it returns the byte offset of the containing integer.
3770 pub fn structFieldOffset(ty: Type, index: usize, target: Target) u64 {3889 pub fn structFieldOffset(ty: Type, index: usize, target: Target) u64 {
...@@ -3774,71 +3893,34 @@ pub const Type = extern union {...@@ -3774,71 +3893,34 @@ pub const Type = extern union {
3774 assert(struct_obj.haveLayout());3893 assert(struct_obj.haveLayout());
3775 const is_packed = struct_obj.layout == .Packed;3894 const is_packed = struct_obj.layout == .Packed;
3776 if (!is_packed) {3895 if (!is_packed) {
3777 var offset: u64 = 0;3896 var it = ty.iterateStructOffsets(target);
3778 var big_align: u32 = 0;3897 while (it.next()) |field_offset| {
3779 for (struct_obj.fields.values()) |field, i| {3898 if (index == field_offset.field)
3780 if (!field.ty.hasCodeGenBits()) {3899 return field_offset.offset;
3781 if (i == index) return offset;
3782 continue;
3783 }
3784
3785 const field_align = field.normalAlignment(target);
3786 big_align = @maximum(big_align, field_align);
3787 offset = std.mem.alignForwardGeneric(u64, offset, field_align);
3788 if (i == index) return offset;
3789 offset += field.ty.abiSize(target);
3790 }
3791 offset = std.mem.alignForwardGeneric(u64, offset, big_align);
3792 return offset;
3793 }
3794
3795 comptime assert(Type.packed_struct_layout_version == 1);
3796 var offset: u64 = 0;
3797 var big_align: u32 = 0;
3798 var running_bits: u16 = 0;
3799 for (struct_obj.fields.values()) |field, i| {
3800 if (!field.ty.hasCodeGenBits()) {
3801 if (i == index) return offset;
3802 continue;
3803 }3900 }
38043901
3805 const field_align = field.packedAlignment();3902 return std.mem.alignForwardGeneric(u64, it.offset, it.big_align);
3806 if (field_align == 0) {3903 }
3807 if (i == index) return offset;
3808 running_bits += @intCast(u16, field.ty.bitSize(target));
3809 } else {
3810 big_align = @maximum(big_align, field_align);
38113904
3812 if (running_bits != 0) {3905 var it = ty.iteratePackedStructOffsets(target);
3813 var int_payload: Payload.Bits = .{3906 while (it.next()) |field_offset| {
3814 .base = .{ .tag = .int_unsigned },3907 if (index == field_offset.field)
3815 .data = running_bits,3908 return field_offset.offset;
3816 };
3817 const int_ty: Type = .{ .ptr_otherwise = &int_payload.base };
3818 const int_align = int_ty.abiAlignment(target);
3819 big_align = @maximum(big_align, int_align);
3820 offset = std.mem.alignForwardGeneric(u64, offset, int_align);
3821 offset += int_ty.abiSize(target);
3822 running_bits = 0;
3823 }
3824 offset = std.mem.alignForwardGeneric(u64, offset, field_align);
3825 if (i == index) return offset;
3826 offset += field.ty.abiSize(target);
3827 }
3828 }3909 }
3829 if (running_bits != 0) {3910
3911 if (it.running_bits != 0) {
3830 var int_payload: Payload.Bits = .{3912 var int_payload: Payload.Bits = .{
3831 .base = .{ .tag = .int_unsigned },3913 .base = .{ .tag = .int_unsigned },
3832 .data = running_bits,3914 .data = it.running_bits,
3833 };3915 };
3834 const int_ty: Type = .{ .ptr_otherwise = &int_payload.base };3916 const int_ty: Type = .{ .ptr_otherwise = &int_payload.base };
3835 const int_align = int_ty.abiAlignment(target);3917 const int_align = int_ty.abiAlignment(target);
3836 big_align = @maximum(big_align, int_align);3918 it.big_align = @maximum(it.big_align, int_align);
3837 offset = std.mem.alignForwardGeneric(u64, offset, int_align);3919 it.offset = std.mem.alignForwardGeneric(u64, it.offset, int_align);
3838 offset += int_ty.abiSize(target);3920 it.offset += int_ty.abiSize(target);
3839 }3921 }
3840 offset = std.mem.alignForwardGeneric(u64, offset, big_align);3922 it.offset = std.mem.alignForwardGeneric(u64, it.offset, it.big_align);
3841 return offset;3923 return it.offset;
3842 },3924 },
3843 .@"union" => return 0,3925 .@"union" => return 0,
3844 .union_tagged => {3926 .union_tagged => {
test/behavior.zig+2-2
...@@ -80,6 +80,7 @@ test {...@@ -80,6 +80,7 @@ test {
80 _ = @import("behavior/bugs/1310.zig");80 _ = @import("behavior/bugs/1310.zig");
81 _ = @import("behavior/bugs/1381.zig");81 _ = @import("behavior/bugs/1381.zig");
82 _ = @import("behavior/bugs/1500.zig");82 _ = @import("behavior/bugs/1500.zig");
83 _ = @import("behavior/bugs/1735.zig");
83 _ = @import("behavior/bugs/1741.zig");84 _ = @import("behavior/bugs/1741.zig");
84 _ = @import("behavior/bugs/2006.zig");85 _ = @import("behavior/bugs/2006.zig");
85 _ = @import("behavior/bugs/2578.zig");86 _ = @import("behavior/bugs/2578.zig");
...@@ -96,6 +97,7 @@ test {...@@ -96,6 +97,7 @@ test {
96 _ = @import("behavior/generics_llvm.zig");97 _ = @import("behavior/generics_llvm.zig");
97 _ = @import("behavior/math.zig");98 _ = @import("behavior/math.zig");
98 _ = @import("behavior/maximum_minimum.zig");99 _ = @import("behavior/maximum_minimum.zig");
100 _ = @import("behavior/merge_error_sets.zig");
99 _ = @import("behavior/namespace_depends_on_compile_var.zig");101 _ = @import("behavior/namespace_depends_on_compile_var.zig");
100 _ = @import("behavior/null_llvm.zig");102 _ = @import("behavior/null_llvm.zig");
101 _ = @import("behavior/optional_llvm.zig");103 _ = @import("behavior/optional_llvm.zig");
...@@ -135,7 +137,6 @@ test {...@@ -135,7 +137,6 @@ test {
135 _ = @import("behavior/bugs/1421.zig");137 _ = @import("behavior/bugs/1421.zig");
136 _ = @import("behavior/bugs/1442.zig");138 _ = @import("behavior/bugs/1442.zig");
137 _ = @import("behavior/bugs/1607.zig");139 _ = @import("behavior/bugs/1607.zig");
138 _ = @import("behavior/bugs/1735.zig");
139 _ = @import("behavior/bugs/1851.zig");140 _ = @import("behavior/bugs/1851.zig");
140 _ = @import("behavior/bugs/1914.zig");141 _ = @import("behavior/bugs/1914.zig");
141 _ = @import("behavior/bugs/2114.zig");142 _ = @import("behavior/bugs/2114.zig");
...@@ -169,7 +170,6 @@ test {...@@ -169,7 +170,6 @@ test {
169 _ = @import("behavior/if_stage1.zig");170 _ = @import("behavior/if_stage1.zig");
170 _ = @import("behavior/ir_block_deps.zig");171 _ = @import("behavior/ir_block_deps.zig");
171 _ = @import("behavior/math_stage1.zig");172 _ = @import("behavior/math_stage1.zig");
172 _ = @import("behavior/merge_error_sets.zig");
173 _ = @import("behavior/misc.zig");173 _ = @import("behavior/misc.zig");
174 _ = @import("behavior/muladd.zig");174 _ = @import("behavior/muladd.zig");
175 _ = @import("behavior/null_stage1.zig");175 _ = @import("behavior/null_stage1.zig");
test/behavior/sizeof_and_typeof.zig+113
...@@ -47,3 +47,116 @@ fn fn1(alpha: bool) void {...@@ -47,3 +47,116 @@ fn fn1(alpha: bool) void {
47test "lazy @sizeOf result is checked for definedness" {47test "lazy @sizeOf result is checked for definedness" {
48 _ = fn1;48 _ = fn1;
49}49}
50
51const A = struct {
52 a: u8,
53 b: u32,
54 c: u8,
55 d: u3,
56 e: u5,
57 f: u16,
58 g: u16,
59 h: u9,
60 i: u7,
61};
62
63const P = packed struct {
64 a: u8,
65 b: u32,
66 c: u8,
67 d: u3,
68 e: u5,
69 f: u16,
70 g: u16,
71 h: u9,
72 i: u7,
73};
74
75test "@offsetOf" {
76
77 // Packed structs have fixed memory layout
78 try expect(@offsetOf(P, "a") == 0);
79 try expect(@offsetOf(P, "b") == 1);
80 try expect(@offsetOf(P, "c") == 5);
81 try expect(@offsetOf(P, "d") == 6);
82 try expect(@offsetOf(P, "e") == 6);
83 try expect(@offsetOf(P, "f") == 7);
84 try expect(@offsetOf(P, "g") == 9);
85 try expect(@offsetOf(P, "h") == 11);
86 try expect(@offsetOf(P, "i") == 12);
87
88 // // Normal struct fields can be moved/padded
89 var a: A = undefined;
90 try expect(@ptrToInt(&a.a) - @ptrToInt(&a) == @offsetOf(A, "a"));
91 try expect(@ptrToInt(&a.b) - @ptrToInt(&a) == @offsetOf(A, "b"));
92 try expect(@ptrToInt(&a.c) - @ptrToInt(&a) == @offsetOf(A, "c"));
93 try expect(@ptrToInt(&a.d) - @ptrToInt(&a) == @offsetOf(A, "d"));
94 try expect(@ptrToInt(&a.e) - @ptrToInt(&a) == @offsetOf(A, "e"));
95 try expect(@ptrToInt(&a.f) - @ptrToInt(&a) == @offsetOf(A, "f"));
96 try expect(@ptrToInt(&a.g) - @ptrToInt(&a) == @offsetOf(A, "g"));
97 try expect(@ptrToInt(&a.h) - @ptrToInt(&a) == @offsetOf(A, "h"));
98 try expect(@ptrToInt(&a.i) - @ptrToInt(&a) == @offsetOf(A, "i"));
99}
100
101test "@offsetOf packed struct, array length not power of 2 or multiple of native pointer width in bytes" {
102 const p3a_len = 3;
103 const P3 = packed struct {
104 a: [p3a_len]u8,
105 b: usize,
106 };
107 try std.testing.expect(0 == @offsetOf(P3, "a"));
108 try std.testing.expect(p3a_len == @offsetOf(P3, "b"));
109
110 const p5a_len = 5;
111 const P5 = packed struct {
112 a: [p5a_len]u8,
113 b: usize,
114 };
115 try std.testing.expect(0 == @offsetOf(P5, "a"));
116 try std.testing.expect(p5a_len == @offsetOf(P5, "b"));
117
118 const p6a_len = 6;
119 const P6 = packed struct {
120 a: [p6a_len]u8,
121 b: usize,
122 };
123 try std.testing.expect(0 == @offsetOf(P6, "a"));
124 try std.testing.expect(p6a_len == @offsetOf(P6, "b"));
125
126 const p7a_len = 7;
127 const P7 = packed struct {
128 a: [p7a_len]u8,
129 b: usize,
130 };
131 try std.testing.expect(0 == @offsetOf(P7, "a"));
132 try std.testing.expect(p7a_len == @offsetOf(P7, "b"));
133
134 const p9a_len = 9;
135 const P9 = packed struct {
136 a: [p9a_len]u8,
137 b: usize,
138 };
139 try std.testing.expect(0 == @offsetOf(P9, "a"));
140 try std.testing.expect(p9a_len == @offsetOf(P9, "b"));
141
142 // 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 25 etc. are further cases
143}
144
145test "@bitOffsetOf" {
146 // Packed structs have fixed memory layout
147 try expect(@bitOffsetOf(P, "a") == 0);
148 try expect(@bitOffsetOf(P, "b") == 8);
149 try expect(@bitOffsetOf(P, "c") == 40);
150 try expect(@bitOffsetOf(P, "d") == 48);
151 try expect(@bitOffsetOf(P, "e") == 51);
152 try expect(@bitOffsetOf(P, "f") == 56);
153 try expect(@bitOffsetOf(P, "g") == 72);
154
155 try expect(@offsetOf(A, "a") * 8 == @bitOffsetOf(A, "a"));
156 try expect(@offsetOf(A, "b") * 8 == @bitOffsetOf(A, "b"));
157 try expect(@offsetOf(A, "c") * 8 == @bitOffsetOf(A, "c"));
158 try expect(@offsetOf(A, "d") * 8 == @bitOffsetOf(A, "d"));
159 try expect(@offsetOf(A, "e") * 8 == @bitOffsetOf(A, "e"));
160 try expect(@offsetOf(A, "f") * 8 == @bitOffsetOf(A, "f"));
161 try expect(@offsetOf(A, "g") * 8 == @bitOffsetOf(A, "g"));
162}
test/behavior/sizeof_and_typeof_stage1.zig-112
...@@ -2,118 +2,6 @@ const std = @import("std");...@@ -2,118 +2,6 @@ const std = @import("std");
2const expect = std.testing.expect;2const expect = std.testing.expect;
3const expectEqual = std.testing.expectEqual;3const expectEqual = std.testing.expectEqual;
44
5const A = struct {
6 a: u8,
7 b: u32,
8 c: u8,
9 d: u3,
10 e: u5,
11 f: u16,
12 g: u16,
13 h: u9,
14 i: u7,
15};
16
17const P = packed struct {
18 a: u8,
19 b: u32,
20 c: u8,
21 d: u3,
22 e: u5,
23 f: u16,
24 g: u16,
25 h: u9,
26 i: u7,
27};
28
29test "@offsetOf" {
30 // Packed structs have fixed memory layout
31 try expect(@offsetOf(P, "a") == 0);
32 try expect(@offsetOf(P, "b") == 1);
33 try expect(@offsetOf(P, "c") == 5);
34 try expect(@offsetOf(P, "d") == 6);
35 try expect(@offsetOf(P, "e") == 6);
36 try expect(@offsetOf(P, "f") == 7);
37 try expect(@offsetOf(P, "g") == 9);
38 try expect(@offsetOf(P, "h") == 11);
39 try expect(@offsetOf(P, "i") == 12);
40
41 // Normal struct fields can be moved/padded
42 var a: A = undefined;
43 try expect(@ptrToInt(&a.a) - @ptrToInt(&a) == @offsetOf(A, "a"));
44 try expect(@ptrToInt(&a.b) - @ptrToInt(&a) == @offsetOf(A, "b"));
45 try expect(@ptrToInt(&a.c) - @ptrToInt(&a) == @offsetOf(A, "c"));
46 try expect(@ptrToInt(&a.d) - @ptrToInt(&a) == @offsetOf(A, "d"));
47 try expect(@ptrToInt(&a.e) - @ptrToInt(&a) == @offsetOf(A, "e"));
48 try expect(@ptrToInt(&a.f) - @ptrToInt(&a) == @offsetOf(A, "f"));
49 try expect(@ptrToInt(&a.g) - @ptrToInt(&a) == @offsetOf(A, "g"));
50 try expect(@ptrToInt(&a.h) - @ptrToInt(&a) == @offsetOf(A, "h"));
51 try expect(@ptrToInt(&a.i) - @ptrToInt(&a) == @offsetOf(A, "i"));
52}
53
54test "@offsetOf packed struct, array length not power of 2 or multiple of native pointer width in bytes" {
55 const p3a_len = 3;
56 const P3 = packed struct {
57 a: [p3a_len]u8,
58 b: usize,
59 };
60 try std.testing.expectEqual(0, @offsetOf(P3, "a"));
61 try std.testing.expectEqual(p3a_len, @offsetOf(P3, "b"));
62
63 const p5a_len = 5;
64 const P5 = packed struct {
65 a: [p5a_len]u8,
66 b: usize,
67 };
68 try std.testing.expectEqual(0, @offsetOf(P5, "a"));
69 try std.testing.expectEqual(p5a_len, @offsetOf(P5, "b"));
70
71 const p6a_len = 6;
72 const P6 = packed struct {
73 a: [p6a_len]u8,
74 b: usize,
75 };
76 try std.testing.expectEqual(0, @offsetOf(P6, "a"));
77 try std.testing.expectEqual(p6a_len, @offsetOf(P6, "b"));
78
79 const p7a_len = 7;
80 const P7 = packed struct {
81 a: [p7a_len]u8,
82 b: usize,
83 };
84 try std.testing.expectEqual(0, @offsetOf(P7, "a"));
85 try std.testing.expectEqual(p7a_len, @offsetOf(P7, "b"));
86
87 const p9a_len = 9;
88 const P9 = packed struct {
89 a: [p9a_len]u8,
90 b: usize,
91 };
92 try std.testing.expectEqual(0, @offsetOf(P9, "a"));
93 try std.testing.expectEqual(p9a_len, @offsetOf(P9, "b"));
94
95 // 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 25 etc. are further cases
96}
97
98test "@bitOffsetOf" {
99 // Packed structs have fixed memory layout
100 try expect(@bitOffsetOf(P, "a") == 0);
101 try expect(@bitOffsetOf(P, "b") == 8);
102 try expect(@bitOffsetOf(P, "c") == 40);
103 try expect(@bitOffsetOf(P, "d") == 48);
104 try expect(@bitOffsetOf(P, "e") == 51);
105 try expect(@bitOffsetOf(P, "f") == 56);
106 try expect(@bitOffsetOf(P, "g") == 72);
107
108 try expect(@offsetOf(A, "a") * 8 == @bitOffsetOf(A, "a"));
109 try expect(@offsetOf(A, "b") * 8 == @bitOffsetOf(A, "b"));
110 try expect(@offsetOf(A, "c") * 8 == @bitOffsetOf(A, "c"));
111 try expect(@offsetOf(A, "d") * 8 == @bitOffsetOf(A, "d"));
112 try expect(@offsetOf(A, "e") * 8 == @bitOffsetOf(A, "e"));
113 try expect(@offsetOf(A, "f") * 8 == @bitOffsetOf(A, "f"));
114 try expect(@offsetOf(A, "g") * 8 == @bitOffsetOf(A, "g"));
115}
116
117test "@sizeOf(T) == 0 doesn't force resolving struct size" {5test "@sizeOf(T) == 0 doesn't force resolving struct size" {
118 const S = struct {6 const S = struct {
119 const Foo = struct {7 const Foo = struct {