authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2023-09-21 02:00:52+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-09-21 14:48:41-07:00
log1b672e41c528b0aa225cbe07c61203d04b2d9034
tree080cac4fbde495c9b216fac1cd67e84799740059
parentcd242b7440e11d9997c33296b3974dfb1fbd5d95

InternPool,Sema,type,llvm: alignment fixes

This changeset fixes the handling of alignment in several places. The new rules are: * `@alignOf(T)` where `T` is a runtime zero-bit type is at least 1, maybe greater. * Zero-bit fields in `extern` structs *do* force alignment, potentially offsetting following fields. * Zero-bit fields *do* have addresses within structs which can be observed and are consistent with `@offsetOf`. These are not necessarily all implemented correctly yet (see disabled test), but this commit fixes all regressions compared to master, and makes one new test pass.

7 files changed, 127 insertions(+), 44 deletions(-)

src/Sema.zig+13-6
...@@ -34325,6 +34325,7 @@ pub fn resolveStructAlignment(...@@ -34325,6 +34325,7 @@ pub fn resolveStructAlignment(
34325 struct_type.flagsPtr(ip).alignment = result;34325 struct_type.flagsPtr(ip).alignment = result;
34326 return result;34326 return result;
34327 }34327 }
34328 defer struct_type.clearAlignmentWip(ip);
3432834329
34329 var result: Alignment = .@"1";34330 var result: Alignment = .@"1";
3433034331
...@@ -34337,7 +34338,7 @@ pub fn resolveStructAlignment(...@@ -34337,7 +34338,7 @@ pub fn resolveStructAlignment(
34337 field_ty,34338 field_ty,
34338 struct_type.layout,34339 struct_type.layout,
34339 );34340 );
34340 result = result.max(field_align);34341 result = result.maxStrict(field_align);
34341 }34342 }
3434234343
34343 struct_type.flagsPtr(ip).alignment = result;34344 struct_type.flagsPtr(ip).alignment = result;
...@@ -34373,6 +34374,8 @@ fn resolveStructLayout(sema: *Sema, ty: Type) CompileError!void {...@@ -34373,6 +34374,8 @@ fn resolveStructLayout(sema: *Sema, ty: Type) CompileError!void {
34373 const aligns = try sema.arena.alloc(Alignment, struct_type.field_types.len);34374 const aligns = try sema.arena.alloc(Alignment, struct_type.field_types.len);
34374 const sizes = try sema.arena.alloc(u64, struct_type.field_types.len);34375 const sizes = try sema.arena.alloc(u64, struct_type.field_types.len);
3437534376
34377 var big_align: Alignment = .@"1";
34378
34376 for (aligns, sizes, 0..) |*field_align, *field_size, i| {34379 for (aligns, sizes, 0..) |*field_align, *field_size, i| {
34377 const field_ty = struct_type.field_types.get(ip)[i].toType();34380 const field_ty = struct_type.field_types.get(ip)[i].toType();
34378 if (struct_type.fieldIsComptime(ip, i) or try sema.typeRequiresComptime(field_ty)) {34381 if (struct_type.fieldIsComptime(ip, i) or try sema.typeRequiresComptime(field_ty)) {
...@@ -34381,6 +34384,7 @@ fn resolveStructLayout(sema: *Sema, ty: Type) CompileError!void {...@@ -34381,6 +34384,7 @@ fn resolveStructLayout(sema: *Sema, ty: Type) CompileError!void {
34381 field_align.* = .none;34384 field_align.* = .none;
34382 continue;34385 continue;
34383 }34386 }
34387
34384 field_size.* = sema.typeAbiSize(field_ty) catch |err| switch (err) {34388 field_size.* = sema.typeAbiSize(field_ty) catch |err| switch (err) {
34385 error.AnalysisFail => {34389 error.AnalysisFail => {
34386 const msg = sema.err orelse return err;34390 const msg = sema.err orelse return err;
...@@ -34394,6 +34398,7 @@ fn resolveStructLayout(sema: *Sema, ty: Type) CompileError!void {...@@ -34394,6 +34398,7 @@ fn resolveStructLayout(sema: *Sema, ty: Type) CompileError!void {
34394 field_ty,34398 field_ty,
34395 struct_type.layout,34399 struct_type.layout,
34396 );34400 );
34401 big_align = big_align.maxStrict(field_align.*);
34397 }34402 }
3439834403
34399 if (struct_type.flagsPtr(ip).assumed_runtime_bits and !(try sema.typeHasRuntimeBits(ty))) {34404 if (struct_type.flagsPtr(ip).assumed_runtime_bits and !(try sema.typeHasRuntimeBits(ty))) {
...@@ -34409,8 +34414,13 @@ fn resolveStructLayout(sema: *Sema, ty: Type) CompileError!void {...@@ -34409,8 +34414,13 @@ fn resolveStructLayout(sema: *Sema, ty: Type) CompileError!void {
34409 if (struct_type.hasReorderedFields()) {34414 if (struct_type.hasReorderedFields()) {
34410 const runtime_order = struct_type.runtime_order.get(ip);34415 const runtime_order = struct_type.runtime_order.get(ip);
3441134416
34412 for (sizes, runtime_order, 0..) |size, *ro, i| {34417 for (runtime_order, 0..) |*ro, i| {
34413 ro.* = if (size != 0) @enumFromInt(i) else .omitted;34418 const field_ty = struct_type.field_types.get(ip)[i].toType();
34419 if (struct_type.fieldIsComptime(ip, i) or try sema.typeRequiresComptime(field_ty)) {
34420 ro.* = .omitted;
34421 } else {
34422 ro.* = @enumFromInt(i);
34423 }
34414 }34424 }
3441534425
34416 const RuntimeOrder = InternPool.Key.StructType.RuntimeOrder;34426 const RuntimeOrder = InternPool.Key.StructType.RuntimeOrder;
...@@ -34454,10 +34464,7 @@ fn resolveStructLayout(sema: *Sema, ty: Type) CompileError!void {...@@ -34454,10 +34464,7 @@ fn resolveStructLayout(sema: *Sema, ty: Type) CompileError!void {
34454 const offsets = struct_type.offsets.get(ip);34464 const offsets = struct_type.offsets.get(ip);
34455 var it = struct_type.iterateRuntimeOrder(ip);34465 var it = struct_type.iterateRuntimeOrder(ip);
34456 var offset: u64 = 0;34466 var offset: u64 = 0;
34457 var big_align: Alignment = .@"1";
34458 while (it.next()) |i| {34467 while (it.next()) |i| {
34459 if (aligns[i] == .none) continue;
34460 big_align = big_align.maxStrict(aligns[i]);
34461 offsets[i] = @intCast(aligns[i].forward(offset));34468 offsets[i] = @intCast(aligns[i].forward(offset));
34462 offset = offsets[i] + sizes[i];34469 offset = offsets[i] + sizes[i];
34463 }34470 }
src/codegen/llvm.zig+41-8
...@@ -833,7 +833,10 @@ pub const Object = struct {...@@ -833,7 +833,10 @@ pub const Object = struct {
833833
834 /// When an LLVM struct type is created, an entry is inserted into this834 /// When an LLVM struct type is created, an entry is inserted into this
835 /// table for every zig source field of the struct that has a corresponding835 /// table for every zig source field of the struct that has a corresponding
836 /// LLVM struct field. comptime fields and 0 bit fields are not included.836 /// LLVM struct field. comptime fields are not included. Zero-bit fields are
837 /// mapped to a field at the correct byte, which may be a padding field, or
838 /// are not mapped, in which case they are sematically at the end of the
839 /// struct.
837 /// The value is the LLVM struct field index.840 /// The value is the LLVM struct field index.
838 /// This is denormalized data.841 /// This is denormalized data.
839 struct_field_map: std.AutoHashMapUnmanaged(ZigStructField, c_uint),842 struct_field_map: std.AutoHashMapUnmanaged(ZigStructField, c_uint),
...@@ -2500,7 +2503,6 @@ pub const Object = struct {...@@ -2500,7 +2503,6 @@ pub const Object = struct {
2500 try di_fields.ensureUnusedCapacity(gpa, field_types.len);2503 try di_fields.ensureUnusedCapacity(gpa, field_types.len);
25012504
2502 comptime assert(struct_layout_version == 2);2505 comptime assert(struct_layout_version == 2);
2503 var offset: u64 = 0;
2504 var it = struct_type.iterateRuntimeOrder(ip);2506 var it = struct_type.iterateRuntimeOrder(ip);
2505 while (it.next()) |field_index| {2507 while (it.next()) |field_index| {
2506 const field_ty = field_types[field_index].toType();2508 const field_ty = field_types[field_index].toType();
...@@ -2511,8 +2513,7 @@ pub const Object = struct {...@@ -2511,8 +2513,7 @@ pub const Object = struct {
2511 field_ty,2513 field_ty,
2512 struct_type.layout,2514 struct_type.layout,
2513 );2515 );
2514 const field_offset = field_align.forward(offset);2516 const field_offset = ty.structFieldOffset(field_index, mod);
2515 offset = field_offset + field_size;
25162517
2517 const field_name = struct_type.fieldName(ip, field_index).unwrap() orelse2518 const field_name = struct_type.fieldName(ip, field_index).unwrap() orelse
2518 try ip.getOrPutStringFmt(gpa, "{d}", .{field_index});2519 try ip.getOrPutStringFmt(gpa, "{d}", .{field_index});
...@@ -3304,10 +3305,10 @@ pub const Object = struct {...@@ -3304,10 +3305,10 @@ pub const Object = struct {
3304 var offset: u64 = 0;3305 var offset: u64 = 0;
3305 var big_align: InternPool.Alignment = .@"1";3306 var big_align: InternPool.Alignment = .@"1";
3306 var struct_kind: Builder.Type.Structure.Kind = .normal;3307 var struct_kind: Builder.Type.Structure.Kind = .normal;
3308 // When we encounter a zero-bit field, we place it here so we know to map it to the next non-zero-bit field (if any).
3307 var it = struct_type.iterateRuntimeOrder(ip);3309 var it = struct_type.iterateRuntimeOrder(ip);
3308 while (it.next()) |field_index| {3310 while (it.next()) |field_index| {
3309 const field_ty = struct_type.field_types.get(ip)[field_index].toType();3311 const field_ty = struct_type.field_types.get(ip)[field_index].toType();
3310 if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue;
3311 const field_align = mod.structFieldAlignment(3312 const field_align = mod.structFieldAlignment(
3312 struct_type.fieldAlign(ip, field_index),3313 struct_type.fieldAlign(ip, field_index),
3313 field_ty,3314 field_ty,
...@@ -3324,6 +3325,20 @@ pub const Object = struct {...@@ -3324,6 +3325,20 @@ pub const Object = struct {
3324 o.gpa,3325 o.gpa,
3325 try o.builder.arrayType(padding_len, .i8),3326 try o.builder.arrayType(padding_len, .i8),
3326 );3327 );
3328
3329 if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) {
3330 // This is a zero-bit field. If there are runtime bits after this field,
3331 // map to the next LLVM field (which we know exists): otherwise, don't
3332 // map the field, indicating it's at the end of the struct.
3333 if (offset != struct_type.size(ip).*) {
3334 try o.struct_field_map.put(o.gpa, .{
3335 .struct_ty = t.toIntern(),
3336 .field_index = field_index,
3337 }, @intCast(llvm_field_types.items.len));
3338 }
3339 continue;
3340 }
3341
3327 try o.struct_field_map.put(o.gpa, .{3342 try o.struct_field_map.put(o.gpa, .{
3328 .struct_ty = t.toIntern(),3343 .struct_ty = t.toIntern(),
3329 .field_index = field_index,3344 .field_index = field_index,
...@@ -3360,12 +3375,14 @@ pub const Object = struct {...@@ -3360,12 +3375,14 @@ pub const Object = struct {
3360 var offset: u64 = 0;3375 var offset: u64 = 0;
3361 var big_align: InternPool.Alignment = .none;3376 var big_align: InternPool.Alignment = .none;
33623377
3378 const struct_size = t.abiSize(mod);
3379
3363 for (3380 for (
3364 anon_struct_type.types.get(ip),3381 anon_struct_type.types.get(ip),
3365 anon_struct_type.values.get(ip),3382 anon_struct_type.values.get(ip),
3366 0..,3383 0..,
3367 ) |field_ty, field_val, field_index| {3384 ) |field_ty, field_val, field_index| {
3368 if (field_val != .none or !field_ty.toType().hasRuntimeBits(mod)) continue;3385 if (field_val != .none) continue;
33693386
3370 const field_align = field_ty.toType().abiAlignment(mod);3387 const field_align = field_ty.toType().abiAlignment(mod);
3371 big_align = big_align.max(field_align);3388 big_align = big_align.max(field_align);
...@@ -3377,6 +3394,18 @@ pub const Object = struct {...@@ -3377,6 +3394,18 @@ pub const Object = struct {
3377 o.gpa,3394 o.gpa,
3378 try o.builder.arrayType(padding_len, .i8),3395 try o.builder.arrayType(padding_len, .i8),
3379 );3396 );
3397 if (!field_ty.toType().hasRuntimeBitsIgnoreComptime(mod)) {
3398 // This is a zero-bit field. If there are runtime bits after this field,
3399 // map to the next LLVM field (which we know exists): otherwise, don't
3400 // map the field, indicating it's at the end of the struct.
3401 if (offset != struct_size) {
3402 try o.struct_field_map.put(o.gpa, .{
3403 .struct_ty = t.toIntern(),
3404 .field_index = @intCast(field_index),
3405 }, @intCast(llvm_field_types.items.len));
3406 }
3407 continue;
3408 }
3380 try o.struct_field_map.put(o.gpa, .{3409 try o.struct_field_map.put(o.gpa, .{
3381 .struct_ty = t.toIntern(),3410 .struct_ty = t.toIntern(),
3382 .field_index = @intCast(field_index),3411 .field_index = @intCast(field_index),
...@@ -4019,7 +4048,6 @@ pub const Object = struct {...@@ -4019,7 +4048,6 @@ pub const Object = struct {
4019 var field_it = struct_type.iterateRuntimeOrder(ip);4048 var field_it = struct_type.iterateRuntimeOrder(ip);
4020 while (field_it.next()) |field_index| {4049 while (field_it.next()) |field_index| {
4021 const field_ty = struct_type.field_types.get(ip)[field_index].toType();4050 const field_ty = struct_type.field_types.get(ip)[field_index].toType();
4022 if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue;
4023 const field_align = mod.structFieldAlignment(4051 const field_align = mod.structFieldAlignment(
4024 struct_type.fieldAlign(ip, field_index),4052 struct_type.fieldAlign(ip, field_index),
4025 field_ty,4053 field_ty,
...@@ -4040,6 +4068,11 @@ pub const Object = struct {...@@ -4040,6 +4068,11 @@ pub const Object = struct {
4040 llvm_index += 1;4068 llvm_index += 1;
4041 }4069 }
40424070
4071 if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) {
4072 // This is a zero-bit field - we only needed it for the alignment.
4073 continue;
4074 }
4075
4043 vals[llvm_index] = try o.lowerValue(4076 vals[llvm_index] = try o.lowerValue(
4044 (try val.fieldValue(mod, field_index)).toIntern(),4077 (try val.fieldValue(mod, field_index)).toIntern(),
4045 );4078 );
...@@ -6122,7 +6155,7 @@ pub const FuncGen = struct {...@@ -6122,7 +6155,7 @@ pub const FuncGen = struct {
6122 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;6155 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
6123 const ptr_ty = self.typeOf(bin_op.lhs);6156 const ptr_ty = self.typeOf(bin_op.lhs);
6124 const elem_ty = ptr_ty.childType(mod);6157 const elem_ty = ptr_ty.childType(mod);
6125 if (!elem_ty.hasRuntimeBitsIgnoreComptime(mod)) return (try o.lowerPtrToVoid(ptr_ty)).toValue();6158 if (!elem_ty.hasRuntimeBitsIgnoreComptime(mod)) return self.resolveInst(bin_op.lhs);
61266159
6127 const base_ptr = try self.resolveInst(bin_op.lhs);6160 const base_ptr = try self.resolveInst(bin_op.lhs);
6128 const rhs = try self.resolveInst(bin_op.rhs);6161 const rhs = try self.resolveInst(bin_op.rhs);
src/type.zig+8-10
...@@ -833,7 +833,7 @@ pub const Type = struct {...@@ -833,7 +833,7 @@ pub const Type = struct {
833 };833 };
834 }834 }
835835
836 /// Returns `none` for 0-bit types.836 /// Never returns `none`. Asserts that all necessary type resolution is already done.
837 pub fn abiAlignment(ty: Type, mod: *Module) Alignment {837 pub fn abiAlignment(ty: Type, mod: *Module) Alignment {
838 return (ty.abiAlignmentAdvanced(mod, .eager) catch unreachable).scalar;838 return (ty.abiAlignmentAdvanced(mod, .eager) catch unreachable).scalar;
839 }839 }
...@@ -878,10 +878,10 @@ pub const Type = struct {...@@ -878,10 +878,10 @@ pub const Type = struct {
878 };878 };
879879
880 switch (ty.toIntern()) {880 switch (ty.toIntern()) {
881 .empty_struct_type => return AbiAlignmentAdvanced{ .scalar = .none },881 .empty_struct_type => return AbiAlignmentAdvanced{ .scalar = .@"1" },
882 else => switch (ip.indexToKey(ty.toIntern())) {882 else => switch (ip.indexToKey(ty.toIntern())) {
883 .int_type => |int_type| {883 .int_type => |int_type| {
884 if (int_type.bits == 0) return AbiAlignmentAdvanced{ .scalar = .none };884 if (int_type.bits == 0) return AbiAlignmentAdvanced{ .scalar = .@"1" };
885 return .{ .scalar = intAbiAlignment(int_type.bits, target) };885 return .{ .scalar = intAbiAlignment(int_type.bits, target) };
886 },886 },
887 .ptr_type, .anyframe_type => {887 .ptr_type, .anyframe_type => {
...@@ -929,6 +929,7 @@ pub const Type = struct {...@@ -929,6 +929,7 @@ pub const Type = struct {
929 .isize,929 .isize,
930 .export_options,930 .export_options,
931 .extern_options,931 .extern_options,
932 .type_info,
932 => return .{933 => return .{
933 .scalar = Alignment.fromByteUnits(@divExact(target.ptrBitWidth(), 8)),934 .scalar = Alignment.fromByteUnits(@divExact(target.ptrBitWidth(), 8)),
934 },935 },
...@@ -974,8 +975,7 @@ pub const Type = struct {...@@ -974,8 +975,7 @@ pub const Type = struct {
974 .null,975 .null,
975 .undefined,976 .undefined,
976 .enum_literal,977 .enum_literal,
977 .type_info,978 => return .{ .scalar = .@"1" },
978 => return .{ .scalar = .none },
979979
980 .noreturn => unreachable,980 .noreturn => unreachable,
981 .generic_poison => unreachable,981 .generic_poison => unreachable,
...@@ -1010,11 +1010,9 @@ pub const Type = struct {...@@ -1010,11 +1010,9 @@ pub const Type = struct {
1010 };1010 };
1011 },1011 },
1012 .anon_struct_type => |tuple| {1012 .anon_struct_type => |tuple| {
1013 var big_align: Alignment = .none;1013 var big_align: Alignment = .@"1";
1014 for (tuple.types.get(ip), tuple.values.get(ip)) |field_ty, val| {1014 for (tuple.types.get(ip), tuple.values.get(ip)) |field_ty, val| {
1015 if (val != .none) continue; // comptime field1015 if (val != .none) continue; // comptime field
1016 if (!(field_ty.toType().hasRuntimeBits(mod))) continue;
1017
1018 switch (try field_ty.toType().abiAlignmentAdvanced(mod, strat)) {1016 switch (try field_ty.toType().abiAlignmentAdvanced(mod, strat)) {
1019 .scalar => |field_align| big_align = big_align.max(field_align),1017 .scalar => |field_align| big_align = big_align.max(field_align),
1020 .val => switch (strat) {1018 .val => switch (strat) {
...@@ -1059,7 +1057,7 @@ pub const Type = struct {...@@ -1059,7 +1057,7 @@ pub const Type = struct {
1059 }1057 }
1060 }1058 }
10611059
1062 var max_align: Alignment = .none;1060 var max_align: Alignment = .@"1";
1063 if (union_obj.hasTag(ip)) max_align = union_obj.enum_tag_ty.toType().abiAlignment(mod);1061 if (union_obj.hasTag(ip)) max_align = union_obj.enum_tag_ty.toType().abiAlignment(mod);
1064 for (0..union_obj.field_names.len) |field_index| {1062 for (0..union_obj.field_names.len) |field_index| {
1065 const field_ty = union_obj.field_types.get(ip)[field_index].toType();1063 const field_ty = union_obj.field_types.get(ip)[field_index].toType();
...@@ -1172,7 +1170,7 @@ pub const Type = struct {...@@ -1172,7 +1170,7 @@ pub const Type = struct {
1172 .scalar = Alignment.fromByteUnits(@divExact(target.ptrBitWidth(), 8)),1170 .scalar = Alignment.fromByteUnits(@divExact(target.ptrBitWidth(), 8)),
1173 },1171 },
1174 .ErrorSet => return abiAlignmentAdvanced(Type.anyerror, mod, strat),1172 .ErrorSet => return abiAlignmentAdvanced(Type.anyerror, mod, strat),
1175 .NoReturn => return .{ .scalar = .none },1173 .NoReturn => return .{ .scalar = .@"1" },
1176 else => {},1174 else => {},
1177 }1175 }
11781176
src/value.zig+2-1
...@@ -1265,7 +1265,8 @@ pub const Value = struct {...@@ -1265,7 +1265,8 @@ pub const Value = struct {
1265 .int => |int| switch (int.storage) {1265 .int => |int| switch (int.storage) {
1266 .big_int => |big_int| big_int.orderAgainstScalar(0),1266 .big_int => |big_int| big_int.orderAgainstScalar(0),
1267 inline .u64, .i64 => |x| std.math.order(x, 0),1267 inline .u64, .i64 => |x| std.math.order(x, 0),
1268 .lazy_align, .lazy_size => |ty| return if (ty.toType().hasRuntimeBitsAdvanced(1268 .lazy_align => .gt, // alignment is never 0
1269 .lazy_size => |ty| return if (ty.toType().hasRuntimeBitsAdvanced(
1269 mod,1270 mod,
1270 false,1271 false,
1271 if (opt_sema) |sema| .{ .sema = sema } else .eager,1272 if (opt_sema) |sema| .{ .sema = sema } else .eager,
test/behavior/align.zig+55
...@@ -619,3 +619,58 @@ test "sub-aligned pointer field access" {...@@ -619,3 +619,58 @@ test "sub-aligned pointer field access" {
619 .Little => try expect(x == 0x09080706),619 .Little => try expect(x == 0x09080706),
620 }620 }
621}621}
622
623test "alignment of zero-bit types is respected" {
624 if (true) return error.SkipZigTest; // TODO
625
626 const S = struct { arr: [0]usize = .{} };
627
628 comptime assert(@alignOf(void) == 1);
629 comptime assert(@alignOf(u0) == 1);
630 comptime assert(@alignOf([0]usize) == @alignOf(usize));
631 comptime assert(@alignOf(S) == @alignOf(usize));
632
633 var s: S = .{};
634 var v32: void align(32) = {};
635 var x32: u0 align(32) = 0;
636 var s32: S align(32) = .{};
637
638 var zero: usize = 0;
639
640 try expect(@intFromPtr(&s) % @alignOf(usize) == 0);
641 try expect(@intFromPtr(&s.arr) % @alignOf(usize) == 0);
642 try expect(@intFromPtr(s.arr[zero..zero].ptr) % @alignOf(usize) == 0);
643 try expect(@intFromPtr(&v32) % 32 == 0);
644 try expect(@intFromPtr(&x32) % 32 == 0);
645 try expect(@intFromPtr(&s32) % 32 == 0);
646 try expect(@intFromPtr(&s32.arr) % 32 == 0);
647 try expect(@intFromPtr(s32.arr[zero..zero].ptr) % 32 == 0);
648}
649
650test "zero-bit fields in extern struct pad fields appropriately" {
651 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
652 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
653 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
654 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
655 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
656
657 const S = extern struct {
658 x: u8,
659 a: [0]u16 = .{},
660 y: u8,
661 };
662
663 // `a` should give `S` alignment 2, and pad the `arr` field.
664 comptime assert(@alignOf(S) == 2);
665 comptime assert(@sizeOf(S) == 4);
666 comptime assert(@offsetOf(S, "x") == 0);
667 comptime assert(@offsetOf(S, "a") == 2);
668 comptime assert(@offsetOf(S, "y") == 2);
669
670 var s: S = .{ .x = 100, .y = 200 };
671
672 try expect(@intFromPtr(&s) % 2 == 0);
673 try expect(@intFromPtr(&s.y) - @intFromPtr(&s.x) == 2);
674 try expect(@intFromPtr(&s.y) == @intFromPtr(&s.a));
675 try expect(@fieldParentPtr(S, "a", &s.a) == &s);
676}
test/behavior/alignof.zig+7-18
...@@ -18,24 +18,13 @@ test "@alignOf(T) before referencing T" {...@@ -18,24 +18,13 @@ test "@alignOf(T) before referencing T" {
18}18}
1919
20test "comparison of @alignOf(T) against zero" {20test "comparison of @alignOf(T) against zero" {
21 {21 const T = struct { x: u32 };
22 const T = struct { x: u32 };22 try expect(!(@alignOf(T) == 0));
23 try expect(!(@alignOf(T) == 0));23 try expect(@alignOf(T) != 0);
24 try expect(@alignOf(T) != 0);24 try expect(!(@alignOf(T) < 0));
25 try expect(!(@alignOf(T) < 0));25 try expect(!(@alignOf(T) <= 0));
26 try expect(!(@alignOf(T) <= 0));26 try expect(@alignOf(T) > 0);
27 try expect(@alignOf(T) > 0);27 try expect(@alignOf(T) >= 0);
28 try expect(@alignOf(T) >= 0);
29 }
30 {
31 const T = struct {};
32 try expect(@alignOf(T) == 0);
33 try expect(!(@alignOf(T) != 0));
34 try expect(!(@alignOf(T) < 0));
35 try expect(@alignOf(T) <= 0);
36 try expect(!(@alignOf(T) > 0));
37 try expect(@alignOf(T) >= 0);
38 }
39}28}
4029
41test "correct alignment for elements and slices of aligned array" {30test "correct alignment for elements and slices of aligned array" {
test/behavior/empty_union.zig+1-1
...@@ -37,7 +37,7 @@ test "switch on empty tagged union" {...@@ -37,7 +37,7 @@ test "switch on empty tagged union" {
37test "empty union" {37test "empty union" {
38 const U = union {};38 const U = union {};
39 try expect(@sizeOf(U) == 0);39 try expect(@sizeOf(U) == 0);
40 try expect(@alignOf(U) == 0);40 try expect(@alignOf(U) == 1);
41}41}
4242
43test "empty extern union" {43test "empty extern union" {