compiler: move unions into InternPool
There are a couple concepts here worth understanding:
Key.UnionType - This type is available *before* resolving the union's
fields. The enum tag type, number of fields, and field names, field
types, and field alignments are not available with this.
InternPool.UnionType - This one can be obtained from the above type with
`InternPool.loadUnionType` which asserts that the union's enum tag type
has been resolved. This one has all the information available.
Additionally:
* ZIR: Turn an unused bit into `any_aligned_fields` flag to help
semantic analysis know whether a union has explicit alignment on any
fields (usually not).
* Sema: delete `resolveTypeRequiresComptime` which had the same type
signature and near-duplicate logic to `typeRequiresComptime`.
- Make opaque types not report comptime-only (this was inconsistent
between the two implementations of this function).
* Implement accepted proposal #12556 which is a breaking change.
This commit is contained in:
402
src/Module.zig
402
src/Module.zig
@@ -96,7 +96,7 @@ intern_pool: InternPool = .{},
|
||||
/// Current uses that must be eliminated:
|
||||
/// * Struct comptime_args
|
||||
/// * Struct optimized_order
|
||||
/// * Union fields
|
||||
/// * comptime pointer mutation
|
||||
/// This memory lives until the Module is destroyed.
|
||||
tmp_hack_arena: std.heap.ArenaAllocator,
|
||||
|
||||
@@ -736,7 +736,7 @@ pub const Decl = struct {
|
||||
|
||||
/// If the Decl owns its value and it is a union, return it,
|
||||
/// otherwise null.
|
||||
pub fn getOwnedUnion(decl: Decl, mod: *Module) ?*Union {
|
||||
pub fn getOwnedUnion(decl: Decl, mod: *Module) ?InternPool.UnionType {
|
||||
if (!decl.owns_tv) return null;
|
||||
if (decl.val.ip_index == .none) return null;
|
||||
return mod.typeToUnion(decl.val.toType());
|
||||
@@ -778,7 +778,7 @@ pub const Decl = struct {
|
||||
else => switch (mod.intern_pool.indexToKey(decl.val.toIntern())) {
|
||||
.opaque_type => |opaque_type| opaque_type.namespace.toOptional(),
|
||||
.struct_type => |struct_type| struct_type.namespace,
|
||||
.union_type => |union_type| mod.unionPtr(union_type.index).namespace.toOptional(),
|
||||
.union_type => |union_type| union_type.namespace.toOptional(),
|
||||
.enum_type => |enum_type| enum_type.namespace,
|
||||
else => .none,
|
||||
},
|
||||
@@ -1064,246 +1064,6 @@ pub const Struct = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub const Union = struct {
|
||||
/// An enum type which is used for the tag of the union.
|
||||
/// This type is created even for untagged unions, even when the memory
|
||||
/// layout does not store the tag.
|
||||
/// Whether zig chooses this type or the user specifies it, it is stored here.
|
||||
/// This will be set to the null type until status is `have_field_types`.
|
||||
tag_ty: Type,
|
||||
/// Set of field names in declaration order.
|
||||
fields: Fields,
|
||||
/// Represents the declarations inside this union.
|
||||
namespace: Namespace.Index,
|
||||
/// The Decl that corresponds to the union itself.
|
||||
owner_decl: Decl.Index,
|
||||
/// Index of the union_decl ZIR instruction.
|
||||
zir_index: Zir.Inst.Index,
|
||||
|
||||
layout: std.builtin.Type.ContainerLayout,
|
||||
status: enum {
|
||||
none,
|
||||
field_types_wip,
|
||||
have_field_types,
|
||||
layout_wip,
|
||||
have_layout,
|
||||
fully_resolved_wip,
|
||||
// The types and all its fields have had their layout resolved. Even through pointer,
|
||||
// which `have_layout` does not ensure.
|
||||
fully_resolved,
|
||||
},
|
||||
requires_comptime: PropertyBoolean = .unknown,
|
||||
assumed_runtime_bits: bool = false,
|
||||
|
||||
pub const Index = enum(u32) {
|
||||
_,
|
||||
|
||||
pub fn toOptional(i: Index) OptionalIndex {
|
||||
return @as(OptionalIndex, @enumFromInt(@intFromEnum(i)));
|
||||
}
|
||||
};
|
||||
|
||||
pub const OptionalIndex = enum(u32) {
|
||||
none = std.math.maxInt(u32),
|
||||
_,
|
||||
|
||||
pub fn init(oi: ?Index) OptionalIndex {
|
||||
return @as(OptionalIndex, @enumFromInt(@intFromEnum(oi orelse return .none)));
|
||||
}
|
||||
|
||||
pub fn unwrap(oi: OptionalIndex) ?Index {
|
||||
if (oi == .none) return null;
|
||||
return @as(Index, @enumFromInt(@intFromEnum(oi)));
|
||||
}
|
||||
};
|
||||
|
||||
pub const Field = struct {
|
||||
/// undefined until `status` is `have_field_types` or `have_layout`.
|
||||
ty: Type,
|
||||
/// 0 means the ABI alignment of the type.
|
||||
abi_align: Alignment,
|
||||
|
||||
/// Returns the field alignment, assuming the union is not packed.
|
||||
/// Keep implementation in sync with `Sema.unionFieldAlignment`.
|
||||
/// Prefer to call that function instead of this one during Sema.
|
||||
pub fn normalAlignment(field: Field, mod: *Module) u32 {
|
||||
return @as(u32, @intCast(field.abi_align.toByteUnitsOptional() orelse field.ty.abiAlignment(mod)));
|
||||
}
|
||||
};
|
||||
|
||||
pub const Fields = std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, Field);
|
||||
|
||||
pub fn getFullyQualifiedName(s: *Union, mod: *Module) !InternPool.NullTerminatedString {
|
||||
return mod.declPtr(s.owner_decl).getFullyQualifiedName(mod);
|
||||
}
|
||||
|
||||
pub fn srcLoc(self: Union, mod: *Module) SrcLoc {
|
||||
const owner_decl = mod.declPtr(self.owner_decl);
|
||||
return .{
|
||||
.file_scope = owner_decl.getFileScope(mod),
|
||||
.parent_decl_node = owner_decl.src_node,
|
||||
.lazy = LazySrcLoc.nodeOffset(0),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn haveFieldTypes(u: Union) bool {
|
||||
return switch (u.status) {
|
||||
.none,
|
||||
.field_types_wip,
|
||||
=> false,
|
||||
.have_field_types,
|
||||
.layout_wip,
|
||||
.have_layout,
|
||||
.fully_resolved_wip,
|
||||
.fully_resolved,
|
||||
=> true,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn hasAllZeroBitFieldTypes(u: Union, mod: *Module) bool {
|
||||
assert(u.haveFieldTypes());
|
||||
for (u.fields.values()) |field| {
|
||||
if (field.ty.hasRuntimeBits(mod)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn mostAlignedField(u: Union, mod: *Module) u32 {
|
||||
assert(u.haveFieldTypes());
|
||||
var most_alignment: u32 = 0;
|
||||
var most_index: usize = undefined;
|
||||
for (u.fields.values(), 0..) |field, i| {
|
||||
if (!field.ty.hasRuntimeBits(mod)) continue;
|
||||
|
||||
const field_align = field.normalAlignment(mod);
|
||||
if (field_align > most_alignment) {
|
||||
most_alignment = field_align;
|
||||
most_index = i;
|
||||
}
|
||||
}
|
||||
return @as(u32, @intCast(most_index));
|
||||
}
|
||||
|
||||
/// Returns 0 if the union is represented with 0 bits at runtime.
|
||||
pub fn abiAlignment(u: Union, mod: *Module, have_tag: bool) u32 {
|
||||
var max_align: u32 = 0;
|
||||
if (have_tag) max_align = u.tag_ty.abiAlignment(mod);
|
||||
for (u.fields.values()) |field| {
|
||||
if (!field.ty.hasRuntimeBits(mod)) continue;
|
||||
|
||||
const field_align = field.normalAlignment(mod);
|
||||
max_align = @max(max_align, field_align);
|
||||
}
|
||||
return max_align;
|
||||
}
|
||||
|
||||
pub fn abiSize(u: Union, mod: *Module, have_tag: bool) u64 {
|
||||
return u.getLayout(mod, have_tag).abi_size;
|
||||
}
|
||||
|
||||
pub const Layout = struct {
|
||||
abi_size: u64,
|
||||
abi_align: u32,
|
||||
most_aligned_field: u32,
|
||||
most_aligned_field_size: u64,
|
||||
biggest_field: u32,
|
||||
payload_size: u64,
|
||||
payload_align: u32,
|
||||
tag_align: u32,
|
||||
tag_size: u64,
|
||||
padding: u32,
|
||||
};
|
||||
|
||||
pub fn haveLayout(u: Union) bool {
|
||||
return switch (u.status) {
|
||||
.none,
|
||||
.field_types_wip,
|
||||
.have_field_types,
|
||||
.layout_wip,
|
||||
=> false,
|
||||
.have_layout,
|
||||
.fully_resolved_wip,
|
||||
.fully_resolved,
|
||||
=> true,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getLayout(u: Union, mod: *Module, have_tag: bool) Layout {
|
||||
assert(u.haveLayout());
|
||||
var most_aligned_field: u32 = undefined;
|
||||
var most_aligned_field_size: u64 = undefined;
|
||||
var biggest_field: u32 = undefined;
|
||||
var payload_size: u64 = 0;
|
||||
var payload_align: u32 = 0;
|
||||
const fields = u.fields.values();
|
||||
for (fields, 0..) |field, i| {
|
||||
if (!field.ty.hasRuntimeBitsIgnoreComptime(mod)) continue;
|
||||
|
||||
const field_align = field.abi_align.toByteUnitsOptional() orelse field.ty.abiAlignment(mod);
|
||||
const field_size = field.ty.abiSize(mod);
|
||||
if (field_size > payload_size) {
|
||||
payload_size = field_size;
|
||||
biggest_field = @as(u32, @intCast(i));
|
||||
}
|
||||
if (field_align > payload_align) {
|
||||
payload_align = @as(u32, @intCast(field_align));
|
||||
most_aligned_field = @as(u32, @intCast(i));
|
||||
most_aligned_field_size = field_size;
|
||||
}
|
||||
}
|
||||
payload_align = @max(payload_align, 1);
|
||||
if (!have_tag or !u.tag_ty.hasRuntimeBits(mod)) {
|
||||
return .{
|
||||
.abi_size = std.mem.alignForward(u64, payload_size, payload_align),
|
||||
.abi_align = payload_align,
|
||||
.most_aligned_field = most_aligned_field,
|
||||
.most_aligned_field_size = most_aligned_field_size,
|
||||
.biggest_field = biggest_field,
|
||||
.payload_size = payload_size,
|
||||
.payload_align = payload_align,
|
||||
.tag_align = 0,
|
||||
.tag_size = 0,
|
||||
.padding = 0,
|
||||
};
|
||||
}
|
||||
// Put the tag before or after the payload depending on which one's
|
||||
// alignment is greater.
|
||||
const tag_size = u.tag_ty.abiSize(mod);
|
||||
const tag_align = @max(1, u.tag_ty.abiAlignment(mod));
|
||||
var size: u64 = 0;
|
||||
var padding: u32 = undefined;
|
||||
if (tag_align >= payload_align) {
|
||||
// {Tag, Payload}
|
||||
size += tag_size;
|
||||
size = std.mem.alignForward(u64, size, payload_align);
|
||||
size += payload_size;
|
||||
const prev_size = size;
|
||||
size = std.mem.alignForward(u64, size, tag_align);
|
||||
padding = @as(u32, @intCast(size - prev_size));
|
||||
} else {
|
||||
// {Payload, Tag}
|
||||
size += payload_size;
|
||||
size = std.mem.alignForward(u64, size, tag_align);
|
||||
size += tag_size;
|
||||
const prev_size = size;
|
||||
size = std.mem.alignForward(u64, size, payload_align);
|
||||
padding = @as(u32, @intCast(size - prev_size));
|
||||
}
|
||||
return .{
|
||||
.abi_size = size,
|
||||
.abi_align = @max(tag_align, payload_align),
|
||||
.most_aligned_field = most_aligned_field,
|
||||
.most_aligned_field_size = most_aligned_field_size,
|
||||
.biggest_field = biggest_field,
|
||||
.payload_size = payload_size,
|
||||
.payload_align = payload_align,
|
||||
.tag_align = tag_align,
|
||||
.tag_size = tag_size,
|
||||
.padding = padding,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const DeclAdapter = struct {
|
||||
mod: *Module,
|
||||
|
||||
@@ -3182,10 +2942,6 @@ pub fn namespacePtr(mod: *Module, index: Namespace.Index) *Namespace {
|
||||
return mod.intern_pool.namespacePtr(index);
|
||||
}
|
||||
|
||||
pub fn unionPtr(mod: *Module, index: Union.Index) *Union {
|
||||
return mod.intern_pool.unionPtr(index);
|
||||
}
|
||||
|
||||
pub fn structPtr(mod: *Module, index: Struct.Index) *Struct {
|
||||
return mod.intern_pool.structPtr(index);
|
||||
}
|
||||
@@ -3651,11 +3407,11 @@ fn updateZirRefs(mod: *Module, file: *File, old_zir: Zir) !void {
|
||||
};
|
||||
}
|
||||
|
||||
if (decl.getOwnedUnion(mod)) |union_obj| {
|
||||
union_obj.zir_index = inst_map.get(union_obj.zir_index) orelse {
|
||||
if (decl.getOwnedUnion(mod)) |union_type| {
|
||||
union_type.setZirIndex(ip, inst_map.get(union_type.zir_index) orelse {
|
||||
try file.deleted_decls.append(gpa, decl_index);
|
||||
continue;
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
if (decl.getOwnedFunction(mod)) |func| {
|
||||
@@ -5550,14 +5306,6 @@ pub fn destroyStruct(mod: *Module, index: Struct.Index) void {
|
||||
return mod.intern_pool.destroyStruct(mod.gpa, index);
|
||||
}
|
||||
|
||||
pub fn createUnion(mod: *Module, initialization: Union) Allocator.Error!Union.Index {
|
||||
return mod.intern_pool.createUnion(mod.gpa, initialization);
|
||||
}
|
||||
|
||||
pub fn destroyUnion(mod: *Module, index: Union.Index) void {
|
||||
return mod.intern_pool.destroyUnion(mod.gpa, index);
|
||||
}
|
||||
|
||||
pub fn allocateNewDecl(
|
||||
mod: *Module,
|
||||
namespace: Namespace.Index,
|
||||
@@ -6956,10 +6704,14 @@ pub fn typeToStruct(mod: *Module, ty: Type) ?*Struct {
|
||||
return mod.structPtr(struct_index);
|
||||
}
|
||||
|
||||
pub fn typeToUnion(mod: *Module, ty: Type) ?*Union {
|
||||
/// This asserts that the union's enum tag type has been resolved.
|
||||
pub fn typeToUnion(mod: *Module, ty: Type) ?InternPool.UnionType {
|
||||
if (ty.ip_index == .none) return null;
|
||||
const union_index = mod.intern_pool.indexToUnionType(ty.toIntern()).unwrap() orelse return null;
|
||||
return mod.unionPtr(union_index);
|
||||
const ip = &mod.intern_pool;
|
||||
switch (ip.indexToKey(ty.ip_index)) {
|
||||
.union_type => |k| return ip.loadUnionType(k),
|
||||
else => return null,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn typeToFunc(mod: *Module, ty: Type) ?InternPool.Key.FuncType {
|
||||
@@ -7045,3 +6797,131 @@ pub fn getParamName(mod: *Module, func_index: InternPool.Index, index: u32) [:0]
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
pub const UnionLayout = struct {
|
||||
abi_size: u64,
|
||||
abi_align: u32,
|
||||
most_aligned_field: u32,
|
||||
most_aligned_field_size: u64,
|
||||
biggest_field: u32,
|
||||
payload_size: u64,
|
||||
payload_align: u32,
|
||||
tag_align: u32,
|
||||
tag_size: u64,
|
||||
padding: u32,
|
||||
};
|
||||
|
||||
pub fn getUnionLayout(mod: *Module, u: InternPool.UnionType) UnionLayout {
|
||||
const ip = &mod.intern_pool;
|
||||
assert(u.haveLayout(ip));
|
||||
var most_aligned_field: u32 = undefined;
|
||||
var most_aligned_field_size: u64 = undefined;
|
||||
var biggest_field: u32 = undefined;
|
||||
var payload_size: u64 = 0;
|
||||
var payload_align: u32 = 0;
|
||||
for (u.field_types.get(ip), 0..) |field_ty, i| {
|
||||
if (!field_ty.toType().hasRuntimeBitsIgnoreComptime(mod)) continue;
|
||||
|
||||
const field_align = u.fieldAlign(ip, @intCast(i)).toByteUnitsOptional() orelse
|
||||
field_ty.toType().abiAlignment(mod);
|
||||
const field_size = field_ty.toType().abiSize(mod);
|
||||
if (field_size > payload_size) {
|
||||
payload_size = field_size;
|
||||
biggest_field = @intCast(i);
|
||||
}
|
||||
if (field_align > payload_align) {
|
||||
payload_align = @intCast(field_align);
|
||||
most_aligned_field = @intCast(i);
|
||||
most_aligned_field_size = field_size;
|
||||
}
|
||||
}
|
||||
payload_align = @max(payload_align, 1);
|
||||
const have_tag = u.flagsPtr(ip).runtime_tag.hasTag();
|
||||
if (!have_tag or !u.enum_tag_ty.toType().hasRuntimeBits(mod)) {
|
||||
return .{
|
||||
.abi_size = std.mem.alignForward(u64, payload_size, payload_align),
|
||||
.abi_align = payload_align,
|
||||
.most_aligned_field = most_aligned_field,
|
||||
.most_aligned_field_size = most_aligned_field_size,
|
||||
.biggest_field = biggest_field,
|
||||
.payload_size = payload_size,
|
||||
.payload_align = payload_align,
|
||||
.tag_align = 0,
|
||||
.tag_size = 0,
|
||||
.padding = 0,
|
||||
};
|
||||
}
|
||||
// Put the tag before or after the payload depending on which one's
|
||||
// alignment is greater.
|
||||
const tag_size = u.enum_tag_ty.toType().abiSize(mod);
|
||||
const tag_align = @max(1, u.enum_tag_ty.toType().abiAlignment(mod));
|
||||
var size: u64 = 0;
|
||||
var padding: u32 = undefined;
|
||||
if (tag_align >= payload_align) {
|
||||
// {Tag, Payload}
|
||||
size += tag_size;
|
||||
size = std.mem.alignForward(u64, size, payload_align);
|
||||
size += payload_size;
|
||||
const prev_size = size;
|
||||
size = std.mem.alignForward(u64, size, tag_align);
|
||||
padding = @as(u32, @intCast(size - prev_size));
|
||||
} else {
|
||||
// {Payload, Tag}
|
||||
size += payload_size;
|
||||
size = std.mem.alignForward(u64, size, tag_align);
|
||||
size += tag_size;
|
||||
const prev_size = size;
|
||||
size = std.mem.alignForward(u64, size, payload_align);
|
||||
padding = @as(u32, @intCast(size - prev_size));
|
||||
}
|
||||
return .{
|
||||
.abi_size = size,
|
||||
.abi_align = @max(tag_align, payload_align),
|
||||
.most_aligned_field = most_aligned_field,
|
||||
.most_aligned_field_size = most_aligned_field_size,
|
||||
.biggest_field = biggest_field,
|
||||
.payload_size = payload_size,
|
||||
.payload_align = payload_align,
|
||||
.tag_align = tag_align,
|
||||
.tag_size = tag_size,
|
||||
.padding = padding,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn unionAbiSize(mod: *Module, u: InternPool.UnionType) u64 {
|
||||
return mod.getUnionLayout(u).abi_size;
|
||||
}
|
||||
|
||||
/// Returns 0 if the union is represented with 0 bits at runtime.
|
||||
/// TODO: this returns alignment in byte units should should be a u64
|
||||
pub fn unionAbiAlignment(mod: *Module, u: InternPool.UnionType) u32 {
|
||||
const ip = &mod.intern_pool;
|
||||
const have_tag = u.flagsPtr(ip).runtime_tag.hasTag();
|
||||
var max_align: u32 = 0;
|
||||
if (have_tag) max_align = u.enum_tag_ty.toType().abiAlignment(mod);
|
||||
for (u.field_types.get(ip), 0..) |field_ty, field_index| {
|
||||
if (!field_ty.toType().hasRuntimeBits(mod)) continue;
|
||||
|
||||
const field_align = mod.unionFieldNormalAlignment(u, @intCast(field_index));
|
||||
max_align = @max(max_align, field_align);
|
||||
}
|
||||
return max_align;
|
||||
}
|
||||
|
||||
/// Returns the field alignment, assuming the union is not packed.
|
||||
/// Keep implementation in sync with `Sema.unionFieldAlignment`.
|
||||
/// Prefer to call that function instead of this one during Sema.
|
||||
/// TODO: this returns alignment in byte units should should be a u64
|
||||
pub fn unionFieldNormalAlignment(mod: *Module, u: InternPool.UnionType, field_index: u32) u32 {
|
||||
const ip = &mod.intern_pool;
|
||||
if (u.fieldAlign(ip, field_index).toByteUnitsOptional()) |a| return @intCast(a);
|
||||
const field_ty = u.field_types.get(ip)[field_index].toType();
|
||||
return field_ty.abiAlignment(mod);
|
||||
}
|
||||
|
||||
pub fn unionTagFieldIndex(mod: *Module, u: InternPool.UnionType, enum_tag: Value) ?u32 {
|
||||
const ip = &mod.intern_pool;
|
||||
assert(ip.typeOf(enum_tag.toIntern()) == u.enum_tag_ty);
|
||||
const enum_type = ip.indexToKey(u.enum_tag_ty).enum_type;
|
||||
return enum_type.tagValueIndex(ip, enum_tag.toIntern());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user