| author | |
| committer | |
| log | 1b672e41c528b0aa225cbe07c61203d04b2d9034 |
| tree | 080cac4fbde495c9b216fac1cd67e84799740059 |
| parent | cd242b7440e11d9997c33296b3974dfb1fbd5d95 |
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); | ||
| 34328 | 34329 | ||
| 34329 | var result: Alignment = .@"1"; | 34330 | var result: Alignment = .@"1"; |
| 34330 | 34331 | ||
| ... | @@ -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 | } |
| 34342 | 34343 | ||
| 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); |
| 34375 | 34376 | ||
| 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 | } |
| 34398 | 34403 | ||
| 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); |
| 34411 | 34416 | ||
| 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 | } |
| 34415 | 34425 | ||
| 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 { |
| 833 | 833 | ||
| 834 | /// When an LLVM struct type is created, an entry is inserted into this | 834 | /// 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 corresponding | 835 | /// 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); |
| 2501 | 2504 | ||
| 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; | ||
| 2516 | 2517 | ||
| 2517 | const field_name = struct_type.fieldName(ip, field_index).unwrap() orelse | 2518 | 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; |
| 3362 | 3377 | ||
| 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; |
| 3369 | 3386 | ||
| 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 | } |
| 4042 | 4070 | ||
| 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); |
| 6126 | 6159 | ||
| 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 | } |
| 835 | 835 | ||
| 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 | }; |
| 879 | 879 | ||
| 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 }, | ||
| 979 | 979 | ||
| 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 field | 1015 | 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 | } |
| 1061 | 1059 | ||
| 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 | } |
| 1178 | 1176 |
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 | |||
| 623 | test "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 | |||
| 650 | test "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 | } |
| 19 | 19 | ||
| 20 | test "comparison of @alignOf(T) against zero" { | 20 | test "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 | } |
| 40 | 29 | ||
| 41 | test "correct alignment for elements and slices of aligned array" { | 30 | test "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" { |
| 37 | test "empty union" { | 37 | test "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 | } |
| 42 | 42 | ||
| 43 | test "empty extern union" { | 43 | test "empty extern union" { |