diff options
Diffstat (limited to 'src/Sema.zig')
| -rw-r--r-- | src/Sema.zig | 113 |
1 files changed, 104 insertions, 9 deletions
diff --git a/src/Sema.zig b/src/Sema.zig index 51895dedb1..c83ef222c0 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -3200,6 +3200,7 @@ fn zirUnionDecl( .any_aligned_fields = small.any_aligned_fields, .requires_comptime = .unknown, .assumed_runtime_bits = false, + .alignment = .none, }, .decl = new_decl_index, .namespace = new_namespace_index, @@ -20988,6 +20989,7 @@ fn zirReify( .any_aligned_fields = any_aligned_fields, .requires_comptime = .unknown, .assumed_runtime_bits = false, + .alignment = .none, }, .field_types = union_fields.items(.type), .field_aligns = if (any_aligned_fields) union_fields.items(.alignment) else &.{}, @@ -34921,11 +34923,56 @@ fn checkMemOperand(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void return sema.failWithOwnedErrorMsg(block, msg); } +/// Resolve a unions's alignment only without triggering resolution of its layout. +/// Asserts that the alignment is not yet resolved. +pub fn resolveUnionAlignment( + sema: *Sema, + ty: Type, + union_type: InternPool.Key.UnionType, +) CompileError!Alignment { + const mod = sema.mod; + const ip = &mod.intern_pool; + const target = mod.getTarget(); + + assert(!union_type.haveLayout(ip)); + + if (union_type.flagsPtr(ip).status == .field_types_wip) { + // We'll guess "pointer-aligned", if the union has an + // underaligned pointer field then some allocations + // might require explicit alignment. + return Alignment.fromByteUnits(@divExact(target.ptrBitWidth(), 8)); + } + + try sema.resolveTypeFieldsUnion(ty, union_type); + + const union_obj = ip.loadUnionType(union_type); + var max_align: Alignment = .@"1"; + for (0..union_obj.field_names.len) |field_index| { + const field_ty = union_obj.field_types.get(ip)[field_index].toType(); + if (!(try sema.typeHasRuntimeBits(field_ty))) continue; + + const explicit_align = union_obj.fieldAlign(ip, @intCast(field_index)); + const field_align = if (explicit_align != .none) + explicit_align + else + try sema.typeAbiAlignment(field_ty); + + max_align = max_align.max(field_align); + } + + union_type.flagsPtr(ip).alignment = max_align; + return max_align; +} + +/// This logic must be kept in sync with `Module.getUnionLayout`. fn resolveUnionLayout(sema: *Sema, ty: Type) CompileError!void { const mod = sema.mod; const ip = &mod.intern_pool; - try sema.resolveTypeFields(ty); - const union_obj = mod.typeToUnion(ty).?; + + const union_type = ip.indexToKey(ty.ip_index).union_type; + try sema.resolveTypeFieldsUnion(ty, union_type); + + const union_obj = ip.loadUnionType(union_type); switch (union_obj.flagsPtr(ip).status) { .none, .have_field_types => {}, .field_types_wip, .layout_wip => { @@ -34939,25 +34986,74 @@ fn resolveUnionLayout(sema: *Sema, ty: Type) CompileError!void { }, .have_layout, .fully_resolved_wip, .fully_resolved => return, } + const prev_status = union_obj.flagsPtr(ip).status; errdefer if (union_obj.flagsPtr(ip).status == .layout_wip) { union_obj.flagsPtr(ip).status = prev_status; }; union_obj.flagsPtr(ip).status = .layout_wip; - for (0..union_obj.field_types.len) |field_index| { + + var max_size: u64 = 0; + var max_align: Alignment = .@"1"; + for (0..union_obj.field_names.len) |field_index| { const field_ty = union_obj.field_types.get(ip)[field_index].toType(); - sema.resolveTypeLayout(field_ty) catch |err| switch (err) { + if (!(try sema.typeHasRuntimeBits(field_ty))) continue; + + max_size = @max(max_size, sema.typeAbiSize(field_ty) catch |err| switch (err) { error.AnalysisFail => { const msg = sema.err orelse return err; try sema.addFieldErrNote(ty, field_index, msg, "while checking this field", .{}); return err; }, else => return err, - }; - } - union_obj.flagsPtr(ip).status = .have_layout; - _ = try sema.typeRequiresComptime(ty); + }); + + const explicit_align = union_obj.fieldAlign(ip, @intCast(field_index)); + const field_align = if (explicit_align != .none) + explicit_align + else + try sema.typeAbiAlignment(field_ty); + + max_align = max_align.max(field_align); + } + + const flags = union_obj.flagsPtr(ip); + const has_runtime_tag = flags.runtime_tag.hasTag() and try sema.typeHasRuntimeBits(union_obj.enum_tag_ty.toType()); + const size, const alignment, const padding = if (has_runtime_tag) layout: { + const enum_tag_type = union_obj.enum_tag_ty.toType(); + const tag_align = try sema.typeAbiAlignment(enum_tag_type); + const tag_size = try sema.typeAbiSize(enum_tag_type); + + // Put the tag before or after the payload depending on which one's + // alignment is greater. + var size: u64 = 0; + var padding: u32 = 0; + if (tag_align.compare(.gte, max_align)) { + // {Tag, Payload} + size += tag_size; + size = max_align.forward(size); + size += max_size; + const prev_size = size; + size = tag_align.forward(size); + padding = @intCast(size - prev_size); + } else { + // {Payload, Tag} + size += max_size; + size = tag_align.forward(size); + size += tag_size; + const prev_size = size; + size = max_align.forward(size); + padding = @intCast(size - prev_size); + } + + break :layout .{ size, max_align.max(tag_align), padding }; + } else .{ max_align.forward(max_size), max_align, 0 }; + + union_type.size(ip).* = @intCast(size); + union_type.padding(ip).* = padding; + flags.alignment = alignment; + flags.status = .have_layout; if (union_obj.flagsPtr(ip).assumed_runtime_bits and !(try sema.typeHasRuntimeBits(ty))) { const msg = try Module.ErrorMsg.create( @@ -35034,7 +35130,6 @@ fn resolveStructFully(sema: *Sema, ty: Type) CompileError!void { fn resolveUnionFully(sema: *Sema, ty: Type) CompileError!void { try sema.resolveUnionLayout(ty); - try sema.resolveTypeFields(ty); const mod = sema.mod; const ip = &mod.intern_pool; |
