| author | |
| committer | |
| log | 5c3393dc2efc836b0ec23e3c2c5ad3da4f8d6a59 |
| tree | 6cce7dc2cb662d5b85d75d77cca12a84f46fa63c |
| parent | 77338947617f5aed0bfe50ca4975d284736a5d90 |
| parent | 405705cb76914072b9a91ac29f7cf0bf67b255f4 |
| signature |
codegen: fix field offsets in packed structs7 files changed, 398 insertions(+), 71 deletions(-)
src/arch/wasm/CodeGen.zig+13-5| ... | @@ -3090,12 +3090,19 @@ fn lowerParentPtr(func: *CodeGen, ptr_val: Value, offset: u32) InnerError!WValue | ... | @@ -3090,12 +3090,19 @@ fn lowerParentPtr(func: *CodeGen, ptr_val: Value, offset: u32) InnerError!WValue |
| 3090 | return func.lowerParentPtr(elem.base.toValue(), @as(u32, @intCast(elem_offset + offset))); | 3090 | return func.lowerParentPtr(elem.base.toValue(), @as(u32, @intCast(elem_offset + offset))); |
| 3091 | }, | 3091 | }, |
| 3092 | .field => |field| { | 3092 | .field => |field| { |
| 3093 | const parent_ty = mod.intern_pool.typeOf(field.base).toType().childType(mod); | 3093 | const parent_ptr_ty = mod.intern_pool.typeOf(field.base).toType(); |
| 3094 | const parent_ty = parent_ptr_ty.childType(mod); | ||
| 3095 | const field_index: u32 = @intCast(field.index); | ||
| 3094 | 3096 | ||
| 3095 | const field_offset = switch (parent_ty.zigTypeTag(mod)) { | 3097 | const field_offset = switch (parent_ty.zigTypeTag(mod)) { |
| 3096 | .Struct => switch (parent_ty.containerLayout(mod)) { | 3098 | .Struct => blk: { |
| 3097 | .Packed => parent_ty.packedStructFieldByteOffset(@as(usize, @intCast(field.index)), mod), | 3099 | if (mod.typeToPackedStruct(parent_ty)) |struct_type| { |
| 3098 | else => parent_ty.structFieldOffset(@as(usize, @intCast(field.index)), mod), | 3100 | if (ptr.ty.toType().ptrInfo(mod).packed_offset.host_size == 0) |
| 3101 | break :blk @divExact(mod.structPackedFieldBitOffset(struct_type, field_index) + parent_ptr_ty.ptrInfo(mod).packed_offset.bit_offset, 8) | ||
| 3102 | else | ||
| 3103 | break :blk 0; | ||
| 3104 | } | ||
| 3105 | break :blk parent_ty.structFieldOffset(field_index, mod); | ||
| 3099 | }, | 3106 | }, |
| 3100 | .Union => switch (parent_ty.containerLayout(mod)) { | 3107 | .Union => switch (parent_ty.containerLayout(mod)) { |
| 3101 | .Packed => 0, | 3108 | .Packed => 0, |
| ... | @@ -3821,7 +3828,8 @@ fn structFieldPtr( | ... | @@ -3821,7 +3828,8 @@ fn structFieldPtr( |
| 3821 | if (result_ty.ptrInfo(mod).packed_offset.host_size != 0) { | 3828 | if (result_ty.ptrInfo(mod).packed_offset.host_size != 0) { |
| 3822 | break :offset @as(u32, 0); | 3829 | break :offset @as(u32, 0); |
| 3823 | } | 3830 | } |
| 3824 | break :offset struct_ty.packedStructFieldByteOffset(index, mod) + @divExact(struct_ptr_ty_info.packed_offset.bit_offset, 8); | 3831 | const struct_type = mod.typeToStruct(struct_ty).?; |
| 3832 | break :offset @divExact(mod.structPackedFieldBitOffset(struct_type, index) + struct_ptr_ty_info.packed_offset.bit_offset, 8); | ||
| 3825 | }, | 3833 | }, |
| 3826 | .Union => 0, | 3834 | .Union => 0, |
| 3827 | else => unreachable, | 3835 | else => unreachable, |
src/arch/x86_64/CodeGen.zig+9-8| ... | @@ -5774,14 +5774,15 @@ fn fieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, index: u32 | ... | @@ -5774,14 +5774,15 @@ fn fieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, index: u32 |
| 5774 | const ptr_container_ty_info = ptr_container_ty.ptrInfo(mod); | 5774 | const ptr_container_ty_info = ptr_container_ty.ptrInfo(mod); |
| 5775 | const container_ty = ptr_container_ty.childType(mod); | 5775 | const container_ty = ptr_container_ty.childType(mod); |
| 5776 | 5776 | ||
| 5777 | const field_offset: i32 = @intCast(switch (container_ty.containerLayout(mod)) { | 5777 | const field_offset: i32 = blk: { |
| 5778 | .Auto, .Extern => container_ty.structFieldOffset(index, mod), | 5778 | if (mod.typeToPackedStruct(container_ty)) |struct_type| { |
| 5779 | .Packed => if (container_ty.zigTypeTag(mod) == .Struct and | 5779 | break :blk if (ptr_field_ty.ptrInfo(mod).packed_offset.host_size == 0) |
| 5780 | ptr_field_ty.ptrInfo(mod).packed_offset.host_size == 0) | 5780 | @divExact(mod.structPackedFieldBitOffset(struct_type, index) + ptr_container_ty_info.packed_offset.bit_offset, 8) |
| 5781 | container_ty.packedStructFieldByteOffset(index, mod) + @divExact(ptr_container_ty_info.packed_offset.bit_offset, 8) | 5781 | else |
| 5782 | else | 5782 | 0; |
| 5783 | 0, | 5783 | } |
| 5784 | }); | 5784 | break :blk @intCast(container_ty.structFieldOffset(index, mod)); |
| 5785 | }; | ||
| 5785 | 5786 | ||
| 5786 | const src_mcv = try self.resolveInst(operand); | 5787 | const src_mcv = try self.resolveInst(operand); |
| 5787 | const dst_mcv = if (switch (src_mcv) { | 5788 | const dst_mcv = if (switch (src_mcv) { |
src/codegen/c.zig+12-8| ... | @@ -5269,22 +5269,26 @@ fn fieldLocation( | ... | @@ -5269,22 +5269,26 @@ fn fieldLocation( |
| 5269 | const ip = &mod.intern_pool; | 5269 | const ip = &mod.intern_pool; |
| 5270 | const container_ty = container_ptr_ty.childType(mod); | 5270 | const container_ty = container_ptr_ty.childType(mod); |
| 5271 | return switch (container_ty.zigTypeTag(mod)) { | 5271 | return switch (container_ty.zigTypeTag(mod)) { |
| 5272 | .Struct => switch (container_ty.containerLayout(mod)) { | 5272 | .Struct => blk: { |
| 5273 | .Auto, .Extern => for (field_index..container_ty.structFieldCount(mod)) |next_field_index_usize| { | 5273 | if (mod.typeToPackedStruct(container_ty)) |struct_type| { |
| 5274 | if (field_ptr_ty.ptrInfo(mod).packed_offset.host_size == 0) | ||
| 5275 | break :blk .{ .byte_offset = @divExact(mod.structPackedFieldBitOffset(struct_type, field_index) + container_ptr_ty.ptrInfo(mod).packed_offset.bit_offset, 8) } | ||
| 5276 | else | ||
| 5277 | break :blk .begin; | ||
| 5278 | } | ||
| 5279 | |||
| 5280 | for (field_index..container_ty.structFieldCount(mod)) |next_field_index_usize| { | ||
| 5274 | const next_field_index: u32 = @intCast(next_field_index_usize); | 5281 | const next_field_index: u32 = @intCast(next_field_index_usize); |
| 5275 | if (container_ty.structFieldIsComptime(next_field_index, mod)) continue; | 5282 | if (container_ty.structFieldIsComptime(next_field_index, mod)) continue; |
| 5276 | const field_ty = container_ty.structFieldType(next_field_index, mod); | 5283 | const field_ty = container_ty.structFieldType(next_field_index, mod); |
| 5277 | if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue; | 5284 | if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue; |
| 5278 | 5285 | ||
| 5279 | break .{ .field = if (container_ty.isSimpleTuple(mod)) | 5286 | break :blk .{ .field = if (container_ty.isSimpleTuple(mod)) |
| 5280 | .{ .field = next_field_index } | 5287 | .{ .field = next_field_index } |
| 5281 | else | 5288 | else |
| 5282 | .{ .identifier = ip.stringToSlice(container_ty.legacyStructFieldName(next_field_index, mod)) } }; | 5289 | .{ .identifier = ip.stringToSlice(container_ty.legacyStructFieldName(next_field_index, mod)) } }; |
| 5283 | } else if (container_ty.hasRuntimeBitsIgnoreComptime(mod)) .end else .begin, | 5290 | } |
| 5284 | .Packed => if (field_ptr_ty.ptrInfo(mod).packed_offset.host_size == 0) | 5291 | break :blk if (container_ty.hasRuntimeBitsIgnoreComptime(mod)) .end else .begin; |
| 5285 | .{ .byte_offset = container_ty.packedStructFieldByteOffset(field_index, mod) + @divExact(container_ptr_ty.ptrInfo(mod).packed_offset.bit_offset, 8) } | ||
| 5286 | else | ||
| 5287 | .begin, | ||
| 5288 | }, | 5292 | }, |
| 5289 | .Union => { | 5293 | .Union => { |
| 5290 | const union_obj = mod.typeToUnion(container_ty).?; | 5294 | const union_obj = mod.typeToUnion(container_ty).?; |
src/codegen/llvm.zig+19-26| ... | @@ -4,7 +4,6 @@ const assert = std.debug.assert; | ... | @@ -4,7 +4,6 @@ const assert = std.debug.assert; |
| 4 | const Allocator = std.mem.Allocator; | 4 | const Allocator = std.mem.Allocator; |
| 5 | const log = std.log.scoped(.codegen); | 5 | const log = std.log.scoped(.codegen); |
| 6 | const math = std.math; | 6 | const math = std.math; |
| 7 | const native_endian = builtin.cpu.arch.endian(); | ||
| 8 | const DW = std.dwarf; | 7 | const DW = std.dwarf; |
| 9 | 8 | ||
| 10 | const Builder = @import("llvm/Builder.zig"); | 9 | const Builder = @import("llvm/Builder.zig"); |
| ... | @@ -3770,7 +3769,7 @@ pub const Object = struct { | ... | @@ -3770,7 +3769,7 @@ pub const Object = struct { |
| 3770 | .opt_payload, | 3769 | .opt_payload, |
| 3771 | .elem, | 3770 | .elem, |
| 3772 | .field, | 3771 | .field, |
| 3773 | => try o.lowerParentPtr(val, ty.ptrInfo(mod).packed_offset.bit_offset % 8 == 0), | 3772 | => try o.lowerParentPtr(val), |
| 3774 | .comptime_field => unreachable, | 3773 | .comptime_field => unreachable, |
| 3775 | }; | 3774 | }; |
| 3776 | switch (ptr.len) { | 3775 | switch (ptr.len) { |
| ... | @@ -4230,15 +4229,16 @@ pub const Object = struct { | ... | @@ -4230,15 +4229,16 @@ pub const Object = struct { |
| 4230 | return o.lowerDeclRefValue(ptr_ty, decl_index); | 4229 | return o.lowerDeclRefValue(ptr_ty, decl_index); |
| 4231 | } | 4230 | } |
| 4232 | 4231 | ||
| 4233 | fn lowerParentPtr(o: *Object, ptr_val: Value, byte_aligned: bool) Allocator.Error!Builder.Constant { | 4232 | fn lowerParentPtr(o: *Object, ptr_val: Value) Allocator.Error!Builder.Constant { |
| 4234 | const mod = o.module; | 4233 | const mod = o.module; |
| 4235 | const ip = &mod.intern_pool; | 4234 | const ip = &mod.intern_pool; |
| 4236 | return switch (ip.indexToKey(ptr_val.toIntern()).ptr.addr) { | 4235 | const ptr = ip.indexToKey(ptr_val.toIntern()).ptr; |
| 4236 | return switch (ptr.addr) { | ||
| 4237 | .decl => |decl| o.lowerParentPtrDecl(decl), | 4237 | .decl => |decl| o.lowerParentPtrDecl(decl), |
| 4238 | .mut_decl => |mut_decl| o.lowerParentPtrDecl(mut_decl.decl), | 4238 | .mut_decl => |mut_decl| o.lowerParentPtrDecl(mut_decl.decl), |
| 4239 | .int => |int| try o.lowerIntAsPtr(int), | 4239 | .int => |int| try o.lowerIntAsPtr(int), |
| 4240 | .eu_payload => |eu_ptr| { | 4240 | .eu_payload => |eu_ptr| { |
| 4241 | const parent_ptr = try o.lowerParentPtr(eu_ptr.toValue(), true); | 4241 | const parent_ptr = try o.lowerParentPtr(eu_ptr.toValue()); |
| 4242 | 4242 | ||
| 4243 | const eu_ty = ip.typeOf(eu_ptr).toType().childType(mod); | 4243 | const eu_ty = ip.typeOf(eu_ptr).toType().childType(mod); |
| 4244 | const payload_ty = eu_ty.errorUnionPayload(mod); | 4244 | const payload_ty = eu_ty.errorUnionPayload(mod); |
| ... | @@ -4256,7 +4256,7 @@ pub const Object = struct { | ... | @@ -4256,7 +4256,7 @@ pub const Object = struct { |
| 4256 | }); | 4256 | }); |
| 4257 | }, | 4257 | }, |
| 4258 | .opt_payload => |opt_ptr| { | 4258 | .opt_payload => |opt_ptr| { |
| 4259 | const parent_ptr = try o.lowerParentPtr(opt_ptr.toValue(), true); | 4259 | const parent_ptr = try o.lowerParentPtr(opt_ptr.toValue()); |
| 4260 | 4260 | ||
| 4261 | const opt_ty = ip.typeOf(opt_ptr).toType().childType(mod); | 4261 | const opt_ty = ip.typeOf(opt_ptr).toType().childType(mod); |
| 4262 | const payload_ty = opt_ty.optionalChild(mod); | 4262 | const payload_ty = opt_ty.optionalChild(mod); |
| ... | @@ -4274,7 +4274,7 @@ pub const Object = struct { | ... | @@ -4274,7 +4274,7 @@ pub const Object = struct { |
| 4274 | }, | 4274 | }, |
| 4275 | .comptime_field => unreachable, | 4275 | .comptime_field => unreachable, |
| 4276 | .elem => |elem_ptr| { | 4276 | .elem => |elem_ptr| { |
| 4277 | const parent_ptr = try o.lowerParentPtr(elem_ptr.base.toValue(), true); | 4277 | const parent_ptr = try o.lowerParentPtr(elem_ptr.base.toValue()); |
| 4278 | const elem_ty = ip.typeOf(elem_ptr.base).toType().elemType2(mod); | 4278 | const elem_ty = ip.typeOf(elem_ptr.base).toType().elemType2(mod); |
| 4279 | 4279 | ||
| 4280 | return o.builder.gepConst(.inbounds, try o.lowerType(elem_ty), parent_ptr, null, &.{ | 4280 | return o.builder.gepConst(.inbounds, try o.lowerType(elem_ty), parent_ptr, null, &.{ |
| ... | @@ -4282,9 +4282,9 @@ pub const Object = struct { | ... | @@ -4282,9 +4282,9 @@ pub const Object = struct { |
| 4282 | }); | 4282 | }); |
| 4283 | }, | 4283 | }, |
| 4284 | .field => |field_ptr| { | 4284 | .field => |field_ptr| { |
| 4285 | const parent_ptr = try o.lowerParentPtr(field_ptr.base.toValue(), byte_aligned); | 4285 | const parent_ptr = try o.lowerParentPtr(field_ptr.base.toValue()); |
| 4286 | const parent_ty = ip.typeOf(field_ptr.base).toType().childType(mod); | 4286 | const parent_ptr_ty = ip.typeOf(field_ptr.base).toType(); |
| 4287 | 4287 | const parent_ty = parent_ptr_ty.childType(mod); | |
| 4288 | const field_index: u32 = @intCast(field_ptr.index); | 4288 | const field_index: u32 = @intCast(field_ptr.index); |
| 4289 | switch (parent_ty.zigTypeTag(mod)) { | 4289 | switch (parent_ty.zigTypeTag(mod)) { |
| 4290 | .Union => { | 4290 | .Union => { |
| ... | @@ -4309,22 +4309,14 @@ pub const Object = struct { | ... | @@ -4309,22 +4309,14 @@ pub const Object = struct { |
| 4309 | }, | 4309 | }, |
| 4310 | .Struct => { | 4310 | .Struct => { |
| 4311 | if (mod.typeToPackedStruct(parent_ty)) |struct_type| { | 4311 | if (mod.typeToPackedStruct(parent_ty)) |struct_type| { |
| 4312 | if (!byte_aligned) return parent_ptr; | 4312 | const ptr_info = ptr.ty.toType().ptrInfo(mod); |
| 4313 | if (ptr_info.packed_offset.host_size != 0) return parent_ptr; | ||
| 4314 | |||
| 4315 | const parent_ptr_info = parent_ptr_ty.ptrInfo(mod); | ||
| 4316 | const bit_offset = mod.structPackedFieldBitOffset(struct_type, field_index) + parent_ptr_info.packed_offset.bit_offset; | ||
| 4313 | const llvm_usize = try o.lowerType(Type.usize); | 4317 | const llvm_usize = try o.lowerType(Type.usize); |
| 4314 | const base_addr = | 4318 | const base_addr = try o.builder.castConst(.ptrtoint, parent_ptr, llvm_usize); |
| 4315 | try o.builder.castConst(.ptrtoint, parent_ptr, llvm_usize); | 4319 | const byte_offset = try o.builder.intConst(llvm_usize, @divExact(bit_offset, 8)); |
| 4316 | // count bits of fields before this one | ||
| 4317 | // TODO https://github.com/ziglang/zig/issues/17178 | ||
| 4318 | const prev_bits = b: { | ||
| 4319 | var b: usize = 0; | ||
| 4320 | for (0..field_index) |i| { | ||
| 4321 | const field_ty = struct_type.field_types.get(ip)[i].toType(); | ||
| 4322 | if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue; | ||
| 4323 | b += @intCast(field_ty.bitSize(mod)); | ||
| 4324 | } | ||
| 4325 | break :b b; | ||
| 4326 | }; | ||
| 4327 | const byte_offset = try o.builder.intConst(llvm_usize, prev_bits / 8); | ||
| 4328 | const field_addr = try o.builder.binConst(.add, base_addr, byte_offset); | 4320 | const field_addr = try o.builder.binConst(.add, base_addr, byte_offset); |
| 4329 | return o.builder.castConst(.inttoptr, field_addr, .ptr); | 4321 | return o.builder.castConst(.inttoptr, field_addr, .ptr); |
| 4330 | } | 4322 | } |
| ... | @@ -10148,6 +10140,7 @@ pub const FuncGen = struct { | ... | @@ -10148,6 +10140,7 @@ pub const FuncGen = struct { |
| 10148 | const result_ty = self.typeOfIndex(inst); | 10140 | const result_ty = self.typeOfIndex(inst); |
| 10149 | const result_ty_info = result_ty.ptrInfo(mod); | 10141 | const result_ty_info = result_ty.ptrInfo(mod); |
| 10150 | const struct_ptr_ty_info = struct_ptr_ty.ptrInfo(mod); | 10142 | const struct_ptr_ty_info = struct_ptr_ty.ptrInfo(mod); |
| 10143 | const struct_type = mod.typeToStruct(struct_ty).?; | ||
| 10151 | 10144 | ||
| 10152 | if (result_ty_info.packed_offset.host_size != 0) { | 10145 | if (result_ty_info.packed_offset.host_size != 0) { |
| 10153 | // From LLVM's perspective, a pointer to a packed struct and a pointer | 10146 | // From LLVM's perspective, a pointer to a packed struct and a pointer |
| ... | @@ -10159,7 +10152,7 @@ pub const FuncGen = struct { | ... | @@ -10159,7 +10152,7 @@ pub const FuncGen = struct { |
| 10159 | 10152 | ||
| 10160 | // We have a pointer to a packed struct field that happens to be byte-aligned. | 10153 | // We have a pointer to a packed struct field that happens to be byte-aligned. |
| 10161 | // Offset our operand pointer by the correct number of bytes. | 10154 | // Offset our operand pointer by the correct number of bytes. |
| 10162 | const byte_offset = struct_ty.packedStructFieldByteOffset(field_index, mod) + @divExact(struct_ptr_ty_info.packed_offset.bit_offset, 8); | 10155 | const byte_offset = @divExact(mod.structPackedFieldBitOffset(struct_type, field_index) + struct_ptr_ty_info.packed_offset.bit_offset, 8); |
| 10163 | if (byte_offset == 0) return struct_ptr; | 10156 | if (byte_offset == 0) return struct_ptr; |
| 10164 | const usize_ty = try o.lowerType(Type.usize); | 10157 | const usize_ty = try o.lowerType(Type.usize); |
| 10165 | const llvm_index = try o.builder.intValue(usize_ty, byte_offset); | 10158 | const llvm_index = try o.builder.intValue(usize_ty, byte_offset); |
src/type.zig-20| ... | @@ -3028,26 +3028,6 @@ pub const Type = struct { | ... | @@ -3028,26 +3028,6 @@ pub const Type = struct { |
| 3028 | }; | 3028 | }; |
| 3029 | } | 3029 | } |
| 3030 | 3030 | ||
| 3031 | pub fn packedStructFieldBitOffset(ty: Type, field_index: usize, mod: *Module) u32 { | ||
| 3032 | const ip = &mod.intern_pool; | ||
| 3033 | const struct_type = ip.indexToKey(ty.toIntern()).struct_type; | ||
| 3034 | assert(struct_type.layout == .Packed); | ||
| 3035 | comptime assert(Type.packed_struct_layout_version == 2); | ||
| 3036 | |||
| 3037 | var running_bits: u32 = 0; | ||
| 3038 | for (struct_type.field_types.get(ip), 0..) |field_ty, i| { | ||
| 3039 | if (i == field_index) break; | ||
| 3040 | if (!field_ty.toType().hasRuntimeBits(mod)) continue; | ||
| 3041 | const field_bits: u32 = @intCast(field_ty.toType().bitSize(mod)); | ||
| 3042 | running_bits += field_bits; | ||
| 3043 | } | ||
| 3044 | return running_bits; | ||
| 3045 | } | ||
| 3046 | |||
| 3047 | pub fn packedStructFieldByteOffset(ty: Type, field_index: usize, mod: *Module) u32 { | ||
| 3048 | return packedStructFieldBitOffset(ty, field_index, mod) / 8; | ||
| 3049 | } | ||
| 3050 | |||
| 3051 | pub const FieldOffset = struct { | 3031 | pub const FieldOffset = struct { |
| 3052 | field: usize, | 3032 | field: usize, |
| 3053 | offset: u64, | 3033 | offset: u64, |
test/behavior/packed-struct.zig+298-4| ... | @@ -253,6 +253,79 @@ test "regular in irregular packed struct" { | ... | @@ -253,6 +253,79 @@ test "regular in irregular packed struct" { |
| 253 | try expectEqual(@as(u8, 42), foo.bar.b); | 253 | try expectEqual(@as(u8, 42), foo.bar.b); |
| 254 | } | 254 | } |
| 255 | 255 | ||
| 256 | test "nested packed struct unaligned" { | ||
| 257 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | ||
| 258 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | ||
| 259 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | ||
| 260 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | ||
| 261 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 262 | if (native_endian != .Little) return error.SkipZigTest; // Byte aligned packed struct field pointers have not been implemented yet | ||
| 263 | |||
| 264 | const S1 = packed struct { | ||
| 265 | a: u4, | ||
| 266 | b: u4, | ||
| 267 | c: u8, | ||
| 268 | }; | ||
| 269 | const S2 = packed struct { | ||
| 270 | base: u8, | ||
| 271 | p0: S1, | ||
| 272 | bit0: u1, | ||
| 273 | p1: packed struct { | ||
| 274 | a: u8, | ||
| 275 | }, | ||
| 276 | p2: packed struct { | ||
| 277 | a: u7, | ||
| 278 | b: u8, | ||
| 279 | }, | ||
| 280 | p3: S1, | ||
| 281 | |||
| 282 | var s: @This() = .{ | ||
| 283 | .base = 1, | ||
| 284 | .p0 = .{ .a = 2, .b = 3, .c = 4 }, | ||
| 285 | .bit0 = 0, | ||
| 286 | .p1 = .{ .a = 5 }, | ||
| 287 | .p2 = .{ .a = 6, .b = 7 }, | ||
| 288 | .p3 = .{ .a = 8, .b = 9, .c = 10 }, | ||
| 289 | }; | ||
| 290 | }; | ||
| 291 | |||
| 292 | try expect(S2.s.base == 1); | ||
| 293 | try expect(S2.s.p0.a == 2); | ||
| 294 | try expect(S2.s.p0.b == 3); | ||
| 295 | try expect(S2.s.p0.c == 4); | ||
| 296 | try expect(S2.s.bit0 == 0); | ||
| 297 | try expect(S2.s.p1.a == 5); | ||
| 298 | try expect(S2.s.p2.a == 6); | ||
| 299 | try expect(S2.s.p2.b == 7); | ||
| 300 | try expect(S2.s.p3.a == 8); | ||
| 301 | try expect(S2.s.p3.b == 9); | ||
| 302 | try expect(S2.s.p3.c == 10); | ||
| 303 | |||
| 304 | const S3 = packed struct { | ||
| 305 | pad: u8, | ||
| 306 | v: u2, | ||
| 307 | s: packed struct { | ||
| 308 | v: u3, | ||
| 309 | s: packed struct { | ||
| 310 | v: u2, | ||
| 311 | s: packed struct { | ||
| 312 | bit0: u1, | ||
| 313 | byte: u8, | ||
| 314 | bit1: u1, | ||
| 315 | }, | ||
| 316 | }, | ||
| 317 | }, | ||
| 318 | var v0: @This() = .{ .pad = 0, .v = 1, .s = .{ .v = 2, .s = .{ .v = 3, .s = .{ .bit0 = 0, .byte = 4, .bit1 = 1 } } } }; | ||
| 319 | }; | ||
| 320 | |||
| 321 | try expect(S3.v0.v == 1); | ||
| 322 | try expect(S3.v0.s.v == 2); | ||
| 323 | try expect(S3.v0.s.s.v == 3); | ||
| 324 | try expect(S3.v0.s.s.s.bit0 == 0); | ||
| 325 | try expect(S3.v0.s.s.s.byte == 4); | ||
| 326 | try expect(S3.v0.s.s.s.bit1 == 1); | ||
| 327 | } | ||
| 328 | |||
| 256 | test "byte-aligned field pointer offsets" { | 329 | test "byte-aligned field pointer offsets" { |
| 257 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 330 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 258 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 331 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| ... | @@ -354,6 +427,45 @@ test "byte-aligned field pointer offsets" { | ... | @@ -354,6 +427,45 @@ test "byte-aligned field pointer offsets" { |
| 354 | try comptime S.doTheTest(); | 427 | try comptime S.doTheTest(); |
| 355 | } | 428 | } |
| 356 | 429 | ||
| 430 | test "nested packed struct field pointers" { | ||
| 431 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | ||
| 432 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | ||
| 433 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | ||
| 434 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | ||
| 435 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // ubsan unaligned pointer access | ||
| 436 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 437 | if (native_endian != .Little) return error.SkipZigTest; // Byte aligned packed struct field pointers have not been implemented yet | ||
| 438 | |||
| 439 | const S2 = packed struct { | ||
| 440 | base: u8, | ||
| 441 | p0: packed struct { | ||
| 442 | a: u4, | ||
| 443 | b: u4, | ||
| 444 | c: u8, | ||
| 445 | }, | ||
| 446 | bit: u1, | ||
| 447 | p1: packed struct { | ||
| 448 | a: u7, | ||
| 449 | b: u8, | ||
| 450 | }, | ||
| 451 | |||
| 452 | var s: @This() = .{ .base = 1, .p0 = .{ .a = 2, .b = 3, .c = 4 }, .bit = 0, .p1 = .{ .a = 5, .b = 6 } }; | ||
| 453 | }; | ||
| 454 | |||
| 455 | const ptr_base = &S2.s.base; | ||
| 456 | const ptr_p0_a = &S2.s.p0.a; | ||
| 457 | const ptr_p0_b = &S2.s.p0.b; | ||
| 458 | const ptr_p0_c = &S2.s.p0.c; | ||
| 459 | const ptr_p1_a = &S2.s.p1.a; | ||
| 460 | const ptr_p1_b = &S2.s.p1.b; | ||
| 461 | try expectEqual(@as(u8, 1), ptr_base.*); | ||
| 462 | try expectEqual(@as(u4, 2), ptr_p0_a.*); | ||
| 463 | try expectEqual(@as(u4, 3), ptr_p0_b.*); | ||
| 464 | try expectEqual(@as(u8, 4), ptr_p0_c.*); | ||
| 465 | try expectEqual(@as(u7, 5), ptr_p1_a.*); | ||
| 466 | try expectEqual(@as(u8, 6), ptr_p1_b.*); | ||
| 467 | } | ||
| 468 | |||
| 357 | test "load pointer from packed struct" { | 469 | test "load pointer from packed struct" { |
| 358 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 470 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 359 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 471 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| ... | @@ -380,6 +492,7 @@ test "@intFromPtr on a packed struct field" { | ... | @@ -380,6 +492,7 @@ test "@intFromPtr on a packed struct field" { |
| 380 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 492 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 381 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 493 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 382 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | 494 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; |
| 495 | if (native_endian != .Little) return error.SkipZigTest; | ||
| 383 | 496 | ||
| 384 | const S = struct { | 497 | const S = struct { |
| 385 | const P = packed struct { | 498 | const P = packed struct { |
| ... | @@ -387,6 +500,7 @@ test "@intFromPtr on a packed struct field" { | ... | @@ -387,6 +500,7 @@ test "@intFromPtr on a packed struct field" { |
| 387 | y: u8, | 500 | y: u8, |
| 388 | z: u32, | 501 | z: u32, |
| 389 | }; | 502 | }; |
| 503 | |||
| 390 | var p0: P = P{ | 504 | var p0: P = P{ |
| 391 | .x = 1, | 505 | .x = 1, |
| 392 | .y = 2, | 506 | .y = 2, |
| ... | @@ -396,6 +510,140 @@ test "@intFromPtr on a packed struct field" { | ... | @@ -396,6 +510,140 @@ test "@intFromPtr on a packed struct field" { |
| 396 | try expect(@intFromPtr(&S.p0.z) - @intFromPtr(&S.p0.x) == 2); | 510 | try expect(@intFromPtr(&S.p0.z) - @intFromPtr(&S.p0.x) == 2); |
| 397 | } | 511 | } |
| 398 | 512 | ||
| 513 | test "@intFromPtr on a packed struct field unaligned and nested" { | ||
| 514 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | ||
| 515 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | ||
| 516 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | ||
| 517 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | ||
| 518 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 519 | if (native_endian != .Little) return error.SkipZigTest; // Byte aligned packed struct field pointers have not been implemented yet | ||
| 520 | |||
| 521 | const S1 = packed struct { | ||
| 522 | a: u4, | ||
| 523 | b: u4, | ||
| 524 | c: u8, | ||
| 525 | }; | ||
| 526 | const S2 = packed struct { | ||
| 527 | base: u8, | ||
| 528 | p0: S1, | ||
| 529 | bit0: u1, | ||
| 530 | p1: packed struct { | ||
| 531 | a: u8, | ||
| 532 | }, | ||
| 533 | p2: packed struct { | ||
| 534 | a: u7, | ||
| 535 | b: u8, | ||
| 536 | }, | ||
| 537 | p3: S1, | ||
| 538 | |||
| 539 | var s: @This() = .{ | ||
| 540 | .base = 1, | ||
| 541 | .p0 = .{ .a = 2, .b = 3, .c = 4 }, | ||
| 542 | .bit0 = 0, | ||
| 543 | .p1 = .{ .a = 5 }, | ||
| 544 | .p2 = .{ .a = 6, .b = 7 }, | ||
| 545 | .p3 = .{ .a = 8, .b = 9, .c = 10 }, | ||
| 546 | }; | ||
| 547 | }; | ||
| 548 | |||
| 549 | switch (comptime @alignOf(S2)) { | ||
| 550 | 4 => { | ||
| 551 | comptime assert(@TypeOf(&S2.s.base) == *align(4) u8); | ||
| 552 | comptime assert(@TypeOf(&S2.s.p0.a) == *align(1:0:2) u4); | ||
| 553 | comptime assert(@TypeOf(&S2.s.p0.b) == *align(1:4:2) u4); | ||
| 554 | comptime assert(@TypeOf(&S2.s.p0.c) == *u8); | ||
| 555 | comptime assert(@TypeOf(&S2.s.bit0) == *align(4:24:8) u1); | ||
| 556 | comptime assert(@TypeOf(&S2.s.p1.a) == *align(4:25:8) u8); | ||
| 557 | comptime assert(@TypeOf(&S2.s.p2.a) == *align(4:33:8) u7); | ||
| 558 | comptime assert(@TypeOf(&S2.s.p2.b) == *u8); | ||
| 559 | comptime assert(@TypeOf(&S2.s.p3.a) == *align(2:0:2) u4); | ||
| 560 | comptime assert(@TypeOf(&S2.s.p3.b) == *align(2:4:2) u4); | ||
| 561 | comptime assert(@TypeOf(&S2.s.p3.c) == *u8); | ||
| 562 | }, | ||
| 563 | 8 => { | ||
| 564 | comptime assert(@TypeOf(&S2.s.base) == *align(8) u8); | ||
| 565 | comptime assert(@TypeOf(&S2.s.p0.a) == *align(1:0:2) u4); | ||
| 566 | comptime assert(@TypeOf(&S2.s.p0.b) == *align(1:4:2) u4); | ||
| 567 | comptime assert(@TypeOf(&S2.s.p0.c) == *u8); | ||
| 568 | comptime assert(@TypeOf(&S2.s.bit0) == *align(8:24:8) u1); | ||
| 569 | comptime assert(@TypeOf(&S2.s.p1.a) == *align(8:25:8) u8); | ||
| 570 | comptime assert(@TypeOf(&S2.s.p2.a) == *align(8:33:8) u7); | ||
| 571 | comptime assert(@TypeOf(&S2.s.p2.b) == *u8); | ||
| 572 | comptime assert(@TypeOf(&S2.s.p3.a) == *align(2:0:2) u4); | ||
| 573 | comptime assert(@TypeOf(&S2.s.p3.b) == *align(2:4:2) u4); | ||
| 574 | comptime assert(@TypeOf(&S2.s.p3.c) == *u8); | ||
| 575 | }, | ||
| 576 | else => {}, | ||
| 577 | } | ||
| 578 | try expect(@intFromPtr(&S2.s.base) - @intFromPtr(&S2.s) == 0); | ||
| 579 | try expect(@intFromPtr(&S2.s.p0.a) - @intFromPtr(&S2.s) == 1); | ||
| 580 | try expect(@intFromPtr(&S2.s.p0.b) - @intFromPtr(&S2.s) == 1); | ||
| 581 | try expect(@intFromPtr(&S2.s.p0.c) - @intFromPtr(&S2.s) == 2); | ||
| 582 | try expect(@intFromPtr(&S2.s.bit0) - @intFromPtr(&S2.s) == 0); | ||
| 583 | try expect(@intFromPtr(&S2.s.p1.a) - @intFromPtr(&S2.s) == 0); | ||
| 584 | try expect(@intFromPtr(&S2.s.p2.a) - @intFromPtr(&S2.s) == 0); | ||
| 585 | try expect(@intFromPtr(&S2.s.p2.b) - @intFromPtr(&S2.s) == 5); | ||
| 586 | try expect(@intFromPtr(&S2.s.p3.a) - @intFromPtr(&S2.s) == 6); | ||
| 587 | try expect(@intFromPtr(&S2.s.p3.b) - @intFromPtr(&S2.s) == 6); | ||
| 588 | try expect(@intFromPtr(&S2.s.p3.c) - @intFromPtr(&S2.s) == 7); | ||
| 589 | |||
| 590 | const S3 = packed struct { | ||
| 591 | pad: u8, | ||
| 592 | v: u2, | ||
| 593 | s: packed struct { | ||
| 594 | v: u3, | ||
| 595 | s: packed struct { | ||
| 596 | v: u2, | ||
| 597 | s: packed struct { | ||
| 598 | bit0: u1, | ||
| 599 | byte: u8, | ||
| 600 | bit1: u1, | ||
| 601 | }, | ||
| 602 | }, | ||
| 603 | }, | ||
| 604 | var v0: @This() = .{ .pad = 0, .v = 1, .s = .{ .v = 2, .s = .{ .v = 3, .s = .{ .bit0 = 0, .byte = 4, .bit1 = 1 } } } }; | ||
| 605 | }; | ||
| 606 | |||
| 607 | comptime assert(@TypeOf(&S3.v0.v) == *align(4:8:4) u2); | ||
| 608 | comptime assert(@TypeOf(&S3.v0.s.v) == *align(4:10:4) u3); | ||
| 609 | comptime assert(@TypeOf(&S3.v0.s.s.v) == *align(4:13:4) u2); | ||
| 610 | comptime assert(@TypeOf(&S3.v0.s.s.s.bit0) == *align(4:15:4) u1); | ||
| 611 | comptime assert(@TypeOf(&S3.v0.s.s.s.byte) == *align(2) u8); | ||
| 612 | comptime assert(@TypeOf(&S3.v0.s.s.s.bit1) == *align(4:24:4) u1); | ||
| 613 | try expect(@intFromPtr(&S3.v0.v) - @intFromPtr(&S3.v0) == 0); | ||
| 614 | try expect(@intFromPtr(&S3.v0.s) - @intFromPtr(&S3.v0) == 0); | ||
| 615 | try expect(@intFromPtr(&S3.v0.s.v) - @intFromPtr(&S3.v0) == 0); | ||
| 616 | try expect(@intFromPtr(&S3.v0.s.s) - @intFromPtr(&S3.v0) == 0); | ||
| 617 | try expect(@intFromPtr(&S3.v0.s.s.v) - @intFromPtr(&S3.v0) == 0); | ||
| 618 | try expect(@intFromPtr(&S3.v0.s.s.s) - @intFromPtr(&S3.v0) == 0); | ||
| 619 | try expect(@intFromPtr(&S3.v0.s.s.s.bit0) - @intFromPtr(&S3.v0) == 0); | ||
| 620 | try expect(@intFromPtr(&S3.v0.s.s.s.byte) - @intFromPtr(&S3.v0) == 2); | ||
| 621 | try expect(@intFromPtr(&S3.v0.s.s.s.bit1) - @intFromPtr(&S3.v0) == 0); | ||
| 622 | } | ||
| 623 | |||
| 624 | test "packed struct fields modification" { | ||
| 625 | // Originally reported at https://github.com/ziglang/zig/issues/16615 | ||
| 626 | |||
| 627 | const Small = packed struct { | ||
| 628 | val: u8 = 0, | ||
| 629 | lo: u4 = 0, | ||
| 630 | hi: u4 = 0, | ||
| 631 | |||
| 632 | var p: @This() = undefined; | ||
| 633 | }; | ||
| 634 | Small.p = .{ | ||
| 635 | .val = 0x12, | ||
| 636 | .lo = 3, | ||
| 637 | .hi = 4, | ||
| 638 | }; | ||
| 639 | try expect(@as(u16, @bitCast(Small.p)) == 0x4312); | ||
| 640 | |||
| 641 | Small.p.val -= Small.p.lo; | ||
| 642 | Small.p.val += Small.p.hi; | ||
| 643 | Small.p.hi -= Small.p.lo; | ||
| 644 | try expect(@as(u16, @bitCast(Small.p)) == 0x1313); | ||
| 645 | } | ||
| 646 | |||
| 399 | test "optional pointer in packed struct" { | 647 | test "optional pointer in packed struct" { |
| 400 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 648 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 401 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 649 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| ... | @@ -410,7 +658,7 @@ test "optional pointer in packed struct" { | ... | @@ -410,7 +658,7 @@ test "optional pointer in packed struct" { |
| 410 | 658 | ||
| 411 | test "nested packed struct field access test" { | 659 | test "nested packed struct field access test" { |
| 412 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | 660 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO |
| 413 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 661 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO packed structs larger than 64 bits |
| 414 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 662 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 415 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 663 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 416 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 664 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| ... | @@ -562,7 +810,7 @@ test "nested packed struct at non-zero offset 2" { | ... | @@ -562,7 +810,7 @@ test "nested packed struct at non-zero offset 2" { |
| 562 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 810 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 563 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 811 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 564 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | 812 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; |
| 565 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 813 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO packed structs larger than 64 bits |
| 566 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | 814 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; |
| 567 | 815 | ||
| 568 | const S = struct { | 816 | const S = struct { |
| ... | @@ -700,7 +948,6 @@ test "packed struct initialized in bitcast" { | ... | @@ -700,7 +948,6 @@ test "packed struct initialized in bitcast" { |
| 700 | test "pointer to container level packed struct field" { | 948 | test "pointer to container level packed struct field" { |
| 701 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 949 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 702 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 950 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 703 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | ||
| 704 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 951 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 705 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | 952 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; |
| 706 | 953 | ||
| ... | @@ -727,7 +974,6 @@ test "store undefined to packed result location" { | ... | @@ -727,7 +974,6 @@ test "store undefined to packed result location" { |
| 727 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 974 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 728 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 975 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 729 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | 976 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; |
| 730 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | ||
| 731 | 977 | ||
| 732 | var x: u4 = 0; | 978 | var x: u4 = 0; |
| 733 | var s = packed struct { x: u4, y: u4 }{ .x = x, .y = if (x > 0) x else undefined }; | 979 | var s = packed struct { x: u4, y: u4 }{ .x = x, .y = if (x > 0) x else undefined }; |
| ... | @@ -743,3 +989,51 @@ test "bitcast back and forth" { | ... | @@ -743,3 +989,51 @@ test "bitcast back and forth" { |
| 743 | try expect(s.one == s2.one); | 989 | try expect(s.one == s2.one); |
| 744 | try expect(s.two == s2.two); | 990 | try expect(s.two == s2.two); |
| 745 | } | 991 | } |
| 992 | |||
| 993 | test "field access of packed struct smaller than its abi size inside struct initialized with rls" { | ||
| 994 | // Originally reported at https://github.com/ziglang/zig/issues/14200 | ||
| 995 | if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .arm) return error.SkipZigTest; | ||
| 996 | const S = struct { | ||
| 997 | ps: packed struct { x: i2, y: i2 }, | ||
| 998 | |||
| 999 | fn init(cond: bool) @This() { | ||
| 1000 | return .{ .ps = .{ .x = 0, .y = if (cond) 1 else 0 } }; | ||
| 1001 | } | ||
| 1002 | }; | ||
| 1003 | |||
| 1004 | var s = S.init(true); | ||
| 1005 | // note: this bug is triggered by the == operator, expectEqual will hide it | ||
| 1006 | try expect(@as(i2, 0) == s.ps.x); | ||
| 1007 | try expect(@as(i2, 1) == s.ps.y); | ||
| 1008 | } | ||
| 1009 | |||
| 1010 | test "modify nested packed struct aligned field" { | ||
| 1011 | // Originally reported at https://github.com/ziglang/zig/issues/14632 | ||
| 1012 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | ||
| 1013 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | ||
| 1014 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; | ||
| 1015 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | ||
| 1016 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 1017 | |||
| 1018 | const Options = packed struct { | ||
| 1019 | foo: bool = false, | ||
| 1020 | bar: bool = false, | ||
| 1021 | pretty_print: packed struct { | ||
| 1022 | enabled: bool = false, | ||
| 1023 | num_spaces: u4 = 4, | ||
| 1024 | space_char: enum { space, tab } = .space, | ||
| 1025 | indent: u8 = 0, | ||
| 1026 | } = .{}, | ||
| 1027 | baz: bool = false, | ||
| 1028 | }; | ||
| 1029 | |||
| 1030 | var opts = Options{}; | ||
| 1031 | opts.pretty_print.indent += 1; | ||
| 1032 | try std.testing.expectEqual(@as(u17, 0b00000000100100000), @bitCast(opts)); | ||
| 1033 | try std.testing.expect(!opts.foo); | ||
| 1034 | try std.testing.expect(!opts.bar); | ||
| 1035 | try std.testing.expect(!opts.pretty_print.enabled); | ||
| 1036 | try std.testing.expectEqual(@as(u4, 4), opts.pretty_print.num_spaces); | ||
| 1037 | try std.testing.expectEqual(@as(u8, 1), opts.pretty_print.indent); | ||
| 1038 | try std.testing.expect(!opts.baz); | ||
| 1039 | } |
test/behavior/packed-union.zig+47| ... | @@ -38,3 +38,50 @@ test "flags in packed union" { | ... | @@ -38,3 +38,50 @@ test "flags in packed union" { |
| 38 | try expectEqual(true, test_bits.enable_1); | 38 | try expectEqual(true, test_bits.enable_1); |
| 39 | try expectEqual(false, test_bits.other_flags.flags.enable_1); | 39 | try expectEqual(false, test_bits.other_flags.flags.enable_1); |
| 40 | } | 40 | } |
| 41 | |||
| 42 | test "flags in packed union at offset" { | ||
| 43 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | ||
| 44 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | ||
| 45 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; | ||
| 46 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | ||
| 47 | |||
| 48 | const FlagBits = packed union { | ||
| 49 | base_flags: packed union { | ||
| 50 | flags: packed struct(u4) { | ||
| 51 | enable_1: bool = true, | ||
| 52 | enable_2: bool = false, | ||
| 53 | enable_3: bool = false, | ||
| 54 | enable_4: bool = false, | ||
| 55 | }, | ||
| 56 | bits: u4, | ||
| 57 | }, | ||
| 58 | adv_flags: packed struct(u12) { | ||
| 59 | pad: u8 = 0, | ||
| 60 | adv: packed union { | ||
| 61 | flags: packed struct(u4) { | ||
| 62 | enable_1: bool = true, | ||
| 63 | enable_2: bool = false, | ||
| 64 | enable_3: bool = false, | ||
| 65 | enable_4: bool = false, | ||
| 66 | }, | ||
| 67 | bits: u4, | ||
| 68 | }, | ||
| 69 | }, | ||
| 70 | }; | ||
| 71 | var test_bits: FlagBits = .{ .adv_flags = .{ .adv = .{ .flags = .{} } } }; | ||
| 72 | |||
| 73 | try expectEqual(@as(u8, 0), test_bits.adv_flags.pad); | ||
| 74 | try expectEqual(true, test_bits.adv_flags.adv.flags.enable_1); | ||
| 75 | try expectEqual(false, test_bits.adv_flags.adv.flags.enable_2); | ||
| 76 | |||
| 77 | test_bits.adv_flags.adv.flags.enable_1 = false; | ||
| 78 | test_bits.adv_flags.adv.flags.enable_2 = true; | ||
| 79 | try expectEqual(@as(u8, 0), test_bits.adv_flags.pad); | ||
| 80 | try expectEqual(false, test_bits.adv_flags.adv.flags.enable_1); | ||
| 81 | try expectEqual(true, test_bits.adv_flags.adv.flags.enable_2); | ||
| 82 | |||
| 83 | test_bits.adv_flags.adv.bits = 12; | ||
| 84 | try expectEqual(@as(u8, 0), test_bits.adv_flags.pad); | ||
| 85 | try expectEqual(false, test_bits.adv_flags.adv.flags.enable_1); | ||
| 86 | try expectEqual(false, test_bits.adv_flags.adv.flags.enable_2); | ||
| 87 | } |