Sema: fix union layout logic to match struct layout logic

This commit is contained in:
Jacob Young
2025-06-09 02:34:30 -04:00
committed by mlugg
parent d312dfc1f2
commit 746137034e
2 changed files with 26 additions and 17 deletions

View File

@@ -35054,7 +35054,7 @@ pub fn resolveUnionAlignment(
union_type.setAlignment(ip, max_align);
}
/// This logic must be kept in sync with `Zcu.getUnionLayout`.
/// This logic must be kept in sync with `Type.getUnionLayout`.
pub fn resolveUnionLayout(sema: *Sema, ty: Type) SemaError!void {
const pt = sema.pt;
const ip = &pt.zcu.intern_pool;
@@ -35090,6 +35090,14 @@ pub fn resolveUnionLayout(sema: *Sema, ty: Type) SemaError!void {
const field_ty: Type = .fromInterned(union_type.field_types.get(ip)[field_index]);
if (field_ty.isNoReturn(pt.zcu)) continue;
// We need to call `hasRuntimeBits` before calling `abiSize` to prevent reachable `unreachable`s,
// but `hasRuntimeBits` only resolves field types and so may infinite recurse on a layout wip type,
// so we must resolve the layout manually first, instead of waiting for `abiSize` to do it for us.
// This is arguably just hacking around bugs in both `abiSize` for not allowing arbitrary types to
// be queried, enabling failures to be handled with the emission of a compile error, and also in
// `hasRuntimeBits` for ever being able to infinite recurse in the first place.
try field_ty.resolveLayout(pt);
if (try field_ty.hasRuntimeBitsSema(pt)) {
max_size = @max(max_size, field_ty.abiSizeSema(pt) catch |err| switch (err) {
error.AnalysisFail => {

View File

@@ -3915,29 +3915,30 @@ fn resolveUnionInner(
pub fn getUnionLayout(loaded_union: InternPool.LoadedUnionType, zcu: *const Zcu) Zcu.UnionLayout {
const ip = &zcu.intern_pool;
assert(loaded_union.haveLayout(ip));
var most_aligned_field: u32 = undefined;
var most_aligned_field_size: u64 = undefined;
var biggest_field: u32 = undefined;
var most_aligned_field: u32 = 0;
var most_aligned_field_align: InternPool.Alignment = .@"1";
var most_aligned_field_size: u64 = 0;
var biggest_field: u32 = 0;
var payload_size: u64 = 0;
var payload_align: InternPool.Alignment = .@"1";
for (loaded_union.field_types.get(ip), 0..) |field_ty, field_index| {
if (Type.fromInterned(field_ty).isNoReturn(zcu)) continue;
for (loaded_union.field_types.get(ip), 0..) |field_ty_ip_index, field_index| {
const field_ty: Type = .fromInterned(field_ty_ip_index);
if (field_ty.isNoReturn(zcu)) continue;
const explicit_align = loaded_union.fieldAlign(ip, field_index);
const field_align = if (explicit_align != .none)
explicit_align
else
Type.fromInterned(field_ty).abiAlignment(zcu);
if (Type.fromInterned(field_ty).hasRuntimeBits(zcu)) {
const field_size = Type.fromInterned(field_ty).abiSize(zcu);
if (field_size > payload_size) {
payload_size = field_size;
biggest_field = @intCast(field_index);
}
if (field_align.compare(.gte, payload_align)) {
most_aligned_field = @intCast(field_index);
most_aligned_field_size = field_size;
}
field_ty.abiAlignment(zcu);
const field_size = field_ty.abiSize(zcu);
if (field_size > payload_size) {
payload_size = field_size;
biggest_field = @intCast(field_index);
}
if (field_size > 0 and field_align.compare(.gte, most_aligned_field_align)) {
most_aligned_field = @intCast(field_index);
most_aligned_field_align = field_align;
most_aligned_field_size = field_size;
}
payload_align = payload_align.max(field_align);
}