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:
Andrew Kelley 2023-08-21 14:27:34 -07:00
parent 6a5463951f
commit ada0010471
27 changed files with 1478 additions and 1440 deletions

View File

@ -69,16 +69,9 @@ pub const Instruction = union(Opcode) {
register: u8, register: u8,
offset: u64, offset: u64,
}, },
offset_extended: struct {
register: u8,
offset: u64,
},
restore: struct { restore: struct {
register: u8, register: u8,
}, },
restore_extended: struct {
register: u8,
},
nop: void, nop: void,
set_loc: struct { set_loc: struct {
address: u64, address: u64,
@ -92,6 +85,13 @@ pub const Instruction = union(Opcode) {
advance_loc4: struct { advance_loc4: struct {
delta: u32, delta: u32,
}, },
offset_extended: struct {
register: u8,
offset: u64,
},
restore_extended: struct {
register: u8,
},
undefined: struct { undefined: struct {
register: u8, register: u8,
}, },

View File

@ -614,9 +614,9 @@ test "std.meta.FieldEnum" {
const Tagged = union(enum) { a: u8, b: void, c: f32 }; const Tagged = union(enum) { a: u8, b: void, c: f32 };
try testing.expectEqual(Tag(Tagged), FieldEnum(Tagged)); try testing.expectEqual(Tag(Tagged), FieldEnum(Tagged));
const Tag2 = enum { b, c, a }; const Tag2 = enum { a, b, c };
const Tagged2 = union(Tag2) { a: u8, b: void, c: f32 }; const Tagged2 = union(Tag2) { a: u8, b: void, c: f32 };
try testing.expect(Tag(Tagged2) != FieldEnum(Tagged2)); try testing.expect(Tag(Tagged2) == FieldEnum(Tagged2));
const Tag3 = enum(u8) { a, b, c = 7 }; const Tag3 = enum(u8) { a, b, c = 7 };
const Tagged3 = union(Tag3) { a: u8, b: void, c: f32 }; const Tagged3 = union(Tag3) { a: u8, b: void, c: f32 };

View File

@ -4696,6 +4696,7 @@ fn unionDeclInner(
const bits_per_field = 4; const bits_per_field = 4;
const max_field_size = 5; const max_field_size = 5;
var any_aligned_fields = false;
var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, field_count, bits_per_field, max_field_size); var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, field_count, bits_per_field, max_field_size);
defer wip_members.deinit(); defer wip_members.deinit();
@ -4733,6 +4734,7 @@ fn unionDeclInner(
if (have_align) { if (have_align) {
const align_inst = try expr(&block_scope, &block_scope.base, .{ .rl = .{ .ty = .u32_type } }, member.ast.align_expr); const align_inst = try expr(&block_scope, &block_scope.base, .{ .rl = .{ .ty = .u32_type } }, member.ast.align_expr);
wip_members.appendToField(@intFromEnum(align_inst)); wip_members.appendToField(@intFromEnum(align_inst));
any_aligned_fields = true;
} }
if (have_value) { if (have_value) {
if (arg_inst == .none) { if (arg_inst == .none) {
@ -4783,6 +4785,7 @@ fn unionDeclInner(
.fields_len = field_count, .fields_len = field_count,
.decls_len = decl_count, .decls_len = decl_count,
.auto_enum_tag = auto_enum_tok != null, .auto_enum_tag = auto_enum_tok != null,
.any_aligned_fields = any_aligned_fields,
}); });
wip_members.finishBits(bits_per_field); wip_members.finishBits(bits_per_field);
@ -11754,6 +11757,7 @@ const GenZir = struct {
decls_len: u32, decls_len: u32,
layout: std.builtin.Type.ContainerLayout, layout: std.builtin.Type.ContainerLayout,
auto_enum_tag: bool, auto_enum_tag: bool,
any_aligned_fields: bool,
}) !void { }) !void {
const astgen = gz.astgen; const astgen = gz.astgen;
const gpa = astgen.gpa; const gpa = astgen.gpa;
@ -11790,6 +11794,7 @@ const GenZir = struct {
.name_strategy = gz.anon_name_strategy, .name_strategy = gz.anon_name_strategy,
.layout = args.layout, .layout = args.layout,
.auto_enum_tag = args.auto_enum_tag, .auto_enum_tag = args.auto_enum_tag,
.any_aligned_fields = args.any_aligned_fields,
}), }),
.operand = payload_index, .operand = payload_index,
} }, } },

View File

@ -46,13 +46,6 @@ allocated_structs: std.SegmentedList(Module.Struct, 0) = .{},
/// When a Struct object is freed from `allocated_structs`, it is pushed into this stack. /// When a Struct object is freed from `allocated_structs`, it is pushed into this stack.
structs_free_list: std.ArrayListUnmanaged(Module.Struct.Index) = .{}, structs_free_list: std.ArrayListUnmanaged(Module.Struct.Index) = .{},
/// Union objects are stored in this data structure because:
/// * They contain pointers such as the field maps.
/// * They need to be mutated after creation.
allocated_unions: std.SegmentedList(Module.Union, 0) = .{},
/// When a Union object is freed from `allocated_unions`, it is pushed into this stack.
unions_free_list: std.ArrayListUnmanaged(Module.Union.Index) = .{},
/// Some types such as enums, structs, and unions need to store mappings from field names /// Some types such as enums, structs, and unions need to store mappings from field names
/// to field index, or value to field index. In such cases, they will store the underlying /// to field index, or value to field index. In such cases, they will store the underlying
/// field names and values directly, relying on one of these maps, stored separately, /// field names and values directly, relying on one of these maps, stored separately,
@ -241,7 +234,7 @@ pub const Key = union(enum) {
/// declaration. It is used for types that have no `struct` keyword in the /// declaration. It is used for types that have no `struct` keyword in the
/// source code, and were not created via `@Type`. /// source code, and were not created via `@Type`.
anon_struct_type: AnonStructType, anon_struct_type: AnonStructType,
union_type: UnionType, union_type: Key.UnionType,
opaque_type: OpaqueType, opaque_type: OpaqueType,
enum_type: EnumType, enum_type: EnumType,
func_type: FuncType, func_type: FuncType,
@ -391,17 +384,72 @@ pub const Key = union(enum) {
} }
}; };
/// Serves two purposes:
/// * Being the key in the InternPool hash map, which only requires the `decl` field.
/// * Provide the other fields that do not require chasing the enum type.
pub const UnionType = struct { pub const UnionType = struct {
index: Module.Union.Index, /// The Decl that corresponds to the union itself.
runtime_tag: RuntimeTag, decl: Module.Decl.Index,
/// The index of the `Tag.TypeUnion` payload. Ignored by `get`,
/// populated by `indexToKey`.
extra_index: u32,
namespace: Module.Namespace.Index,
flags: Tag.TypeUnion.Flags,
/// The enum that provides the list of field names and values.
enum_tag_ty: Index,
zir_index: Zir.Inst.Index,
pub const RuntimeTag = enum { none, safety, tagged }; /// The returned pointer expires with any addition to the `InternPool`.
pub fn flagsPtr(self: @This(), ip: *const InternPool) *Tag.TypeUnion.Flags {
const flags_field_index = std.meta.fieldIndex(Tag.TypeUnion, "flags").?;
return @ptrCast(&ip.extra.items[self.extra_index + flags_field_index]);
}
pub fn hasTag(self: UnionType) bool { pub fn haveFieldTypes(self: @This(), ip: *const InternPool) bool {
return switch (self.runtime_tag) { return self.flagsPtr(ip).status.haveFieldTypes();
.none => false, }
.tagged, .safety => true,
}; pub fn hasTag(self: @This(), ip: *const InternPool) bool {
return self.flagsPtr(ip).runtime_tag.hasTag();
}
pub fn getLayout(self: @This(), ip: *const InternPool) std.builtin.Type.ContainerLayout {
return self.flagsPtr(ip).layout;
}
pub fn haveLayout(self: @This(), ip: *const InternPool) bool {
return self.flagsPtr(ip).status.haveLayout();
}
/// Pointer to 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`.
/// This accessor is provided so that the tag type can be mutated, and so that
/// when it is mutated, the mutations are observed.
/// The returned pointer is invalidated when something is added to the `InternPool`.
pub fn tagTypePtr(self: @This(), ip: *const InternPool) *Index {
const tag_ty_field_index = std.meta.fieldIndex(Tag.TypeUnion, "tag_ty").?;
return @ptrCast(&ip.extra.items[self.extra_index + tag_ty_field_index]);
}
pub fn setFieldTypes(self: @This(), ip: *InternPool, types: []const Index) void {
@memcpy((Index.Slice{
.start = @intCast(self.extra_index + @typeInfo(Tag.TypeUnion).Struct.fields.len),
.len = @intCast(types.len),
}).get(ip), types);
}
pub fn setFieldAligns(self: @This(), ip: *InternPool, aligns: []const Alignment) void {
if (aligns.len == 0) return;
assert(self.flagsPtr(ip).any_aligned_fields);
@memcpy((Alignment.Slice{
.start = @intCast(
self.extra_index + @typeInfo(Tag.TypeUnion).Struct.fields.len + aligns.len,
),
.len = @intCast(aligns.len),
}).get(ip), aligns);
} }
}; };
@ -833,7 +881,6 @@ pub const Key = union(enum) {
=> |x| Hash.hash(seed, asBytes(&x)), => |x| Hash.hash(seed, asBytes(&x)),
.int_type => |x| Hash.hash(seed + @intFromEnum(x.signedness), asBytes(&x.bits)), .int_type => |x| Hash.hash(seed + @intFromEnum(x.signedness), asBytes(&x.bits)),
.union_type => |x| Hash.hash(seed + @intFromEnum(x.runtime_tag), asBytes(&x.index)),
.error_union => |x| switch (x.val) { .error_union => |x| switch (x.val) {
.err_name => |y| Hash.hash(seed + 0, asBytes(&x.ty) ++ asBytes(&y)), .err_name => |y| Hash.hash(seed + 0, asBytes(&x.ty) ++ asBytes(&y)),
@ -845,6 +892,7 @@ pub const Key = union(enum) {
inline .opaque_type, inline .opaque_type,
.enum_type, .enum_type,
.variable, .variable,
.union_type,
=> |x| Hash.hash(seed, asBytes(&x.decl)), => |x| Hash.hash(seed, asBytes(&x.decl)),
.int => |int| { .int => |int| {
@ -1079,10 +1127,6 @@ pub const Key = union(enum) {
const b_info = b.struct_type; const b_info = b.struct_type;
return std.meta.eql(a_info, b_info); return std.meta.eql(a_info, b_info);
}, },
.union_type => |a_info| {
const b_info = b.union_type;
return std.meta.eql(a_info, b_info);
},
.un => |a_info| { .un => |a_info| {
const b_info = b.un; const b_info = b.un;
return std.meta.eql(a_info, b_info); return std.meta.eql(a_info, b_info);
@ -1250,6 +1294,10 @@ pub const Key = union(enum) {
const b_info = b.enum_type; const b_info = b.enum_type;
return a_info.decl == b_info.decl; return a_info.decl == b_info.decl;
}, },
.union_type => |a_info| {
const b_info = b.union_type;
return a_info.decl == b_info.decl;
},
.aggregate => |a_info| { .aggregate => |a_info| {
const b_info = b.aggregate; const b_info = b.aggregate;
if (a_info.ty != b_info.ty) return false; if (a_info.ty != b_info.ty) return false;
@ -1385,6 +1433,158 @@ pub const Key = union(enum) {
} }
}; };
// Unlike `Tag.TypeUnion` which is an encoding, and `Key.UnionType` which is a
// minimal hashmap key, this type is a convenience type that contains info
// needed by semantic analysis.
pub const UnionType = struct {
/// The Decl that corresponds to the union itself.
decl: Module.Decl.Index,
/// Represents the declarations inside this union.
namespace: Module.Namespace.Index,
/// The enum tag type.
enum_tag_ty: Index,
/// The integer tag type of the enum.
int_tag_ty: Index,
/// List of field names in declaration order.
field_names: NullTerminatedString.Slice,
/// List of field types in declaration order.
/// These are `none` until `status` is `have_field_types` or `have_layout`.
field_types: Index.Slice,
/// List of field alignments in declaration order.
/// `none` means the ABI alignment of the type.
/// If this slice has length 0 it means all elements are `none`.
field_aligns: Alignment.Slice,
/// Index of the union_decl ZIR instruction.
zir_index: Zir.Inst.Index,
/// Index into extra array of the `flags` field.
flags_index: u32,
/// Copied from `enum_tag_ty`.
names_map: OptionalMapIndex,
pub const RuntimeTag = enum(u2) {
none,
safety,
tagged,
pub fn hasTag(self: RuntimeTag) bool {
return switch (self) {
.none => false,
.tagged, .safety => true,
};
}
};
pub const RequiresComptime = enum(u2) { no, yes, unknown, wip };
pub const Status = enum(u3) {
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,
pub fn haveFieldTypes(status: Status) bool {
return switch (status) {
.none,
.field_types_wip,
=> false,
.have_field_types,
.layout_wip,
.have_layout,
.fully_resolved_wip,
.fully_resolved,
=> true,
};
}
pub fn haveLayout(status: Status) bool {
return switch (status) {
.none,
.field_types_wip,
.have_field_types,
.layout_wip,
=> false,
.have_layout,
.fully_resolved_wip,
.fully_resolved,
=> true,
};
}
};
/// The returned pointer expires with any addition to the `InternPool`.
pub fn flagsPtr(self: UnionType, ip: *const InternPool) *Tag.TypeUnion.Flags {
return @ptrCast(&ip.extra.items[self.flags_index]);
}
/// Look up field index based on field name.
pub fn nameIndex(self: UnionType, ip: *const InternPool, name: NullTerminatedString) ?u32 {
const map = &ip.maps.items[@intFromEnum(self.names_map.unwrap().?)];
const adapter: NullTerminatedString.Adapter = .{ .strings = self.field_names.get(ip) };
const field_index = map.getIndexAdapted(name, adapter) orelse return null;
return @intCast(field_index);
}
pub fn hasTag(self: UnionType, ip: *const InternPool) bool {
return self.flagsPtr(ip).runtime_tag.hasTag();
}
pub fn haveLayout(self: UnionType, ip: *const InternPool) bool {
return self.flagsPtr(ip).status.haveLayout();
}
pub fn getLayout(self: UnionType, ip: *const InternPool) std.builtin.Type.ContainerLayout {
return self.flagsPtr(ip).layout;
}
pub fn fieldAlign(self: UnionType, ip: *const InternPool, field_index: u32) Alignment {
if (self.field_aligns.len == 0) return .none;
return self.field_aligns.get(ip)[field_index];
}
/// This does not mutate the field of UnionType.
pub fn setZirIndex(self: @This(), ip: *InternPool, new_zir_index: Zir.Inst.Index) void {
const flags_field_index = std.meta.fieldIndex(Tag.TypeUnion, "flags").?;
const zir_index_field_index = std.meta.fieldIndex(Tag.TypeUnion, "zir_index").?;
const ptr: *Zir.Inst.Index =
@ptrCast(&ip.extra.items[self.flags_index - flags_field_index + zir_index_field_index]);
ptr.* = new_zir_index;
}
};
/// Fetch all the interesting fields of a union type into a convenient data
/// structure.
/// This asserts that the union's enum tag type has been resolved.
pub fn loadUnionType(ip: *InternPool, key: Key.UnionType) UnionType {
const type_union = ip.extraDataTrail(Tag.TypeUnion, key.extra_index);
const enum_ty = type_union.data.tag_ty;
const enum_info = ip.indexToKey(enum_ty).enum_type;
const fields_len: u32 = @intCast(enum_info.names.len);
return .{
.decl = type_union.data.decl,
.namespace = type_union.data.namespace,
.enum_tag_ty = enum_ty,
.int_tag_ty = enum_info.tag_ty,
.field_names = enum_info.names,
.names_map = enum_info.names_map,
.field_types = .{
.start = type_union.end,
.len = fields_len,
},
.field_aligns = .{
.start = type_union.end + fields_len,
.len = if (type_union.data.flags.any_aligned_fields) fields_len else 0,
},
.zir_index = type_union.data.zir_index,
.flags_index = key.extra_index + std.meta.fieldIndex(Tag.TypeUnion, "flags").?,
};
}
pub const Item = struct { pub const Item = struct {
tag: Tag, tag: Tag,
/// The doc comments on the respective Tag explain how to interpret this. /// The doc comments on the respective Tag explain how to interpret this.
@ -1618,9 +1818,7 @@ pub const Index = enum(u32) {
type_struct_ns: struct { data: Module.Namespace.Index }, type_struct_ns: struct { data: Module.Namespace.Index },
type_struct_anon: DataIsExtraIndexOfTypeStructAnon, type_struct_anon: DataIsExtraIndexOfTypeStructAnon,
type_tuple_anon: DataIsExtraIndexOfTypeStructAnon, type_tuple_anon: DataIsExtraIndexOfTypeStructAnon,
type_union_tagged: struct { data: Module.Union.Index }, type_union: struct { data: *Tag.TypeUnion },
type_union_untagged: struct { data: Module.Union.Index },
type_union_safety: struct { data: Module.Union.Index },
type_function: struct { type_function: struct {
const @"data.flags.has_comptime_bits" = opaque {}; const @"data.flags.has_comptime_bits" = opaque {};
const @"data.flags.has_noalias_bits" = opaque {}; const @"data.flags.has_noalias_bits" = opaque {};
@ -2057,15 +2255,9 @@ pub const Tag = enum(u8) {
/// An AnonStructType which has only types and values for fields. /// An AnonStructType which has only types and values for fields.
/// data is extra index of `TypeStructAnon`. /// data is extra index of `TypeStructAnon`.
type_tuple_anon, type_tuple_anon,
/// A tagged union type. /// A union type.
/// `data` is `Module.Union.Index`. /// `data` is extra index of `TypeUnion`.
type_union_tagged, type_union,
/// An untagged union type. It also has no safety tag.
/// `data` is `Module.Union.Index`.
type_union_untagged,
/// An untagged union type which has a safety tag.
/// `data` is `Module.Union.Index`.
type_union_safety,
/// A function body type. /// A function body type.
/// `data` is extra index to `TypeFunction`. /// `data` is extra index to `TypeFunction`.
type_function, type_function,
@ -2273,9 +2465,7 @@ pub const Tag = enum(u8) {
.type_struct_ns => unreachable, .type_struct_ns => unreachable,
.type_struct_anon => TypeStructAnon, .type_struct_anon => TypeStructAnon,
.type_tuple_anon => TypeStructAnon, .type_tuple_anon => TypeStructAnon,
.type_union_tagged => unreachable, .type_union => TypeUnion,
.type_union_untagged => unreachable,
.type_union_safety => unreachable,
.type_function => TypeFunction, .type_function => TypeFunction,
.undef => unreachable, .undef => unreachable,
@ -2425,6 +2615,30 @@ pub const Tag = enum(u8) {
_: u9 = 0, _: u9 = 0,
}; };
}; };
/// The number of fields is provided by the `tag_ty` field.
/// Trailing:
/// 0. field type: Index for each field; declaration order
/// 1. field align: Alignment for each field; declaration order
pub const TypeUnion = struct {
flags: Flags,
decl: Module.Decl.Index,
namespace: Module.Namespace.Index,
/// The enum that provides the list of field names and values.
tag_ty: Index,
zir_index: Zir.Inst.Index,
pub const Flags = packed struct(u32) {
runtime_tag: UnionType.RuntimeTag,
/// If false, the field alignment trailing data is omitted.
any_aligned_fields: bool,
layout: std.builtin.Type.ContainerLayout,
status: UnionType.Status,
requires_comptime: UnionType.RequiresComptime,
assumed_runtime_bits: bool,
_: u21 = 0,
};
};
}; };
/// State that is mutable during semantic analysis. This data is not used for /// State that is mutable during semantic analysis. This data is not used for
@ -2582,6 +2796,21 @@ pub const Alignment = enum(u6) {
assert(lhs != .none and rhs != .none); assert(lhs != .none and rhs != .none);
return std.math.order(@intFromEnum(lhs), @intFromEnum(rhs)); return std.math.order(@intFromEnum(lhs), @intFromEnum(rhs));
} }
/// An array of `Alignment` objects existing within the `extra` array.
/// This type exists to provide a struct with lifetime that is
/// not invalidated when items are added to the `InternPool`.
pub const Slice = struct {
start: u32,
len: u32,
pub fn get(slice: Slice, ip: *const InternPool) []Alignment {
// TODO: implement @ptrCast between slices changing the length
//const bytes: []u8 = @ptrCast(ip.extra.items[slice.start..]);
const bytes: []u8 = std.mem.sliceAsBytes(ip.extra.items[slice.start..]);
return @ptrCast(bytes[0..slice.len]);
}
};
}; };
/// Used for non-sentineled arrays that have length fitting in u32, as well as /// Used for non-sentineled arrays that have length fitting in u32, as well as
@ -2829,9 +3058,6 @@ pub fn deinit(ip: *InternPool, gpa: Allocator) void {
ip.structs_free_list.deinit(gpa); ip.structs_free_list.deinit(gpa);
ip.allocated_structs.deinit(gpa); ip.allocated_structs.deinit(gpa);
ip.unions_free_list.deinit(gpa);
ip.allocated_unions.deinit(gpa);
ip.decls_free_list.deinit(gpa); ip.decls_free_list.deinit(gpa);
ip.allocated_decls.deinit(gpa); ip.allocated_decls.deinit(gpa);
@ -2953,18 +3179,7 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
} }; } };
}, },
.type_union_untagged => .{ .union_type = .{ .type_union => .{ .union_type = extraUnionType(ip, data) },
.index = @as(Module.Union.Index, @enumFromInt(data)),
.runtime_tag = .none,
} },
.type_union_tagged => .{ .union_type = .{
.index = @as(Module.Union.Index, @enumFromInt(data)),
.runtime_tag = .tagged,
} },
.type_union_safety => .{ .union_type = .{
.index = @as(Module.Union.Index, @enumFromInt(data)),
.runtime_tag = .safety,
} },
.type_enum_auto => { .type_enum_auto => {
const enum_auto = ip.extraDataTrail(EnumAuto, data); const enum_auto = ip.extraDataTrail(EnumAuto, data);
@ -3279,9 +3494,7 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
.type_enum_auto, .type_enum_auto,
.type_enum_explicit, .type_enum_explicit,
.type_union_tagged, .type_union,
.type_union_untagged,
.type_union_safety,
=> .{ .empty_enum_value = ty }, => .{ .empty_enum_value = ty },
else => unreachable, else => unreachable,
@ -3352,6 +3565,18 @@ fn extraErrorSet(ip: *const InternPool, extra_index: u32) Key.ErrorSetType {
}; };
} }
fn extraUnionType(ip: *const InternPool, extra_index: u32) Key.UnionType {
const type_union = ip.extraData(Tag.TypeUnion, extra_index);
return .{
.decl = type_union.decl,
.namespace = type_union.namespace,
.flags = type_union.flags,
.enum_tag_ty = type_union.tag_ty,
.zir_index = type_union.zir_index,
.extra_index = extra_index,
};
}
fn extraFuncType(ip: *const InternPool, extra_index: u32) Key.FuncType { fn extraFuncType(ip: *const InternPool, extra_index: u32) Key.FuncType {
const type_function = ip.extraDataTrail(Tag.TypeFunction, extra_index); const type_function = ip.extraDataTrail(Tag.TypeFunction, extra_index);
var index: usize = type_function.end; var index: usize = type_function.end;
@ -3678,16 +3903,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
return @enumFromInt(ip.items.len - 1); return @enumFromInt(ip.items.len - 1);
}, },
.union_type => |union_type| { .union_type => unreachable, // use getUnionType() instead
ip.items.appendAssumeCapacity(.{
.tag = switch (union_type.runtime_tag) {
.none => .type_union_untagged,
.safety => .type_union_safety,
.tagged => .type_union_tagged,
},
.data = @intFromEnum(union_type.index),
});
},
.opaque_type => |opaque_type| { .opaque_type => |opaque_type| {
ip.items.appendAssumeCapacity(.{ ip.items.appendAssumeCapacity(.{
@ -3791,9 +4007,10 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
assert(ptr.addr == .field); assert(ptr.addr == .field);
assert(base_index.index < ip.structPtrUnwrapConst(struct_type.index).?.fields.count()); assert(base_index.index < ip.structPtrUnwrapConst(struct_type.index).?.fields.count());
}, },
.union_type => |union_type| { .union_type => |union_key| {
const union_type = ip.loadUnionType(union_key);
assert(ptr.addr == .field); assert(ptr.addr == .field);
assert(base_index.index < ip.unionPtrConst(union_type.index).fields.count()); assert(base_index.index < union_type.field_names.len);
}, },
.ptr_type => |slice_type| { .ptr_type => |slice_type| {
assert(ptr.addr == .field); assert(ptr.addr == .field);
@ -4359,6 +4576,76 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
return @enumFromInt(ip.items.len - 1); return @enumFromInt(ip.items.len - 1);
} }
pub const UnionTypeInit = struct {
flags: Tag.TypeUnion.Flags,
decl: Module.Decl.Index,
namespace: Module.Namespace.Index,
zir_index: Zir.Inst.Index,
fields_len: u32,
enum_tag_ty: Index,
/// May have length 0 which leaves the values unset until later.
field_types: []const Index,
/// May have length 0 which leaves the values unset until later.
/// The logic for `any_aligned_fields` is asserted to have been done before
/// calling this function.
field_aligns: []const Alignment,
};
pub fn getUnionType(ip: *InternPool, gpa: Allocator, ini: UnionTypeInit) Allocator.Error!Index {
const prev_extra_len = ip.extra.items.len;
const align_elements_len = if (ini.flags.any_aligned_fields) (ini.fields_len + 3) / 4 else 0;
const align_element: u32 = @bitCast([1]u8{@intFromEnum(Alignment.none)} ** 4);
try ip.extra.ensureUnusedCapacity(gpa, @typeInfo(Tag.TypeUnion).Struct.fields.len +
ini.fields_len + // field types
align_elements_len);
try ip.items.ensureUnusedCapacity(gpa, 1);
const union_type_extra_index = ip.addExtraAssumeCapacity(Tag.TypeUnion{
.flags = ini.flags,
.decl = ini.decl,
.namespace = ini.namespace,
.tag_ty = ini.enum_tag_ty,
.zir_index = ini.zir_index,
});
// field types
if (ini.field_types.len > 0) {
assert(ini.field_types.len == ini.fields_len);
ip.extra.appendSliceAssumeCapacity(@ptrCast(ini.field_types));
} else {
ip.extra.appendNTimesAssumeCapacity(@intFromEnum(Index.none), ini.fields_len);
}
// field alignments
if (ini.flags.any_aligned_fields) {
ip.extra.appendNTimesAssumeCapacity(align_element, align_elements_len);
if (ini.field_aligns.len > 0) {
assert(ini.field_aligns.len == ini.fields_len);
@memcpy((Alignment.Slice{
.start = @intCast(ip.extra.items.len - align_elements_len),
.len = @intCast(ini.field_aligns.len),
}).get(ip), ini.field_aligns);
}
} else {
assert(ini.field_aligns.len == 0);
}
const adapter: KeyAdapter = .{ .intern_pool = ip };
const gop = try ip.map.getOrPutAdapted(gpa, Key{
.union_type = extraUnionType(ip, union_type_extra_index),
}, adapter);
if (gop.found_existing) {
ip.extra.items.len = prev_extra_len;
return @enumFromInt(gop.index);
}
ip.items.appendAssumeCapacity(.{
.tag = .type_union,
.data = union_type_extra_index,
});
return @enumFromInt(ip.items.len - 1);
}
/// This is equivalent to `Key.FuncType` but adjusted to have a slice for `param_types`. /// This is equivalent to `Key.FuncType` but adjusted to have a slice for `param_types`.
pub const GetFuncTypeKey = struct { pub const GetFuncTypeKey = struct {
param_types: []Index, param_types: []Index,
@ -5310,6 +5597,7 @@ fn addExtraAssumeCapacity(ip: *InternPool, extra: anytype) u32 {
Tag.TypeFunction.Flags, Tag.TypeFunction.Flags,
Tag.TypePointer.PackedOffset, Tag.TypePointer.PackedOffset,
Tag.Variable.Flags, Tag.Variable.Flags,
Tag.TypeUnion.Flags,
=> @bitCast(@field(extra, field.name)), => @bitCast(@field(extra, field.name)),
else => @compileError("bad field type: " ++ @typeName(field.type)), else => @compileError("bad field type: " ++ @typeName(field.type)),
@ -5380,6 +5668,7 @@ fn extraDataTrail(ip: *const InternPool, comptime T: type, index: usize) struct
Tag.TypePointer.Flags, Tag.TypePointer.Flags,
Tag.TypeFunction.Flags, Tag.TypeFunction.Flags,
Tag.TypePointer.PackedOffset, Tag.TypePointer.PackedOffset,
Tag.TypeUnion.Flags,
Tag.Variable.Flags, Tag.Variable.Flags,
FuncAnalysis, FuncAnalysis,
=> @bitCast(int32), => @bitCast(int32),
@ -5893,7 +6182,7 @@ pub fn indexToUnionType(ip: *const InternPool, val: Index) Module.Union.Optional
assert(val != .none); assert(val != .none);
const tags = ip.items.items(.tag); const tags = ip.items.items(.tag);
switch (tags[@intFromEnum(val)]) { switch (tags[@intFromEnum(val)]) {
.type_union_tagged, .type_union_untagged, .type_union_safety => {}, .type_union => {},
else => return .none, else => return .none,
} }
const datas = ip.items.items(.data); const datas = ip.items.items(.data);
@ -5946,6 +6235,10 @@ pub fn isEnumType(ip: *const InternPool, ty: Index) bool {
}; };
} }
pub fn isUnion(ip: *const InternPool, ty: Index) bool {
return ip.indexToKey(ty) == .union_type;
}
pub fn isFunctionType(ip: *const InternPool, ty: Index) bool { pub fn isFunctionType(ip: *const InternPool, ty: Index) bool {
return ip.indexToKey(ty) == .func_type; return ip.indexToKey(ty) == .func_type;
} }
@ -6010,13 +6303,11 @@ fn dumpStatsFallible(ip: *const InternPool, arena: Allocator) anyerror!void {
const limbs_size = 8 * ip.limbs.items.len; const limbs_size = 8 * ip.limbs.items.len;
// TODO: fields size is not taken into account // TODO: fields size is not taken into account
const structs_size = ip.allocated_structs.len * const structs_size = ip.allocated_structs.len *
(@sizeOf(Module.Struct) + @sizeOf(Module.Namespace) + @sizeOf(Module.Decl)); (@sizeOf(Module.Struct) + @sizeOf(Module.Namespace));
const unions_size = ip.allocated_unions.len * const decls_size = ip.allocated_decls.len * @sizeOf(Module.Decl);
(@sizeOf(Module.Union) + @sizeOf(Module.Namespace) + @sizeOf(Module.Decl));
// TODO: map overhead size is not taken into account // TODO: map overhead size is not taken into account
const total_size = @sizeOf(InternPool) + items_size + extra_size + limbs_size + const total_size = @sizeOf(InternPool) + items_size + extra_size + limbs_size + structs_size + decls_size;
structs_size + unions_size;
std.debug.print( std.debug.print(
\\InternPool size: {d} bytes \\InternPool size: {d} bytes
@ -6024,7 +6315,7 @@ fn dumpStatsFallible(ip: *const InternPool, arena: Allocator) anyerror!void {
\\ {d} extra: {d} bytes \\ {d} extra: {d} bytes
\\ {d} limbs: {d} bytes \\ {d} limbs: {d} bytes
\\ {d} structs: {d} bytes \\ {d} structs: {d} bytes
\\ {d} unions: {d} bytes \\ {d} decls: {d} bytes
\\ \\
, .{ , .{
total_size, total_size,
@ -6036,8 +6327,8 @@ fn dumpStatsFallible(ip: *const InternPool, arena: Allocator) anyerror!void {
limbs_size, limbs_size,
ip.allocated_structs.len, ip.allocated_structs.len,
structs_size, structs_size,
ip.allocated_unions.len, ip.allocated_decls.len,
unions_size, decls_size,
}); });
const tags = ip.items.items(.tag); const tags = ip.items.items(.tag);
@ -6076,7 +6367,6 @@ fn dumpStatsFallible(ip: *const InternPool, arena: Allocator) anyerror!void {
const struct_obj = ip.structPtrConst(struct_index); const struct_obj = ip.structPtrConst(struct_index);
break :b @sizeOf(Module.Struct) + break :b @sizeOf(Module.Struct) +
@sizeOf(Module.Namespace) + @sizeOf(Module.Namespace) +
@sizeOf(Module.Decl) +
(struct_obj.fields.count() * @sizeOf(Module.Struct.Field)); (struct_obj.fields.count() * @sizeOf(Module.Struct.Field));
}, },
.type_struct_ns => @sizeOf(Module.Namespace), .type_struct_ns => @sizeOf(Module.Namespace),
@ -6089,10 +6379,18 @@ fn dumpStatsFallible(ip: *const InternPool, arena: Allocator) anyerror!void {
break :b @sizeOf(TypeStructAnon) + (@sizeOf(u32) * 2 * info.fields_len); break :b @sizeOf(TypeStructAnon) + (@sizeOf(u32) * 2 * info.fields_len);
}, },
.type_union_tagged, .type_union => b: {
.type_union_untagged, const info = ip.extraData(Tag.TypeUnion, data);
.type_union_safety, const enum_info = ip.indexToKey(info.tag_ty).enum_type;
=> @sizeOf(Module.Union) + @sizeOf(Module.Namespace) + @sizeOf(Module.Decl), const fields_len: u32 = @intCast(enum_info.names.len);
const per_field = @sizeOf(u32); // field type
// 1 byte per field for alignment, rounded up to the nearest 4 bytes
const alignments = if (info.flags.any_aligned_fields)
((fields_len + 3) / 4) * 4
else
0;
break :b @sizeOf(Tag.TypeUnion) + (fields_len * per_field) + alignments;
},
.type_function => b: { .type_function => b: {
const info = ip.extraData(Tag.TypeFunction, data); const info = ip.extraData(Tag.TypeFunction, data);
@ -6161,15 +6459,14 @@ fn dumpStatsFallible(ip: *const InternPool, arena: Allocator) anyerror!void {
.float_c_longdouble_f80 => @sizeOf(Float80), .float_c_longdouble_f80 => @sizeOf(Float80),
.float_c_longdouble_f128 => @sizeOf(Float128), .float_c_longdouble_f128 => @sizeOf(Float128),
.float_comptime_float => @sizeOf(Float128), .float_comptime_float => @sizeOf(Float128),
.variable => @sizeOf(Tag.Variable) + @sizeOf(Module.Decl), .variable => @sizeOf(Tag.Variable),
.extern_func => @sizeOf(Tag.ExternFunc) + @sizeOf(Module.Decl), .extern_func => @sizeOf(Tag.ExternFunc),
.func_decl => @sizeOf(Tag.FuncDecl) + @sizeOf(Module.Decl), .func_decl => @sizeOf(Tag.FuncDecl),
.func_instance => b: { .func_instance => b: {
const info = ip.extraData(Tag.FuncInstance, data); const info = ip.extraData(Tag.FuncInstance, data);
const ty = ip.typeOf(info.generic_owner); const ty = ip.typeOf(info.generic_owner);
const params_len = ip.indexToKey(ty).func_type.param_types.len; const params_len = ip.indexToKey(ty).func_type.param_types.len;
break :b @sizeOf(Tag.FuncInstance) + @sizeOf(Index) * params_len + break :b @sizeOf(Tag.FuncInstance) + @sizeOf(Index) * params_len;
@sizeOf(Module.Decl);
}, },
.func_coerced => @sizeOf(Tag.FuncCoerced), .func_coerced => @sizeOf(Tag.FuncCoerced),
.only_possible_value => 0, .only_possible_value => 0,
@ -6230,9 +6527,7 @@ fn dumpAllFallible(ip: *const InternPool) anyerror!void {
.type_struct_ns, .type_struct_ns,
.type_struct_anon, .type_struct_anon,
.type_tuple_anon, .type_tuple_anon,
.type_union_tagged, .type_union,
.type_union_untagged,
.type_union_safety,
.type_function, .type_function,
.undef, .undef,
.runtime_value, .runtime_value,
@ -6358,14 +6653,6 @@ pub fn structPtrUnwrapConst(ip: *const InternPool, index: Module.Struct.Optional
return structPtrConst(ip, index.unwrap() orelse return null); return structPtrConst(ip, index.unwrap() orelse return null);
} }
pub fn unionPtr(ip: *InternPool, index: Module.Union.Index) *Module.Union {
return ip.allocated_unions.at(@intFromEnum(index));
}
pub fn unionPtrConst(ip: *const InternPool, index: Module.Union.Index) *const Module.Union {
return ip.allocated_unions.at(@intFromEnum(index));
}
pub fn declPtr(ip: *InternPool, index: Module.Decl.Index) *Module.Decl { pub fn declPtr(ip: *InternPool, index: Module.Decl.Index) *Module.Decl {
return ip.allocated_decls.at(@intFromEnum(index)); return ip.allocated_decls.at(@intFromEnum(index));
} }
@ -6400,28 +6687,6 @@ pub fn destroyStruct(ip: *InternPool, gpa: Allocator, index: Module.Struct.Index
}; };
} }
pub fn createUnion(
ip: *InternPool,
gpa: Allocator,
initialization: Module.Union,
) Allocator.Error!Module.Union.Index {
if (ip.unions_free_list.popOrNull()) |index| {
ip.allocated_unions.at(@intFromEnum(index)).* = initialization;
return index;
}
const ptr = try ip.allocated_unions.addOne(gpa);
ptr.* = initialization;
return @enumFromInt(ip.allocated_unions.len - 1);
}
pub fn destroyUnion(ip: *InternPool, gpa: Allocator, index: Module.Union.Index) void {
ip.unionPtr(index).* = undefined;
ip.unions_free_list.append(gpa, index) catch {
// In order to keep `destroyUnion` a non-fallible function, we ignore memory
// allocation failures here, instead leaking the Union until garbage collection.
};
}
pub fn createDecl( pub fn createDecl(
ip: *InternPool, ip: *InternPool,
gpa: Allocator, gpa: Allocator,
@ -6667,9 +6932,7 @@ pub fn typeOf(ip: *const InternPool, index: Index) Index {
.type_struct_ns, .type_struct_ns,
.type_struct_anon, .type_struct_anon,
.type_tuple_anon, .type_tuple_anon,
.type_union_tagged, .type_union,
.type_union_untagged,
.type_union_safety,
.type_function, .type_function,
=> .type_type, => .type_type,
@ -7005,10 +7268,7 @@ pub fn zigTypeTagOrPoison(ip: *const InternPool, index: Index) error{GenericPois
.type_tuple_anon, .type_tuple_anon,
=> .Struct, => .Struct,
.type_union_tagged, .type_union => .Union,
.type_union_untagged,
.type_union_safety,
=> .Union,
.type_function => .Fn, .type_function => .Fn,

View File

@ -96,7 +96,7 @@ intern_pool: InternPool = .{},
/// Current uses that must be eliminated: /// Current uses that must be eliminated:
/// * Struct comptime_args /// * Struct comptime_args
/// * Struct optimized_order /// * Struct optimized_order
/// * Union fields /// * comptime pointer mutation
/// This memory lives until the Module is destroyed. /// This memory lives until the Module is destroyed.
tmp_hack_arena: std.heap.ArenaAllocator, 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, /// If the Decl owns its value and it is a union, return it,
/// otherwise null. /// 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.owns_tv) return null;
if (decl.val.ip_index == .none) return null; if (decl.val.ip_index == .none) return null;
return mod.typeToUnion(decl.val.toType()); return mod.typeToUnion(decl.val.toType());
@ -778,7 +778,7 @@ pub const Decl = struct {
else => switch (mod.intern_pool.indexToKey(decl.val.toIntern())) { else => switch (mod.intern_pool.indexToKey(decl.val.toIntern())) {
.opaque_type => |opaque_type| opaque_type.namespace.toOptional(), .opaque_type => |opaque_type| opaque_type.namespace.toOptional(),
.struct_type => |struct_type| struct_type.namespace, .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, .enum_type => |enum_type| enum_type.namespace,
else => .none, 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 { pub const DeclAdapter = struct {
mod: *Module, mod: *Module,
@ -3182,10 +2942,6 @@ pub fn namespacePtr(mod: *Module, index: Namespace.Index) *Namespace {
return mod.intern_pool.namespacePtr(index); 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 { pub fn structPtr(mod: *Module, index: Struct.Index) *Struct {
return mod.intern_pool.structPtr(index); 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| { if (decl.getOwnedUnion(mod)) |union_type| {
union_obj.zir_index = inst_map.get(union_obj.zir_index) orelse { union_type.setZirIndex(ip, inst_map.get(union_type.zir_index) orelse {
try file.deleted_decls.append(gpa, decl_index); try file.deleted_decls.append(gpa, decl_index);
continue; continue;
}; });
} }
if (decl.getOwnedFunction(mod)) |func| { 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); 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( pub fn allocateNewDecl(
mod: *Module, mod: *Module,
namespace: Namespace.Index, namespace: Namespace.Index,
@ -6956,10 +6704,14 @@ pub fn typeToStruct(mod: *Module, ty: Type) ?*Struct {
return mod.structPtr(struct_index); 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; if (ty.ip_index == .none) return null;
const union_index = mod.intern_pool.indexToUnionType(ty.toIntern()).unwrap() orelse return null; const ip = &mod.intern_pool;
return mod.unionPtr(union_index); 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 { 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, 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());
}

File diff suppressed because it is too large Load Diff

View File

@ -88,7 +88,7 @@ pub fn print(
try writer.writeAll(".{ "); try writer.writeAll(".{ ");
try print(.{ try print(.{
.ty = mod.unionPtr(ip.indexToKey(ty.toIntern()).union_type.index).tag_ty, .ty = ip.indexToKey(ty.toIntern()).union_type.enum_tag_ty.toType(),
.val = union_val.tag, .val = union_val.tag,
}, writer, level - 1, mod); }, writer, level - 1, mod);
try writer.writeAll(" = "); try writer.writeAll(" = ");
@ -357,7 +357,7 @@ pub fn print(
try writer.print(".{i}", .{field_name.fmt(ip)}); try writer.print(".{i}", .{field_name.fmt(ip)});
}, },
.Union => { .Union => {
const field_name = container_ty.unionFields(mod).keys()[@as(usize, @intCast(field.index))]; const field_name = mod.typeToUnion(container_ty).?.field_names.get(ip)[@intCast(field.index)];
try writer.print(".{i}", .{field_name.fmt(ip)}); try writer.print(".{i}", .{field_name.fmt(ip)});
}, },
.Pointer => { .Pointer => {

View File

@ -2956,7 +2956,8 @@ pub const Inst = struct {
/// true | true | union(enum(T)) { } /// true | true | union(enum(T)) { }
/// true | false | union(T) { } /// true | false | union(T) { }
auto_enum_tag: bool, auto_enum_tag: bool,
_: u6 = undefined, any_aligned_fields: bool,
_: u5 = undefined,
}; };
}; };

View File

@ -75,14 +75,15 @@ pub fn classifyType(ty: Type, mod: *Module) Class {
const sret_float_count = 4; const sret_float_count = 4;
fn countFloats(ty: Type, mod: *Module, maybe_float_bits: *?u16) u8 { fn countFloats(ty: Type, mod: *Module, maybe_float_bits: *?u16) u8 {
const ip = &mod.intern_pool;
const target = mod.getTarget(); const target = mod.getTarget();
const invalid = std.math.maxInt(u8); const invalid = std.math.maxInt(u8);
switch (ty.zigTypeTag(mod)) { switch (ty.zigTypeTag(mod)) {
.Union => { .Union => {
const fields = ty.unionFields(mod); const union_obj = mod.typeToUnion(ty).?;
var max_count: u8 = 0; var max_count: u8 = 0;
for (fields.values()) |field| { for (union_obj.field_types.get(ip)) |field_ty| {
const field_count = countFloats(field.ty, mod, maybe_float_bits); const field_count = countFloats(field_ty.toType(), mod, maybe_float_bits);
if (field_count == invalid) return invalid; if (field_count == invalid) return invalid;
if (field_count > max_count) max_count = field_count; if (field_count > max_count) max_count = field_count;
if (max_count > sret_float_count) return invalid; if (max_count > sret_float_count) return invalid;
@ -116,11 +117,12 @@ fn countFloats(ty: Type, mod: *Module, maybe_float_bits: *?u16) u8 {
} }
pub fn getFloatArrayType(ty: Type, mod: *Module) ?Type { pub fn getFloatArrayType(ty: Type, mod: *Module) ?Type {
const ip = &mod.intern_pool;
switch (ty.zigTypeTag(mod)) { switch (ty.zigTypeTag(mod)) {
.Union => { .Union => {
const fields = ty.unionFields(mod); const union_obj = mod.typeToUnion(ty).?;
for (fields.values()) |field| { for (union_obj.field_types.get(ip)) |field_ty| {
if (getFloatArrayType(field.ty, mod)) |some| return some; if (getFloatArrayType(field_ty.toType(), mod)) |some| return some;
} }
return null; return null;
}, },

View File

@ -29,6 +29,7 @@ pub fn classifyType(ty: Type, mod: *Module, ctx: Context) Class {
var maybe_float_bits: ?u16 = null; var maybe_float_bits: ?u16 = null;
const max_byval_size = 512; const max_byval_size = 512;
const ip = &mod.intern_pool;
switch (ty.zigTypeTag(mod)) { switch (ty.zigTypeTag(mod)) {
.Struct => { .Struct => {
const bit_size = ty.bitSize(mod); const bit_size = ty.bitSize(mod);
@ -54,7 +55,8 @@ pub fn classifyType(ty: Type, mod: *Module, ctx: Context) Class {
}, },
.Union => { .Union => {
const bit_size = ty.bitSize(mod); const bit_size = ty.bitSize(mod);
if (ty.containerLayout(mod) == .Packed) { const union_obj = mod.typeToUnion(ty).?;
if (union_obj.getLayout(ip) == .Packed) {
if (bit_size > 64) return .memory; if (bit_size > 64) return .memory;
return .byval; return .byval;
} }
@ -62,8 +64,10 @@ pub fn classifyType(ty: Type, mod: *Module, ctx: Context) Class {
const float_count = countFloats(ty, mod, &maybe_float_bits); const float_count = countFloats(ty, mod, &maybe_float_bits);
if (float_count <= byval_float_count) return .byval; if (float_count <= byval_float_count) return .byval;
for (ty.unionFields(mod).values()) |field| { for (union_obj.field_types.get(ip), 0..) |field_ty, field_index| {
if (field.ty.bitSize(mod) > 32 or field.normalAlignment(mod) > 32) { if (field_ty.toType().bitSize(mod) > 32 or
mod.unionFieldNormalAlignment(union_obj, @intCast(field_index)) > 32)
{
return Class.arrSize(bit_size, 64); return Class.arrSize(bit_size, 64);
} }
} }
@ -117,14 +121,15 @@ pub fn classifyType(ty: Type, mod: *Module, ctx: Context) Class {
const byval_float_count = 4; const byval_float_count = 4;
fn countFloats(ty: Type, mod: *Module, maybe_float_bits: *?u16) u32 { fn countFloats(ty: Type, mod: *Module, maybe_float_bits: *?u16) u32 {
const ip = &mod.intern_pool;
const target = mod.getTarget(); const target = mod.getTarget();
const invalid = std.math.maxInt(u32); const invalid = std.math.maxInt(u32);
switch (ty.zigTypeTag(mod)) { switch (ty.zigTypeTag(mod)) {
.Union => { .Union => {
const fields = ty.unionFields(mod); const union_obj = mod.typeToUnion(ty).?;
var max_count: u32 = 0; var max_count: u32 = 0;
for (fields.values()) |field| { for (union_obj.field_types.get(ip)) |field_ty| {
const field_count = countFloats(field.ty, mod, maybe_float_bits); const field_count = countFloats(field_ty.toType(), mod, maybe_float_bits);
if (field_count == invalid) return invalid; if (field_count == invalid) return invalid;
if (field_count > max_count) max_count = field_count; if (field_count > max_count) max_count = field_count;
if (max_count > byval_float_count) return invalid; if (max_count > byval_float_count) return invalid;

View File

@ -1717,6 +1717,7 @@ fn arch(func: *const CodeGen) std.Target.Cpu.Arch {
/// For a given `Type`, will return true when the type will be passed /// For a given `Type`, will return true when the type will be passed
/// by reference, rather than by value /// by reference, rather than by value
fn isByRef(ty: Type, mod: *Module) bool { fn isByRef(ty: Type, mod: *Module) bool {
const ip = &mod.intern_pool;
const target = mod.getTarget(); const target = mod.getTarget();
switch (ty.zigTypeTag(mod)) { switch (ty.zigTypeTag(mod)) {
.Type, .Type,
@ -1742,7 +1743,7 @@ fn isByRef(ty: Type, mod: *Module) bool {
=> return ty.hasRuntimeBitsIgnoreComptime(mod), => return ty.hasRuntimeBitsIgnoreComptime(mod),
.Union => { .Union => {
if (mod.typeToUnion(ty)) |union_obj| { if (mod.typeToUnion(ty)) |union_obj| {
if (union_obj.layout == .Packed) { if (union_obj.getLayout(ip) == .Packed) {
return ty.abiSize(mod) > 8; return ty.abiSize(mod) > 8;
} }
} }
@ -2974,7 +2975,7 @@ fn lowerParentPtr(func: *CodeGen, ptr_val: Value, offset: u32) InnerError!WValue
.Union => switch (parent_ty.containerLayout(mod)) { .Union => switch (parent_ty.containerLayout(mod)) {
.Packed => 0, .Packed => 0,
else => blk: { else => blk: {
const layout: Module.Union.Layout = parent_ty.unionGetLayout(mod); const layout: Module.UnionLayout = parent_ty.unionGetLayout(mod);
if (layout.payload_size == 0) break :blk 0; if (layout.payload_size == 0) break :blk 0;
if (layout.payload_align > layout.tag_align) break :blk 0; if (layout.payload_align > layout.tag_align) break :blk 0;
@ -3058,8 +3059,9 @@ fn toTwosComplement(value: anytype, bits: u7) std.meta.Int(.unsigned, @typeInfo(
fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue { fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue {
const mod = func.bin_file.base.options.module.?; const mod = func.bin_file.base.options.module.?;
const ip = &mod.intern_pool;
var val = arg_val; var val = arg_val;
switch (mod.intern_pool.indexToKey(val.ip_index)) { switch (ip.indexToKey(val.ip_index)) {
.runtime_value => |rt| val = rt.val.toValue(), .runtime_value => |rt| val = rt.val.toValue(),
else => {}, else => {},
} }
@ -3110,7 +3112,7 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue {
=> unreachable, // comptime-only types => unreachable, // comptime-only types
}; };
switch (mod.intern_pool.indexToKey(val.ip_index)) { switch (ip.indexToKey(val.ip_index)) {
.int_type, .int_type,
.ptr_type, .ptr_type,
.array_type, .array_type,
@ -3198,7 +3200,7 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue {
return func.fail("Wasm TODO: lowerConstant error union with non-zero-bit payload type", .{}); return func.fail("Wasm TODO: lowerConstant error union with non-zero-bit payload type", .{});
}, },
.enum_tag => |enum_tag| { .enum_tag => |enum_tag| {
const int_tag_ty = mod.intern_pool.typeOf(enum_tag.int); const int_tag_ty = ip.typeOf(enum_tag.int);
return func.lowerConstant(enum_tag.int.toValue(), int_tag_ty.toType()); return func.lowerConstant(enum_tag.int.toValue(), int_tag_ty.toType());
}, },
.float => |float| switch (float.storage) { .float => |float| switch (float.storage) {
@ -3210,7 +3212,7 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue {
.ptr => |ptr| switch (ptr.addr) { .ptr => |ptr| switch (ptr.addr) {
.decl => |decl| return func.lowerDeclRefValue(.{ .ty = ty, .val = val }, decl, 0), .decl => |decl| return func.lowerDeclRefValue(.{ .ty = ty, .val = val }, decl, 0),
.mut_decl => |mut_decl| return func.lowerDeclRefValue(.{ .ty = ty, .val = val }, mut_decl.decl, 0), .mut_decl => |mut_decl| return func.lowerDeclRefValue(.{ .ty = ty, .val = val }, mut_decl.decl, 0),
.int => |int| return func.lowerConstant(int.toValue(), mod.intern_pool.typeOf(int).toType()), .int => |int| return func.lowerConstant(int.toValue(), ip.typeOf(int).toType()),
.opt_payload, .elem, .field => return func.lowerParentPtr(val, 0), .opt_payload, .elem, .field => return func.lowerParentPtr(val, 0),
else => return func.fail("Wasm TODO: lowerConstant for other const addr tag {}", .{ptr.addr}), else => return func.fail("Wasm TODO: lowerConstant for other const addr tag {}", .{ptr.addr}),
}, },
@ -3224,7 +3226,7 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue {
} else { } else {
return WValue{ .imm32 = @intFromBool(!val.isNull(mod)) }; return WValue{ .imm32 = @intFromBool(!val.isNull(mod)) };
}, },
.aggregate => switch (mod.intern_pool.indexToKey(ty.ip_index)) { .aggregate => switch (ip.indexToKey(ty.ip_index)) {
.array_type => return func.fail("Wasm TODO: LowerConstant for {}", .{ty.fmt(mod)}), .array_type => return func.fail("Wasm TODO: LowerConstant for {}", .{ty.fmt(mod)}),
.vector_type => { .vector_type => {
assert(determineSimdStoreStrategy(ty, mod) == .direct); assert(determineSimdStoreStrategy(ty, mod) == .direct);
@ -3245,11 +3247,12 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue {
}, },
else => unreachable, else => unreachable,
}, },
.un => |union_obj| { .un => |un| {
// in this case we have a packed union which will not be passed by reference. // in this case we have a packed union which will not be passed by reference.
const field_index = ty.unionTagFieldIndex(union_obj.tag.toValue(), func.bin_file.base.options.module.?).?; const union_obj = mod.typeToUnion(ty).?;
const field_ty = ty.unionFields(mod).values()[field_index].ty; const field_index = mod.unionTagFieldIndex(union_obj, un.tag.toValue()).?;
return func.lowerConstant(union_obj.val.toValue(), field_ty); const field_ty = union_obj.field_types.get(ip)[field_index].toType();
return func.lowerConstant(un.val.toValue(), field_ty);
}, },
.memoized_call => unreachable, .memoized_call => unreachable,
} }
@ -5163,6 +5166,7 @@ fn airAggregateInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
fn airUnionInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { fn airUnionInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const mod = func.bin_file.base.options.module.?; const mod = func.bin_file.base.options.module.?;
const ip = &mod.intern_pool;
const ty_pl = func.air.instructions.items(.data)[inst].ty_pl; const ty_pl = func.air.instructions.items(.data)[inst].ty_pl;
const extra = func.air.extraData(Air.UnionInit, ty_pl.payload).data; const extra = func.air.extraData(Air.UnionInit, ty_pl.payload).data;
@ -5170,8 +5174,8 @@ fn airUnionInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const union_ty = func.typeOfIndex(inst); const union_ty = func.typeOfIndex(inst);
const layout = union_ty.unionGetLayout(mod); const layout = union_ty.unionGetLayout(mod);
const union_obj = mod.typeToUnion(union_ty).?; const union_obj = mod.typeToUnion(union_ty).?;
const field = union_obj.fields.values()[extra.field_index]; const field_ty = union_obj.field_types.get(ip)[extra.field_index].toType();
const field_name = union_obj.fields.keys()[extra.field_index]; const field_name = union_obj.field_names.get(ip)[extra.field_index];
const tag_int = blk: { const tag_int = blk: {
const tag_ty = union_ty.unionTagTypeHypothetical(mod); const tag_ty = union_ty.unionTagTypeHypothetical(mod);
@ -5191,24 +5195,24 @@ fn airUnionInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const result_ptr = try func.allocStack(union_ty); const result_ptr = try func.allocStack(union_ty);
const payload = try func.resolveInst(extra.init); const payload = try func.resolveInst(extra.init);
if (layout.tag_align >= layout.payload_align) { if (layout.tag_align >= layout.payload_align) {
if (isByRef(field.ty, mod)) { if (isByRef(field_ty, mod)) {
const payload_ptr = try func.buildPointerOffset(result_ptr, layout.tag_size, .new); const payload_ptr = try func.buildPointerOffset(result_ptr, layout.tag_size, .new);
try func.store(payload_ptr, payload, field.ty, 0); try func.store(payload_ptr, payload, field_ty, 0);
} else { } else {
try func.store(result_ptr, payload, field.ty, @as(u32, @intCast(layout.tag_size))); try func.store(result_ptr, payload, field_ty, @intCast(layout.tag_size));
} }
if (layout.tag_size > 0) { if (layout.tag_size > 0) {
try func.store(result_ptr, tag_int, union_obj.tag_ty, 0); try func.store(result_ptr, tag_int, union_obj.enum_tag_ty.toType(), 0);
} }
} else { } else {
try func.store(result_ptr, payload, field.ty, 0); try func.store(result_ptr, payload, field_ty, 0);
if (layout.tag_size > 0) { if (layout.tag_size > 0) {
try func.store( try func.store(
result_ptr, result_ptr,
tag_int, tag_int,
union_obj.tag_ty, union_obj.enum_tag_ty.toType(),
@as(u32, @intCast(layout.payload_size)), @intCast(layout.payload_size),
); );
} }
} }
@ -5216,18 +5220,18 @@ fn airUnionInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
} else { } else {
const operand = try func.resolveInst(extra.init); const operand = try func.resolveInst(extra.init);
const union_int_type = try mod.intType(.unsigned, @as(u16, @intCast(union_ty.bitSize(mod)))); const union_int_type = try mod.intType(.unsigned, @as(u16, @intCast(union_ty.bitSize(mod))));
if (field.ty.zigTypeTag(mod) == .Float) { if (field_ty.zigTypeTag(mod) == .Float) {
const int_type = try mod.intType(.unsigned, @as(u16, @intCast(field.ty.bitSize(mod)))); const int_type = try mod.intType(.unsigned, @intCast(field_ty.bitSize(mod)));
const bitcasted = try func.bitcast(field.ty, int_type, operand); const bitcasted = try func.bitcast(field_ty, int_type, operand);
const casted = try func.trunc(bitcasted, int_type, union_int_type); const casted = try func.trunc(bitcasted, int_type, union_int_type);
break :result try casted.toLocal(func, field.ty); break :result try casted.toLocal(func, field_ty);
} else if (field.ty.isPtrAtRuntime(mod)) { } else if (field_ty.isPtrAtRuntime(mod)) {
const int_type = try mod.intType(.unsigned, @as(u16, @intCast(field.ty.bitSize(mod)))); const int_type = try mod.intType(.unsigned, @intCast(field_ty.bitSize(mod)));
const casted = try func.intcast(operand, int_type, union_int_type); const casted = try func.intcast(operand, int_type, union_int_type);
break :result try casted.toLocal(func, field.ty); break :result try casted.toLocal(func, field_ty);
} }
const casted = try func.intcast(operand, field.ty, union_int_type); const casted = try func.intcast(operand, field_ty, union_int_type);
break :result try casted.toLocal(func, field.ty); break :result try casted.toLocal(func, field_ty);
} }
}; };

View File

@ -6,6 +6,7 @@
const std = @import("std"); const std = @import("std");
const Target = std.Target; const Target = std.Target;
const assert = std.debug.assert;
const Type = @import("../../type.zig").Type; const Type = @import("../../type.zig").Type;
const Module = @import("../../Module.zig"); const Module = @import("../../Module.zig");
@ -22,6 +23,7 @@ const direct: [2]Class = .{ .direct, .none };
/// or returned as value within a wasm function. /// or returned as value within a wasm function.
/// When all elements result in `.none`, no value must be passed in or returned. /// When all elements result in `.none`, no value must be passed in or returned.
pub fn classifyType(ty: Type, mod: *Module) [2]Class { pub fn classifyType(ty: Type, mod: *Module) [2]Class {
const ip = &mod.intern_pool;
const target = mod.getTarget(); const target = mod.getTarget();
if (!ty.hasRuntimeBitsIgnoreComptime(mod)) return none; if (!ty.hasRuntimeBitsIgnoreComptime(mod)) return none;
switch (ty.zigTypeTag(mod)) { switch (ty.zigTypeTag(mod)) {
@ -56,22 +58,24 @@ pub fn classifyType(ty: Type, mod: *Module) [2]Class {
.Bool => return direct, .Bool => return direct,
.Array => return memory, .Array => return memory,
.Optional => { .Optional => {
std.debug.assert(ty.isPtrLikeOptional(mod)); assert(ty.isPtrLikeOptional(mod));
return direct; return direct;
}, },
.Pointer => { .Pointer => {
std.debug.assert(!ty.isSlice(mod)); assert(!ty.isSlice(mod));
return direct; return direct;
}, },
.Union => { .Union => {
if (ty.containerLayout(mod) == .Packed) { const union_obj = mod.typeToUnion(ty).?;
if (union_obj.getLayout(ip) == .Packed) {
if (ty.bitSize(mod) <= 64) return direct; if (ty.bitSize(mod) <= 64) return direct;
return .{ .direct, .direct }; return .{ .direct, .direct };
} }
const layout = ty.unionGetLayout(mod); const layout = ty.unionGetLayout(mod);
std.debug.assert(layout.tag_size == 0); assert(layout.tag_size == 0);
if (ty.unionFields(mod).count() > 1) return memory; if (union_obj.field_names.len > 1) return memory;
return classifyType(ty.unionFields(mod).values()[0].ty, mod); const first_field_ty = union_obj.field_types.get(ip)[0].toType();
return classifyType(first_field_ty, mod);
}, },
.ErrorUnion, .ErrorUnion,
.Frame, .Frame,
@ -94,6 +98,7 @@ pub fn classifyType(ty: Type, mod: *Module) [2]Class {
/// Asserts given type can be represented as scalar, such as /// Asserts given type can be represented as scalar, such as
/// a struct with a single scalar field. /// a struct with a single scalar field.
pub fn scalarType(ty: Type, mod: *Module) Type { pub fn scalarType(ty: Type, mod: *Module) Type {
const ip = &mod.intern_pool;
switch (ty.zigTypeTag(mod)) { switch (ty.zigTypeTag(mod)) {
.Struct => { .Struct => {
switch (ty.containerLayout(mod)) { switch (ty.containerLayout(mod)) {
@ -102,20 +107,22 @@ pub fn scalarType(ty: Type, mod: *Module) Type {
return scalarType(struct_obj.backing_int_ty, mod); return scalarType(struct_obj.backing_int_ty, mod);
}, },
else => { else => {
std.debug.assert(ty.structFieldCount(mod) == 1); assert(ty.structFieldCount(mod) == 1);
return scalarType(ty.structFieldType(0, mod), mod); return scalarType(ty.structFieldType(0, mod), mod);
}, },
} }
}, },
.Union => { .Union => {
if (ty.containerLayout(mod) != .Packed) { const union_obj = mod.typeToUnion(ty).?;
const layout = ty.unionGetLayout(mod); if (union_obj.getLayout(ip) != .Packed) {
const layout = mod.getUnionLayout(union_obj);
if (layout.payload_size == 0 and layout.tag_size != 0) { if (layout.payload_size == 0 and layout.tag_size != 0) {
return scalarType(ty.unionTagTypeSafety(mod).?, mod); return scalarType(ty.unionTagTypeSafety(mod).?, mod);
} }
std.debug.assert(ty.unionFields(mod).count() == 1); assert(union_obj.field_types.len == 1);
} }
return scalarType(ty.unionFields(mod).values()[0].ty, mod); const first_field_ty = union_obj.field_types.get(ip)[0].toType();
return scalarType(first_field_ty, mod);
}, },
else => return ty, else => return ty,
} }

View File

@ -11534,6 +11534,7 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {
fn airUnionInit(self: *Self, inst: Air.Inst.Index) !void { fn airUnionInit(self: *Self, inst: Air.Inst.Index) !void {
const mod = self.bin_file.options.module.?; const mod = self.bin_file.options.module.?;
const ip = &mod.intern_pool;
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data; const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data;
const result: MCValue = result: { const result: MCValue = result: {
@ -11553,8 +11554,8 @@ fn airUnionInit(self: *Self, inst: Air.Inst.Index) !void {
const dst_mcv = try self.allocRegOrMem(inst, false); const dst_mcv = try self.allocRegOrMem(inst, false);
const union_obj = mod.typeToUnion(union_ty).?; const union_obj = mod.typeToUnion(union_ty).?;
const field_name = union_obj.fields.keys()[extra.field_index]; const field_name = union_obj.field_names.get(ip)[extra.field_index];
const tag_ty = union_obj.tag_ty; const tag_ty = union_obj.enum_tag_ty.toType();
const field_index = tag_ty.enumFieldIndex(field_name, mod).?; const field_index = tag_ty.enumFieldIndex(field_name, mod).?;
const tag_val = try mod.enumValueFieldIndex(tag_ty, field_index); const tag_val = try mod.enumValueFieldIndex(tag_ty, field_index);
const tag_int_val = try tag_val.intFromEnum(tag_ty, mod); const tag_int_val = try tag_val.intFromEnum(tag_ty, mod);

View File

@ -69,6 +69,7 @@ pub const Context = enum { ret, arg, other };
/// There are a maximum of 8 possible return slots. Returned values are in /// There are a maximum of 8 possible return slots. Returned values are in
/// the beginning of the array; unused slots are filled with .none. /// the beginning of the array; unused slots are filled with .none.
pub fn classifySystemV(ty: Type, mod: *Module, ctx: Context) [8]Class { pub fn classifySystemV(ty: Type, mod: *Module, ctx: Context) [8]Class {
const ip = &mod.intern_pool;
const target = mod.getTarget(); const target = mod.getTarget();
const memory_class = [_]Class{ const memory_class = [_]Class{
.memory, .none, .none, .none, .memory, .none, .none, .none,
@ -328,8 +329,9 @@ pub fn classifySystemV(ty: Type, mod: *Module, ctx: Context) [8]Class {
// it contains unaligned fields, it has class MEMORY" // it contains unaligned fields, it has class MEMORY"
// "If the size of the aggregate exceeds a single eightbyte, each is classified // "If the size of the aggregate exceeds a single eightbyte, each is classified
// separately.". // separately.".
const ty_size = ty.abiSize(mod); const union_obj = mod.typeToUnion(ty).?;
if (ty.containerLayout(mod) == .Packed) { const ty_size = mod.unionAbiSize(union_obj);
if (union_obj.getLayout(ip) == .Packed) {
assert(ty_size <= 128); assert(ty_size <= 128);
result[0] = .integer; result[0] = .integer;
if (ty_size > 64) result[1] = .integer; if (ty_size > 64) result[1] = .integer;
@ -338,15 +340,14 @@ pub fn classifySystemV(ty: Type, mod: *Module, ctx: Context) [8]Class {
if (ty_size > 64) if (ty_size > 64)
return memory_class; return memory_class;
const fields = ty.unionFields(mod); for (union_obj.field_types.get(ip), 0..) |field_ty, field_index| {
for (fields.values()) |field| { if (union_obj.fieldAlign(ip, @intCast(field_index)).toByteUnitsOptional()) |a| {
if (field.abi_align != .none) { if (a < field_ty.toType().abiAlignment(mod)) {
if (field.abi_align.toByteUnitsOptional().? < field.ty.abiAlignment(mod)) {
return memory_class; return memory_class;
} }
} }
// Combine this field with the previous one. // Combine this field with the previous one.
const field_class = classifySystemV(field.ty, mod, .other); const field_class = classifySystemV(field_ty.toType(), mod, .other);
for (&result, 0..) |*result_item, i| { for (&result, 0..) |*result_item, i| {
const field_item = field_class[i]; const field_item = field_class[i];
// "If both classes are equal, this is the resulting class." // "If both classes are equal, this is the resulting class."

View File

@ -185,8 +185,9 @@ pub fn generateSymbol(
defer tracy.end(); defer tracy.end();
const mod = bin_file.options.module.?; const mod = bin_file.options.module.?;
const ip = &mod.intern_pool;
var typed_value = arg_tv; var typed_value = arg_tv;
switch (mod.intern_pool.indexToKey(typed_value.val.toIntern())) { switch (ip.indexToKey(typed_value.val.toIntern())) {
.runtime_value => |rt| typed_value.val = rt.val.toValue(), .runtime_value => |rt| typed_value.val = rt.val.toValue(),
else => {}, else => {},
} }
@ -205,7 +206,7 @@ pub fn generateSymbol(
return .ok; return .ok;
} }
switch (mod.intern_pool.indexToKey(typed_value.val.toIntern())) { switch (ip.indexToKey(typed_value.val.toIntern())) {
.int_type, .int_type,
.ptr_type, .ptr_type,
.array_type, .array_type,
@ -385,7 +386,7 @@ pub fn generateSymbol(
try code.appendNTimes(0, padding); try code.appendNTimes(0, padding);
} }
}, },
.aggregate => |aggregate| switch (mod.intern_pool.indexToKey(typed_value.ty.toIntern())) { .aggregate => |aggregate| switch (ip.indexToKey(typed_value.ty.toIntern())) {
.array_type => |array_type| switch (aggregate.storage) { .array_type => |array_type| switch (aggregate.storage) {
.bytes => |bytes| try code.appendSlice(bytes), .bytes => |bytes| try code.appendSlice(bytes),
.elems, .repeated_elem => { .elems, .repeated_elem => {
@ -442,7 +443,7 @@ pub fn generateSymbol(
if (!field_ty.toType().hasRuntimeBits(mod)) continue; if (!field_ty.toType().hasRuntimeBits(mod)) continue;
const field_val = switch (aggregate.storage) { const field_val = switch (aggregate.storage) {
.bytes => |bytes| try mod.intern_pool.get(mod.gpa, .{ .int = .{ .bytes => |bytes| try ip.get(mod.gpa, .{ .int = .{
.ty = field_ty, .ty = field_ty,
.storage = .{ .u64 = bytes[index] }, .storage = .{ .u64 = bytes[index] },
} }), } }),
@ -484,7 +485,7 @@ pub fn generateSymbol(
const field_ty = field.ty; const field_ty = field.ty;
const field_val = switch (aggregate.storage) { const field_val = switch (aggregate.storage) {
.bytes => |bytes| try mod.intern_pool.get(mod.gpa, .{ .int = .{ .bytes => |bytes| try ip.get(mod.gpa, .{ .int = .{
.ty = field_ty.toIntern(), .ty = field_ty.toIntern(),
.storage = .{ .u64 = bytes[index] }, .storage = .{ .u64 = bytes[index] },
} }), } }),
@ -522,8 +523,8 @@ pub fn generateSymbol(
if (!field_ty.hasRuntimeBits(mod)) continue; if (!field_ty.hasRuntimeBits(mod)) continue;
const field_val = switch (mod.intern_pool.indexToKey(typed_value.val.toIntern()).aggregate.storage) { const field_val = switch (ip.indexToKey(typed_value.val.toIntern()).aggregate.storage) {
.bytes => |bytes| try mod.intern_pool.get(mod.gpa, .{ .int = .{ .bytes => |bytes| try ip.get(mod.gpa, .{ .int = .{
.ty = field_ty.toIntern(), .ty = field_ty.toIntern(),
.storage = .{ .u64 = bytes[field_offset.field] }, .storage = .{ .u64 = bytes[field_offset.field] },
} }), } }),
@ -570,10 +571,9 @@ pub fn generateSymbol(
} }
} }
const union_ty = mod.typeToUnion(typed_value.ty).?; const union_obj = mod.typeToUnion(typed_value.ty).?;
const field_index = typed_value.ty.unionTagFieldIndex(un.tag.toValue(), mod).?; const field_index = typed_value.ty.unionTagFieldIndex(un.tag.toValue(), mod).?;
assert(union_ty.haveFieldTypes()); const field_ty = union_obj.field_types.get(ip)[field_index].toType();
const field_ty = union_ty.fields.values()[field_index].ty;
if (!field_ty.hasRuntimeBits(mod)) { if (!field_ty.hasRuntimeBits(mod)) {
try code.appendNTimes(0xaa, math.cast(usize, layout.payload_size) orelse return error.Overflow); try code.appendNTimes(0xaa, math.cast(usize, layout.payload_size) orelse return error.Overflow);
} else { } else {
@ -593,7 +593,7 @@ pub fn generateSymbol(
if (layout.tag_size > 0 and layout.tag_align < layout.payload_align) { if (layout.tag_size > 0 and layout.tag_align < layout.payload_align) {
switch (try generateSymbol(bin_file, src_loc, .{ switch (try generateSymbol(bin_file, src_loc, .{
.ty = union_ty.tag_ty, .ty = union_obj.enum_tag_ty.toType(),
.val = un.tag.toValue(), .val = un.tag.toValue(),
}, code, debug_output, reloc_info)) { }, code, debug_output, reloc_info)) {
.ok => {}, .ok => {},

View File

@ -708,8 +708,10 @@ pub const DeclGen = struct {
location: ValueRenderLocation, location: ValueRenderLocation,
) error{ OutOfMemory, AnalysisFail }!void { ) error{ OutOfMemory, AnalysisFail }!void {
const mod = dg.module; const mod = dg.module;
const ip = &mod.intern_pool;
var val = arg_val; var val = arg_val;
switch (mod.intern_pool.indexToKey(val.ip_index)) { switch (ip.indexToKey(val.ip_index)) {
.runtime_value => |rt| val = rt.val.toValue(), .runtime_value => |rt| val = rt.val.toValue(),
else => {}, else => {},
} }
@ -836,9 +838,10 @@ pub const DeclGen = struct {
if (layout.tag_size != 0) try writer.writeByte(','); if (layout.tag_size != 0) try writer.writeByte(',');
try writer.writeAll(" .payload = {"); try writer.writeAll(" .payload = {");
} }
for (ty.unionFields(mod).values()) |field| { const union_obj = mod.typeToUnion(ty).?;
if (!field.ty.hasRuntimeBits(mod)) continue; for (union_obj.field_types.get(ip)) |field_ty| {
try dg.renderValue(writer, field.ty, val, initializer_type); if (!field_ty.toType().hasRuntimeBits(mod)) continue;
try dg.renderValue(writer, field_ty.toType(), val, initializer_type);
break; break;
} }
if (ty.unionTagTypeSafety(mod)) |_| try writer.writeByte('}'); if (ty.unionTagTypeSafety(mod)) |_| try writer.writeByte('}');
@ -912,7 +915,7 @@ pub const DeclGen = struct {
unreachable; unreachable;
} }
switch (mod.intern_pool.indexToKey(val.ip_index)) { switch (ip.indexToKey(val.ip_index)) {
// types, not values // types, not values
.int_type, .int_type,
.ptr_type, .ptr_type,
@ -962,7 +965,7 @@ pub const DeclGen = struct {
}, },
}, },
.err => |err| try writer.print("zig_error_{}", .{ .err => |err| try writer.print("zig_error_{}", .{
fmtIdent(mod.intern_pool.stringToSlice(err.name)), fmtIdent(ip.stringToSlice(err.name)),
}), }),
.error_union => |error_union| { .error_union => |error_union| {
const payload_ty = ty.errorUnionPayload(mod); const payload_ty = ty.errorUnionPayload(mod);
@ -1024,8 +1027,8 @@ pub const DeclGen = struct {
try writer.writeAll(" }"); try writer.writeAll(" }");
}, },
.enum_tag => { .enum_tag => {
const enum_tag = mod.intern_pool.indexToKey(val.ip_index).enum_tag; const enum_tag = ip.indexToKey(val.ip_index).enum_tag;
const int_tag_ty = mod.intern_pool.typeOf(enum_tag.int); const int_tag_ty = ip.typeOf(enum_tag.int);
try dg.renderValue(writer, int_tag_ty.toType(), enum_tag.int.toValue(), location); try dg.renderValue(writer, int_tag_ty.toType(), enum_tag.int.toValue(), location);
}, },
.float => { .float => {
@ -1205,7 +1208,7 @@ pub const DeclGen = struct {
try dg.renderValue(writer, Type.bool, is_null_val, initializer_type); try dg.renderValue(writer, Type.bool, is_null_val, initializer_type);
try writer.writeAll(" }"); try writer.writeAll(" }");
}, },
.aggregate => switch (mod.intern_pool.indexToKey(ty.ip_index)) { .aggregate => switch (ip.indexToKey(ty.ip_index)) {
.array_type, .vector_type => { .array_type, .vector_type => {
if (location == .FunctionArgument) { if (location == .FunctionArgument) {
try writer.writeByte('('); try writer.writeByte('(');
@ -1278,8 +1281,8 @@ pub const DeclGen = struct {
if (!empty) try writer.writeByte(','); if (!empty) try writer.writeByte(',');
const field_val = switch (mod.intern_pool.indexToKey(val.ip_index).aggregate.storage) { const field_val = switch (ip.indexToKey(val.ip_index).aggregate.storage) {
.bytes => |bytes| try mod.intern_pool.get(mod.gpa, .{ .int = .{ .bytes => |bytes| try ip.get(mod.gpa, .{ .int = .{
.ty = field_ty, .ty = field_ty,
.storage = .{ .u64 = bytes[field_i] }, .storage = .{ .u64 = bytes[field_i] },
} }), } }),
@ -1309,8 +1312,8 @@ pub const DeclGen = struct {
if (!field.ty.hasRuntimeBitsIgnoreComptime(mod)) continue; if (!field.ty.hasRuntimeBitsIgnoreComptime(mod)) continue;
if (!empty) try writer.writeByte(','); if (!empty) try writer.writeByte(',');
const field_val = switch (mod.intern_pool.indexToKey(val.ip_index).aggregate.storage) { const field_val = switch (ip.indexToKey(val.ip_index).aggregate.storage) {
.bytes => |bytes| try mod.intern_pool.get(mod.gpa, .{ .int = .{ .bytes => |bytes| try ip.get(mod.gpa, .{ .int = .{
.ty = field.ty.toIntern(), .ty = field.ty.toIntern(),
.storage = .{ .u64 = bytes[field_i] }, .storage = .{ .u64 = bytes[field_i] },
} }), } }),
@ -1358,8 +1361,8 @@ pub const DeclGen = struct {
if (field.is_comptime) continue; if (field.is_comptime) continue;
if (!field.ty.hasRuntimeBitsIgnoreComptime(mod)) continue; if (!field.ty.hasRuntimeBitsIgnoreComptime(mod)) continue;
const field_val = switch (mod.intern_pool.indexToKey(val.ip_index).aggregate.storage) { const field_val = switch (ip.indexToKey(val.ip_index).aggregate.storage) {
.bytes => |bytes| try mod.intern_pool.get(mod.gpa, .{ .int = .{ .bytes => |bytes| try ip.get(mod.gpa, .{ .int = .{
.ty = field.ty.toIntern(), .ty = field.ty.toIntern(),
.storage = .{ .u64 = bytes[field_i] }, .storage = .{ .u64 = bytes[field_i] },
} }), } }),
@ -1400,8 +1403,8 @@ pub const DeclGen = struct {
try dg.renderType(writer, ty); try dg.renderType(writer, ty);
try writer.writeByte(')'); try writer.writeByte(')');
const field_val = switch (mod.intern_pool.indexToKey(val.ip_index).aggregate.storage) { const field_val = switch (ip.indexToKey(val.ip_index).aggregate.storage) {
.bytes => |bytes| try mod.intern_pool.get(mod.gpa, .{ .int = .{ .bytes => |bytes| try ip.get(mod.gpa, .{ .int = .{
.ty = field.ty.toIntern(), .ty = field.ty.toIntern(),
.storage = .{ .u64 = bytes[field_i] }, .storage = .{ .u64 = bytes[field_i] },
} }), } }),
@ -1435,10 +1438,11 @@ pub const DeclGen = struct {
try writer.writeByte(')'); try writer.writeByte(')');
} }
const field_i = ty.unionTagFieldIndex(un.tag.toValue(), mod).?; const union_obj = mod.typeToUnion(ty).?;
const field_ty = ty.unionFields(mod).values()[field_i].ty; const field_i = mod.unionTagFieldIndex(union_obj, un.tag.toValue()).?;
const field_name = ty.unionFields(mod).keys()[field_i]; const field_ty = union_obj.field_types.get(ip)[field_i].toType();
if (ty.containerLayout(mod) == .Packed) { const field_name = union_obj.field_names.get(ip)[field_i];
if (union_obj.getLayout(ip) == .Packed) {
if (field_ty.hasRuntimeBits(mod)) { if (field_ty.hasRuntimeBits(mod)) {
if (field_ty.isPtrAtRuntime(mod)) { if (field_ty.isPtrAtRuntime(mod)) {
try writer.writeByte('('); try writer.writeByte('(');
@ -1458,7 +1462,7 @@ pub const DeclGen = struct {
try writer.writeByte('{'); try writer.writeByte('{');
if (ty.unionTagTypeSafety(mod)) |tag_ty| { if (ty.unionTagTypeSafety(mod)) |tag_ty| {
const layout = ty.unionGetLayout(mod); const layout = mod.getUnionLayout(union_obj);
if (layout.tag_size != 0) { if (layout.tag_size != 0) {
try writer.writeAll(" .tag = "); try writer.writeAll(" .tag = ");
try dg.renderValue(writer, tag_ty, un.tag.toValue(), initializer_type); try dg.renderValue(writer, tag_ty, un.tag.toValue(), initializer_type);
@ -1468,12 +1472,12 @@ pub const DeclGen = struct {
try writer.writeAll(" .payload = {"); try writer.writeAll(" .payload = {");
} }
if (field_ty.hasRuntimeBits(mod)) { if (field_ty.hasRuntimeBits(mod)) {
try writer.print(" .{ } = ", .{fmtIdent(mod.intern_pool.stringToSlice(field_name))}); try writer.print(" .{ } = ", .{fmtIdent(ip.stringToSlice(field_name))});
try dg.renderValue(writer, field_ty, un.val.toValue(), initializer_type); try dg.renderValue(writer, field_ty, un.val.toValue(), initializer_type);
try writer.writeByte(' '); try writer.writeByte(' ');
} else for (ty.unionFields(mod).values()) |field| { } else for (union_obj.field_types.get(ip)) |this_field_ty| {
if (!field.ty.hasRuntimeBits(mod)) continue; if (!this_field_ty.toType().hasRuntimeBits(mod)) continue;
try dg.renderValue(writer, field.ty, Value.undef, initializer_type); try dg.renderValue(writer, this_field_ty.toType(), Value.undef, initializer_type);
break; break;
} }
if (ty.unionTagTypeSafety(mod)) |_| try writer.writeByte('}'); if (ty.unionTagTypeSafety(mod)) |_| try writer.writeByte('}');
@ -5237,22 +5241,25 @@ fn fieldLocation(
else else
.begin, .begin,
}, },
.Union => switch (container_ty.containerLayout(mod)) { .Union => {
.Auto, .Extern => { const union_obj = mod.typeToUnion(container_ty).?;
const field_ty = container_ty.structFieldType(field_index, mod); return switch (union_obj.getLayout(ip)) {
if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) .Auto, .Extern => {
return if (container_ty.unionTagTypeSafety(mod) != null and const field_ty = union_obj.field_types.get(ip)[field_index].toType();
!container_ty.unionHasAllZeroBitFieldTypes(mod)) if (!field_ty.hasRuntimeBitsIgnoreComptime(mod))
.{ .field = .{ .identifier = "payload" } } return if (container_ty.unionTagTypeSafety(mod) != null and
!container_ty.unionHasAllZeroBitFieldTypes(mod))
.{ .field = .{ .identifier = "payload" } }
else
.begin;
const field_name = union_obj.field_names.get(ip)[field_index];
return .{ .field = if (container_ty.unionTagTypeSafety(mod)) |_|
.{ .payload_identifier = ip.stringToSlice(field_name) }
else else
.begin; .{ .identifier = ip.stringToSlice(field_name) } };
const field_name = container_ty.unionFields(mod).keys()[field_index]; },
return .{ .field = if (container_ty.unionTagTypeSafety(mod)) |_| .Packed => .begin,
.{ .payload_identifier = ip.stringToSlice(field_name) } };
else
.{ .identifier = ip.stringToSlice(field_name) } };
},
.Packed => .begin,
}, },
.Pointer => switch (container_ty.ptrSize(mod)) { .Pointer => switch (container_ty.ptrSize(mod)) {
.Slice => switch (field_index) { .Slice => switch (field_index) {
@ -5479,8 +5486,8 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
.{ .identifier = ip.stringToSlice(struct_ty.structFieldName(extra.field_index, mod)) }, .{ .identifier = ip.stringToSlice(struct_ty.structFieldName(extra.field_index, mod)) },
.union_type => |union_type| field_name: { .union_type => |union_type| field_name: {
const union_obj = mod.unionPtr(union_type.index); const union_obj = ip.loadUnionType(union_type);
if (union_obj.layout == .Packed) { if (union_obj.flagsPtr(ip).layout == .Packed) {
const operand_lval = if (struct_byval == .constant) blk: { const operand_lval = if (struct_byval == .constant) blk: {
const operand_local = try f.allocLocal(inst, struct_ty); const operand_local = try f.allocLocal(inst, struct_ty);
try f.writeCValue(writer, operand_local, .Other); try f.writeCValue(writer, operand_local, .Other);
@ -5505,8 +5512,8 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
return local; return local;
} else { } else {
const name = union_obj.fields.keys()[extra.field_index]; const name = union_obj.field_names.get(ip)[extra.field_index];
break :field_name if (union_type.hasTag()) .{ break :field_name if (union_type.hasTag(ip)) .{
.payload_identifier = ip.stringToSlice(name), .payload_identifier = ip.stringToSlice(name),
} else .{ } else .{
.identifier = ip.stringToSlice(name), .identifier = ip.stringToSlice(name),
@ -6902,14 +6909,14 @@ fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue {
const union_ty = f.typeOfIndex(inst); const union_ty = f.typeOfIndex(inst);
const union_obj = mod.typeToUnion(union_ty).?; const union_obj = mod.typeToUnion(union_ty).?;
const field_name = union_obj.fields.keys()[extra.field_index]; const field_name = union_obj.field_names.get(ip)[extra.field_index];
const payload_ty = f.typeOf(extra.init); const payload_ty = f.typeOf(extra.init);
const payload = try f.resolveInst(extra.init); const payload = try f.resolveInst(extra.init);
try reap(f, inst, &.{extra.init}); try reap(f, inst, &.{extra.init});
const writer = f.object.writer(); const writer = f.object.writer();
const local = try f.allocLocal(inst, union_ty); const local = try f.allocLocal(inst, union_ty);
if (union_obj.layout == .Packed) { if (union_obj.getLayout(ip) == .Packed) {
try f.writeCValue(writer, local, .Other); try f.writeCValue(writer, local, .Other);
try writer.writeAll(" = "); try writer.writeAll(" = ");
try f.writeCValue(writer, payload, .Initializer); try f.writeCValue(writer, payload, .Initializer);

View File

@ -303,7 +303,7 @@ pub const CType = extern union {
} }
pub fn unionPayloadAlign(union_ty: Type, mod: *Module) AlignAs { pub fn unionPayloadAlign(union_ty: Type, mod: *Module) AlignAs {
const union_obj = mod.typeToUnion(union_ty).?; const union_obj = mod.typeToUnion(union_ty).?;
const union_payload_align = union_obj.abiAlignment(mod, false); const union_payload_align = mod.unionAbiAlignment(union_obj);
return init(union_payload_align, union_payload_align); return init(union_payload_align, union_payload_align);
} }
@ -1499,7 +1499,7 @@ pub const CType = extern union {
if (lookup.isMutable()) { if (lookup.isMutable()) {
for (0..switch (zig_ty_tag) { for (0..switch (zig_ty_tag) {
.Struct => ty.structFieldCount(mod), .Struct => ty.structFieldCount(mod),
.Union => ty.unionFields(mod).count(), .Union => mod.typeToUnion(ty).?.field_names.len,
else => unreachable, else => unreachable,
}) |field_i| { }) |field_i| {
const field_ty = ty.structFieldType(field_i, mod); const field_ty = ty.structFieldType(field_i, mod);
@ -1581,7 +1581,7 @@ pub const CType = extern union {
var is_packed = false; var is_packed = false;
for (0..switch (zig_ty_tag) { for (0..switch (zig_ty_tag) {
.Struct => ty.structFieldCount(mod), .Struct => ty.structFieldCount(mod),
.Union => ty.unionFields(mod).count(), .Union => mod.typeToUnion(ty).?.field_names.len,
else => unreachable, else => unreachable,
}) |field_i| { }) |field_i| {
const field_ty = ty.structFieldType(field_i, mod); const field_ty = ty.structFieldType(field_i, mod);
@ -1912,6 +1912,7 @@ pub const CType = extern union {
kind: Kind, kind: Kind,
convert: Convert, convert: Convert,
) !CType { ) !CType {
const ip = &mod.intern_pool;
const arena = store.arena.allocator(); const arena = store.arena.allocator();
switch (convert.value) { switch (convert.value) {
.cty => |c| return c.copy(arena), .cty => |c| return c.copy(arena),
@ -1932,7 +1933,7 @@ pub const CType = extern union {
const zig_ty_tag = ty.zigTypeTag(mod); const zig_ty_tag = ty.zigTypeTag(mod);
const fields_len = switch (zig_ty_tag) { const fields_len = switch (zig_ty_tag) {
.Struct => ty.structFieldCount(mod), .Struct => ty.structFieldCount(mod),
.Union => ty.unionFields(mod).count(), .Union => mod.typeToUnion(ty).?.field_names.len,
else => unreachable, else => unreachable,
}; };
@ -1956,9 +1957,9 @@ pub const CType = extern union {
.name = try if (ty.isSimpleTuple(mod)) .name = try if (ty.isSimpleTuple(mod))
std.fmt.allocPrintZ(arena, "f{}", .{field_i}) std.fmt.allocPrintZ(arena, "f{}", .{field_i})
else else
arena.dupeZ(u8, mod.intern_pool.stringToSlice(switch (zig_ty_tag) { arena.dupeZ(u8, ip.stringToSlice(switch (zig_ty_tag) {
.Struct => ty.structFieldName(field_i, mod), .Struct => ty.structFieldName(field_i, mod),
.Union => ty.unionFields(mod).keys()[field_i], .Union => mod.typeToUnion(ty).?.field_names.get(ip)[field_i],
else => unreachable, else => unreachable,
})), })),
.type = store.set.typeToIndex(field_ty, mod, switch (kind) { .type = store.set.typeToIndex(field_ty, mod, switch (kind) {
@ -2015,7 +2016,6 @@ pub const CType = extern union {
.function, .function,
.varargs_function, .varargs_function,
=> { => {
const ip = &mod.intern_pool;
const info = mod.typeToFunc(ty).?; const info = mod.typeToFunc(ty).?;
assert(!info.is_generic); assert(!info.is_generic);
const param_kind: Kind = switch (kind) { const param_kind: Kind = switch (kind) {
@ -2068,6 +2068,7 @@ pub const CType = extern union {
pub fn eql(self: @This(), ty: Type, cty: CType) bool { pub fn eql(self: @This(), ty: Type, cty: CType) bool {
const mod = self.lookup.getModule(); const mod = self.lookup.getModule();
const ip = &mod.intern_pool;
switch (self.convert.value) { switch (self.convert.value) {
.cty => |c| return c.eql(cty), .cty => |c| return c.eql(cty),
.tag => |t| { .tag => |t| {
@ -2088,7 +2089,7 @@ pub const CType = extern union {
var c_field_i: usize = 0; var c_field_i: usize = 0;
for (0..switch (zig_ty_tag) { for (0..switch (zig_ty_tag) {
.Struct => ty.structFieldCount(mod), .Struct => ty.structFieldCount(mod),
.Union => ty.unionFields(mod).count(), .Union => mod.typeToUnion(ty).?.field_names.len,
else => unreachable, else => unreachable,
}) |field_i| { }) |field_i| {
const field_ty = ty.structFieldType(field_i, mod); const field_ty = ty.structFieldType(field_i, mod);
@ -2108,9 +2109,9 @@ pub const CType = extern union {
if (ty.isSimpleTuple(mod)) if (ty.isSimpleTuple(mod))
std.fmt.bufPrintZ(&name_buf, "f{}", .{field_i}) catch unreachable std.fmt.bufPrintZ(&name_buf, "f{}", .{field_i}) catch unreachable
else else
mod.intern_pool.stringToSlice(switch (zig_ty_tag) { ip.stringToSlice(switch (zig_ty_tag) {
.Struct => ty.structFieldName(field_i, mod), .Struct => ty.structFieldName(field_i, mod),
.Union => ty.unionFields(mod).keys()[field_i], .Union => mod.typeToUnion(ty).?.field_names.get(ip)[field_i],
else => unreachable, else => unreachable,
}), }),
mem.span(c_field.name), mem.span(c_field.name),
@ -2149,7 +2150,6 @@ pub const CType = extern union {
=> { => {
if (ty.zigTypeTag(mod) != .Fn) return false; if (ty.zigTypeTag(mod) != .Fn) return false;
const ip = &mod.intern_pool;
const info = mod.typeToFunc(ty).?; const info = mod.typeToFunc(ty).?;
assert(!info.is_generic); assert(!info.is_generic);
const data = cty.cast(Payload.Function).?.data; const data = cty.cast(Payload.Function).?.data;
@ -2217,7 +2217,7 @@ pub const CType = extern union {
const zig_ty_tag = ty.zigTypeTag(mod); const zig_ty_tag = ty.zigTypeTag(mod);
for (0..switch (ty.zigTypeTag(mod)) { for (0..switch (ty.zigTypeTag(mod)) {
.Struct => ty.structFieldCount(mod), .Struct => ty.structFieldCount(mod),
.Union => ty.unionFields(mod).count(), .Union => mod.typeToUnion(ty).?.field_names.len,
else => unreachable, else => unreachable,
}) |field_i| { }) |field_i| {
const field_ty = ty.structFieldType(field_i, mod); const field_ty = ty.structFieldType(field_i, mod);
@ -2235,7 +2235,7 @@ pub const CType = extern union {
else else
mod.intern_pool.stringToSlice(switch (zig_ty_tag) { mod.intern_pool.stringToSlice(switch (zig_ty_tag) {
.Struct => ty.structFieldName(field_i, mod), .Struct => ty.structFieldName(field_i, mod),
.Union => ty.unionFields(mod).keys()[field_i], .Union => mod.typeToUnion(ty).?.field_names.get(ip)[field_i],
else => unreachable, else => unreachable,
})); }));
autoHash(hasher, AlignAs.fieldAlign(ty, field_i, mod).@"align"); autoHash(hasher, AlignAs.fieldAlign(ty, field_i, mod).@"align");

View File

@ -2382,7 +2382,7 @@ pub const Object = struct {
break :blk fwd_decl; break :blk fwd_decl;
}; };
switch (mod.intern_pool.indexToKey(ty.toIntern())) { switch (ip.indexToKey(ty.toIntern())) {
.anon_struct_type => |tuple| { .anon_struct_type => |tuple| {
var di_fields: std.ArrayListUnmanaged(*llvm.DIType) = .{}; var di_fields: std.ArrayListUnmanaged(*llvm.DIType) = .{};
defer di_fields.deinit(gpa); defer di_fields.deinit(gpa);
@ -2401,7 +2401,7 @@ pub const Object = struct {
offset = field_offset + field_size; offset = field_offset + field_size;
const field_name = if (tuple.names.len != 0) const field_name = if (tuple.names.len != 0)
mod.intern_pool.stringToSlice(tuple.names[i]) ip.stringToSlice(tuple.names[i])
else else
try std.fmt.allocPrintZ(gpa, "{d}", .{i}); try std.fmt.allocPrintZ(gpa, "{d}", .{i});
defer if (tuple.names.len == 0) gpa.free(field_name); defer if (tuple.names.len == 0) gpa.free(field_name);
@ -2491,7 +2491,7 @@ pub const Object = struct {
const field_offset = std.mem.alignForward(u64, offset, field_align); const field_offset = std.mem.alignForward(u64, offset, field_align);
offset = field_offset + field_size; offset = field_offset + field_size;
const field_name = mod.intern_pool.stringToSlice(fields.keys()[field_and_index.index]); const field_name = ip.stringToSlice(fields.keys()[field_and_index.index]);
try di_fields.append(gpa, dib.createMemberType( try di_fields.append(gpa, dib.createMemberType(
fwd_decl.toScope(), fwd_decl.toScope(),
@ -2546,8 +2546,8 @@ pub const Object = struct {
break :blk fwd_decl; break :blk fwd_decl;
}; };
const union_obj = mod.typeToUnion(ty).?; const union_type = ip.indexToKey(ty.toIntern()).union_type;
if (!union_obj.haveFieldTypes() or !ty.hasRuntimeBitsIgnoreComptime(mod)) { if (!union_type.haveFieldTypes(ip) or !ty.hasRuntimeBitsIgnoreComptime(mod)) {
const union_di_ty = try o.makeEmptyNamespaceDIType(owner_decl_index); const union_di_ty = try o.makeEmptyNamespaceDIType(owner_decl_index);
dib.replaceTemporary(fwd_decl, union_di_ty); dib.replaceTemporary(fwd_decl, union_di_ty);
// The recursive call to `lowerDebugType` via `makeEmptyNamespaceDIType` // The recursive call to `lowerDebugType` via `makeEmptyNamespaceDIType`
@ -2556,10 +2556,11 @@ pub const Object = struct {
return union_di_ty; return union_di_ty;
} }
const layout = ty.unionGetLayout(mod); const union_obj = ip.loadUnionType(union_type);
const layout = mod.getUnionLayout(union_obj);
if (layout.payload_size == 0) { if (layout.payload_size == 0) {
const tag_di_ty = try o.lowerDebugType(union_obj.tag_ty, .full); const tag_di_ty = try o.lowerDebugType(union_obj.enum_tag_ty.toType(), .full);
const di_fields = [_]*llvm.DIType{tag_di_ty}; const di_fields = [_]*llvm.DIType{tag_di_ty};
const full_di_ty = dib.createStructType( const full_di_ty = dib.createStructType(
compile_unit_scope, compile_unit_scope,
@ -2586,22 +2587,20 @@ pub const Object = struct {
var di_fields: std.ArrayListUnmanaged(*llvm.DIType) = .{}; var di_fields: std.ArrayListUnmanaged(*llvm.DIType) = .{};
defer di_fields.deinit(gpa); defer di_fields.deinit(gpa);
try di_fields.ensureUnusedCapacity(gpa, union_obj.fields.count()); try di_fields.ensureUnusedCapacity(gpa, union_obj.field_names.len);
var it = union_obj.fields.iterator(); for (0..union_obj.field_names.len) |field_index| {
while (it.next()) |kv| { const field_ty = union_obj.field_types.get(ip)[field_index];
const field_name = kv.key_ptr.*; if (!field_ty.toType().hasRuntimeBitsIgnoreComptime(mod)) continue;
const field = kv.value_ptr.*;
if (!field.ty.hasRuntimeBitsIgnoreComptime(mod)) continue; const field_size = field_ty.toType().abiSize(mod);
const field_align = mod.unionFieldNormalAlignment(union_obj, @intCast(field_index));
const field_size = field.ty.abiSize(mod); const field_di_ty = try o.lowerDebugType(field_ty.toType(), .full);
const field_align = field.normalAlignment(mod); const field_name = union_obj.field_names.get(ip)[field_index];
const field_di_ty = try o.lowerDebugType(field.ty, .full);
di_fields.appendAssumeCapacity(dib.createMemberType( di_fields.appendAssumeCapacity(dib.createMemberType(
fwd_decl.toScope(), fwd_decl.toScope(),
mod.intern_pool.stringToSlice(field_name), ip.stringToSlice(field_name),
null, // file null, // file
0, // line 0, // line
field_size * 8, // size in bits field_size * 8, // size in bits
@ -2659,7 +2658,7 @@ pub const Object = struct {
layout.tag_align * 8, // align in bits layout.tag_align * 8, // align in bits
tag_offset * 8, // offset in bits tag_offset * 8, // offset in bits
0, // flags 0, // flags
try o.lowerDebugType(union_obj.tag_ty, .full), try o.lowerDebugType(union_obj.enum_tag_ty.toType(), .full),
); );
const payload_di = dib.createMemberType( const payload_di = dib.createMemberType(
@ -3078,6 +3077,7 @@ pub const Object = struct {
fn lowerTypeInner(o: *Object, t: Type) Allocator.Error!Builder.Type { fn lowerTypeInner(o: *Object, t: Type) Allocator.Error!Builder.Type {
const mod = o.module; const mod = o.module;
const target = mod.getTarget(); const target = mod.getTarget();
const ip = &mod.intern_pool;
return switch (t.toIntern()) { return switch (t.toIntern()) {
.u0_type, .i0_type => unreachable, .u0_type, .i0_type => unreachable,
inline .u1_type, inline .u1_type,
@ -3172,7 +3172,7 @@ pub const Object = struct {
.var_args_param_type, .var_args_param_type,
.none, .none,
=> unreachable, => unreachable,
else => switch (mod.intern_pool.indexToKey(t.toIntern())) { else => switch (ip.indexToKey(t.toIntern())) {
.int_type => |int_type| try o.builder.intType(int_type.bits), .int_type => |int_type| try o.builder.intType(int_type.bits),
.ptr_type => |ptr_type| type: { .ptr_type => |ptr_type| type: {
const ptr_ty = try o.builder.ptrType( const ptr_ty = try o.builder.ptrType(
@ -3264,7 +3264,7 @@ pub const Object = struct {
return int_ty; return int_ty;
} }
const name = try o.builder.string(mod.intern_pool.stringToSlice( const name = try o.builder.string(ip.stringToSlice(
try struct_obj.getFullyQualifiedName(mod), try struct_obj.getFullyQualifiedName(mod),
)); ));
const ty = try o.builder.opaqueType(name); const ty = try o.builder.opaqueType(name);
@ -3357,40 +3357,40 @@ pub const Object = struct {
const gop = try o.type_map.getOrPut(o.gpa, t.toIntern()); const gop = try o.type_map.getOrPut(o.gpa, t.toIntern());
if (gop.found_existing) return gop.value_ptr.*; if (gop.found_existing) return gop.value_ptr.*;
const union_obj = mod.unionPtr(union_type.index); const union_obj = ip.loadUnionType(union_type);
const layout = union_obj.getLayout(mod, union_type.hasTag()); const layout = mod.getUnionLayout(union_obj);
if (union_obj.layout == .Packed) { if (union_obj.flagsPtr(ip).layout == .Packed) {
const int_ty = try o.builder.intType(@intCast(t.bitSize(mod))); const int_ty = try o.builder.intType(@intCast(t.bitSize(mod)));
gop.value_ptr.* = int_ty; gop.value_ptr.* = int_ty;
return int_ty; return int_ty;
} }
if (layout.payload_size == 0) { if (layout.payload_size == 0) {
const enum_tag_ty = try o.lowerType(union_obj.tag_ty); const enum_tag_ty = try o.lowerType(union_obj.enum_tag_ty.toType());
gop.value_ptr.* = enum_tag_ty; gop.value_ptr.* = enum_tag_ty;
return enum_tag_ty; return enum_tag_ty;
} }
const name = try o.builder.string(mod.intern_pool.stringToSlice( const name = try o.builder.string(ip.stringToSlice(
try union_obj.getFullyQualifiedName(mod), try mod.declPtr(union_obj.decl).getFullyQualifiedName(mod),
)); ));
const ty = try o.builder.opaqueType(name); const ty = try o.builder.opaqueType(name);
gop.value_ptr.* = ty; // must be done before any recursive calls gop.value_ptr.* = ty; // must be done before any recursive calls
const aligned_field = union_obj.fields.values()[layout.most_aligned_field]; const aligned_field_ty = union_obj.field_types.get(ip)[layout.most_aligned_field].toType();
const aligned_field_ty = try o.lowerType(aligned_field.ty); const aligned_field_llvm_ty = try o.lowerType(aligned_field_ty);
const payload_ty = ty: { const payload_ty = ty: {
if (layout.most_aligned_field_size == layout.payload_size) { if (layout.most_aligned_field_size == layout.payload_size) {
break :ty aligned_field_ty; break :ty aligned_field_llvm_ty;
} }
const padding_len = if (layout.tag_size == 0) const padding_len = if (layout.tag_size == 0)
layout.abi_size - layout.most_aligned_field_size layout.abi_size - layout.most_aligned_field_size
else else
layout.payload_size - layout.most_aligned_field_size; layout.payload_size - layout.most_aligned_field_size;
break :ty try o.builder.structType(.@"packed", &.{ break :ty try o.builder.structType(.@"packed", &.{
aligned_field_ty, aligned_field_llvm_ty,
try o.builder.arrayType(padding_len, .i8), try o.builder.arrayType(padding_len, .i8),
}); });
}; };
@ -3402,7 +3402,7 @@ pub const Object = struct {
); );
return ty; return ty;
} }
const enum_tag_ty = try o.lowerType(union_obj.tag_ty); const enum_tag_ty = try o.lowerType(union_obj.enum_tag_ty.toType());
// Put the tag before or after the payload depending on which one's // Put the tag before or after the payload depending on which one's
// alignment is greater. // alignment is greater.
@ -3430,7 +3430,7 @@ pub const Object = struct {
.opaque_type => |opaque_type| { .opaque_type => |opaque_type| {
const gop = try o.type_map.getOrPut(o.gpa, t.toIntern()); const gop = try o.type_map.getOrPut(o.gpa, t.toIntern());
if (!gop.found_existing) { if (!gop.found_existing) {
const name = try o.builder.string(mod.intern_pool.stringToSlice( const name = try o.builder.string(ip.stringToSlice(
try mod.opaqueFullyQualifiedName(opaque_type), try mod.opaqueFullyQualifiedName(opaque_type),
)); ));
gop.value_ptr.* = try o.builder.opaqueType(name); gop.value_ptr.* = try o.builder.opaqueType(name);
@ -3551,10 +3551,11 @@ pub const Object = struct {
fn lowerValue(o: *Object, arg_val: InternPool.Index) Error!Builder.Constant { fn lowerValue(o: *Object, arg_val: InternPool.Index) Error!Builder.Constant {
const mod = o.module; const mod = o.module;
const ip = &mod.intern_pool;
const target = mod.getTarget(); const target = mod.getTarget();
var val = arg_val.toValue(); var val = arg_val.toValue();
const arg_val_key = mod.intern_pool.indexToKey(arg_val); const arg_val_key = ip.indexToKey(arg_val);
switch (arg_val_key) { switch (arg_val_key) {
.runtime_value => |rt| val = rt.val.toValue(), .runtime_value => |rt| val = rt.val.toValue(),
else => {}, else => {},
@ -3563,7 +3564,7 @@ pub const Object = struct {
return o.builder.undefConst(try o.lowerType(arg_val_key.typeOf().toType())); return o.builder.undefConst(try o.lowerType(arg_val_key.typeOf().toType()));
} }
const val_key = mod.intern_pool.indexToKey(val.toIntern()); const val_key = ip.indexToKey(val.toIntern());
const ty = val_key.typeOf().toType(); const ty = val_key.typeOf().toType();
return switch (val_key) { return switch (val_key) {
.int_type, .int_type,
@ -3749,7 +3750,7 @@ pub const Object = struct {
fields[0..llvm_ty_fields.len], fields[0..llvm_ty_fields.len],
), vals[0..llvm_ty_fields.len]); ), vals[0..llvm_ty_fields.len]);
}, },
.aggregate => |aggregate| switch (mod.intern_pool.indexToKey(ty.toIntern())) { .aggregate => |aggregate| switch (ip.indexToKey(ty.toIntern())) {
.array_type => |array_type| switch (aggregate.storage) { .array_type => |array_type| switch (aggregate.storage) {
.bytes => |bytes| try o.builder.stringConst(try o.builder.string(bytes)), .bytes => |bytes| try o.builder.stringConst(try o.builder.string(bytes)),
.elems => |elems| { .elems => |elems| {
@ -4024,11 +4025,10 @@ pub const Object = struct {
if (layout.payload_size == 0) return o.lowerValue(un.tag); if (layout.payload_size == 0) return o.lowerValue(un.tag);
const union_obj = mod.typeToUnion(ty).?; const union_obj = mod.typeToUnion(ty).?;
const field_index = ty.unionTagFieldIndex(un.tag.toValue(), o.module).?; const field_index = mod.unionTagFieldIndex(union_obj, un.tag.toValue()).?;
assert(union_obj.haveFieldTypes());
const field_ty = union_obj.fields.values()[field_index].ty; const field_ty = union_obj.field_types.get(ip)[field_index].toType();
if (union_obj.layout == .Packed) { if (union_obj.getLayout(ip) == .Packed) {
if (!field_ty.hasRuntimeBits(mod)) return o.builder.intConst(union_ty, 0); if (!field_ty.hasRuntimeBits(mod)) return o.builder.intConst(union_ty, 0);
const small_int_val = try o.builder.castConst( const small_int_val = try o.builder.castConst(
if (field_ty.isPtrAtRuntime(mod)) .ptrtoint else .bitcast, if (field_ty.isPtrAtRuntime(mod)) .ptrtoint else .bitcast,
@ -9676,6 +9676,7 @@ pub const FuncGen = struct {
fn airUnionInit(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { fn airUnionInit(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value {
const o = self.dg.object; const o = self.dg.object;
const mod = o.module; const mod = o.module;
const ip = &mod.intern_pool;
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data; const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data;
const union_ty = self.typeOfIndex(inst); const union_ty = self.typeOfIndex(inst);
@ -9683,13 +9684,13 @@ pub const FuncGen = struct {
const layout = union_ty.unionGetLayout(mod); const layout = union_ty.unionGetLayout(mod);
const union_obj = mod.typeToUnion(union_ty).?; const union_obj = mod.typeToUnion(union_ty).?;
if (union_obj.layout == .Packed) { if (union_obj.getLayout(ip) == .Packed) {
const big_bits = union_ty.bitSize(mod); const big_bits = union_ty.bitSize(mod);
const int_llvm_ty = try o.builder.intType(@intCast(big_bits)); const int_llvm_ty = try o.builder.intType(@intCast(big_bits));
const field = union_obj.fields.values()[extra.field_index]; const field_ty = union_obj.field_types.get(ip)[extra.field_index].toType();
const non_int_val = try self.resolveInst(extra.init); const non_int_val = try self.resolveInst(extra.init);
const small_int_ty = try o.builder.intType(@intCast(field.ty.bitSize(mod))); const small_int_ty = try o.builder.intType(@intCast(field_ty.bitSize(mod)));
const small_int_val = if (field.ty.isPtrAtRuntime(mod)) const small_int_val = if (field_ty.isPtrAtRuntime(mod))
try self.wip.cast(.ptrtoint, non_int_val, small_int_ty, "") try self.wip.cast(.ptrtoint, non_int_val, small_int_ty, "")
else else
try self.wip.cast(.bitcast, non_int_val, small_int_ty, ""); try self.wip.cast(.bitcast, non_int_val, small_int_ty, "");
@ -9698,7 +9699,7 @@ pub const FuncGen = struct {
const tag_int = blk: { const tag_int = blk: {
const tag_ty = union_ty.unionTagTypeHypothetical(mod); const tag_ty = union_ty.unionTagTypeHypothetical(mod);
const union_field_name = union_obj.fields.keys()[extra.field_index]; const union_field_name = union_obj.field_names.get(ip)[extra.field_index];
const enum_field_index = tag_ty.enumFieldIndex(union_field_name, mod).?; const enum_field_index = tag_ty.enumFieldIndex(union_field_name, mod).?;
const tag_val = try mod.enumValueFieldIndex(tag_ty, enum_field_index); const tag_val = try mod.enumValueFieldIndex(tag_ty, enum_field_index);
const tag_int_val = try tag_val.intFromEnum(tag_ty, mod); const tag_int_val = try tag_val.intFromEnum(tag_ty, mod);
@ -9719,18 +9720,17 @@ pub const FuncGen = struct {
const alignment = Builder.Alignment.fromByteUnits(layout.abi_align); const alignment = Builder.Alignment.fromByteUnits(layout.abi_align);
const result_ptr = try self.buildAlloca(union_llvm_ty, alignment); const result_ptr = try self.buildAlloca(union_llvm_ty, alignment);
const llvm_payload = try self.resolveInst(extra.init); const llvm_payload = try self.resolveInst(extra.init);
assert(union_obj.haveFieldTypes()); const field_ty = union_obj.field_types.get(ip)[extra.field_index].toType();
const field = union_obj.fields.values()[extra.field_index]; const field_llvm_ty = try o.lowerType(field_ty);
const field_llvm_ty = try o.lowerType(field.ty); const field_size = field_ty.abiSize(mod);
const field_size = field.ty.abiSize(mod); const field_align = mod.unionFieldNormalAlignment(union_obj, extra.field_index);
const field_align = field.normalAlignment(mod);
const llvm_usize = try o.lowerType(Type.usize); const llvm_usize = try o.lowerType(Type.usize);
const usize_zero = try o.builder.intValue(llvm_usize, 0); const usize_zero = try o.builder.intValue(llvm_usize, 0);
const i32_zero = try o.builder.intValue(.i32, 0); const i32_zero = try o.builder.intValue(.i32, 0);
const llvm_union_ty = t: { const llvm_union_ty = t: {
const payload_ty = p: { const payload_ty = p: {
if (!field.ty.hasRuntimeBitsIgnoreComptime(mod)) { if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) {
const padding_len = layout.payload_size; const padding_len = layout.payload_size;
break :p try o.builder.arrayType(padding_len, .i8); break :p try o.builder.arrayType(padding_len, .i8);
} }
@ -9743,7 +9743,7 @@ pub const FuncGen = struct {
}); });
}; };
if (layout.tag_size == 0) break :t try o.builder.structType(.normal, &.{payload_ty}); if (layout.tag_size == 0) break :t try o.builder.structType(.normal, &.{payload_ty});
const tag_ty = try o.lowerType(union_obj.tag_ty); const tag_ty = try o.lowerType(union_obj.enum_tag_ty.toType());
var fields: [3]Builder.Type = undefined; var fields: [3]Builder.Type = undefined;
var fields_len: usize = 2; var fields_len: usize = 2;
if (layout.tag_align >= layout.payload_align) { if (layout.tag_align >= layout.payload_align) {
@ -9761,7 +9761,7 @@ pub const FuncGen = struct {
// Now we follow the layout as expressed above with GEP instructions to set the // Now we follow the layout as expressed above with GEP instructions to set the
// tag and the payload. // tag and the payload.
const field_ptr_ty = try mod.ptrType(.{ const field_ptr_ty = try mod.ptrType(.{
.child = field.ty.toIntern(), .child = field_ty.toIntern(),
.flags = .{ .alignment = InternPool.Alignment.fromNonzeroByteUnits(field_align) }, .flags = .{ .alignment = InternPool.Alignment.fromNonzeroByteUnits(field_align) },
}); });
if (layout.tag_size == 0) { if (layout.tag_size == 0) {
@ -9786,9 +9786,9 @@ pub const FuncGen = struct {
const tag_index = @intFromBool(layout.tag_align < layout.payload_align); const tag_index = @intFromBool(layout.tag_align < layout.payload_align);
const indices: [2]Builder.Value = .{ usize_zero, try o.builder.intValue(.i32, tag_index) }; const indices: [2]Builder.Value = .{ usize_zero, try o.builder.intValue(.i32, tag_index) };
const field_ptr = try self.wip.gep(.inbounds, llvm_union_ty, result_ptr, &indices, ""); const field_ptr = try self.wip.gep(.inbounds, llvm_union_ty, result_ptr, &indices, "");
const tag_ty = try o.lowerType(union_obj.tag_ty); const tag_ty = try o.lowerType(union_obj.enum_tag_ty.toType());
const llvm_tag = try o.builder.intValue(tag_ty, tag_int); const llvm_tag = try o.builder.intValue(tag_ty, tag_int);
const tag_alignment = Builder.Alignment.fromByteUnits(union_obj.tag_ty.abiAlignment(mod)); const tag_alignment = Builder.Alignment.fromByteUnits(union_obj.enum_tag_ty.toType().abiAlignment(mod));
_ = try self.wip.store(.normal, llvm_tag, field_ptr, tag_alignment); _ = try self.wip.store(.normal, llvm_tag, field_ptr, tag_alignment);
} }

View File

@ -619,9 +619,10 @@ pub const DeclGen = struct {
fn lower(self: *@This(), ty: Type, arg_val: Value) !void { fn lower(self: *@This(), ty: Type, arg_val: Value) !void {
const dg = self.dg; const dg = self.dg;
const mod = dg.module; const mod = dg.module;
const ip = &mod.intern_pool;
var val = arg_val; var val = arg_val;
switch (mod.intern_pool.indexToKey(val.toIntern())) { switch (ip.indexToKey(val.toIntern())) {
.runtime_value => |rt| val = rt.val.toValue(), .runtime_value => |rt| val = rt.val.toValue(),
else => {}, else => {},
} }
@ -631,7 +632,7 @@ pub const DeclGen = struct {
return try self.addUndef(size); return try self.addUndef(size);
} }
switch (mod.intern_pool.indexToKey(val.toIntern())) { switch (ip.indexToKey(val.toIntern())) {
.int_type, .int_type,
.ptr_type, .ptr_type,
.array_type, .array_type,
@ -770,7 +771,7 @@ pub const DeclGen = struct {
try self.addConstBool(payload_val != null); try self.addConstBool(payload_val != null);
try self.addUndef(padding); try self.addUndef(padding);
}, },
.aggregate => |aggregate| switch (mod.intern_pool.indexToKey(ty.ip_index)) { .aggregate => |aggregate| switch (ip.indexToKey(ty.ip_index)) {
.array_type => |array_type| { .array_type => |array_type| {
const elem_ty = array_type.child.toType(); const elem_ty = array_type.child.toType();
switch (aggregate.storage) { switch (aggregate.storage) {
@ -801,7 +802,7 @@ pub const DeclGen = struct {
if (field.is_comptime or !field.ty.hasRuntimeBits(mod)) continue; if (field.is_comptime or !field.ty.hasRuntimeBits(mod)) continue;
const field_val = switch (aggregate.storage) { const field_val = switch (aggregate.storage) {
.bytes => |bytes| try mod.intern_pool.get(mod.gpa, .{ .int = .{ .bytes => |bytes| try ip.get(mod.gpa, .{ .int = .{
.ty = field.ty.toIntern(), .ty = field.ty.toIntern(),
.storage = .{ .u64 = bytes[i] }, .storage = .{ .u64 = bytes[i] },
} }), } }),
@ -828,13 +829,13 @@ pub const DeclGen = struct {
return try self.lower(ty.unionTagTypeSafety(mod).?, un.tag.toValue()); return try self.lower(ty.unionTagTypeSafety(mod).?, un.tag.toValue());
} }
const union_ty = mod.typeToUnion(ty).?; const union_obj = mod.typeToUnion(ty).?;
if (union_ty.layout == .Packed) { if (union_obj.getLayout(ip) == .Packed) {
return dg.todo("packed union constants", .{}); return dg.todo("packed union constants", .{});
} }
const active_field = ty.unionTagFieldIndex(un.tag.toValue(), dg.module).?; const active_field = ty.unionTagFieldIndex(un.tag.toValue(), dg.module).?;
const active_field_ty = union_ty.fields.values()[active_field].ty; const active_field_ty = union_obj.field_types.get(ip)[active_field].toType();
const has_tag = layout.tag_size != 0; const has_tag = layout.tag_size != 0;
const tag_first = layout.tag_align >= layout.payload_align; const tag_first = layout.tag_align >= layout.payload_align;
@ -1162,16 +1163,17 @@ pub const DeclGen = struct {
/// resulting struct will be *underaligned*. /// resulting struct will be *underaligned*.
fn resolveUnionType(self: *DeclGen, ty: Type, maybe_active_field: ?usize) !CacheRef { fn resolveUnionType(self: *DeclGen, ty: Type, maybe_active_field: ?usize) !CacheRef {
const mod = self.module; const mod = self.module;
const ip = &mod.intern_pool;
const layout = ty.unionGetLayout(mod); const layout = ty.unionGetLayout(mod);
const union_ty = mod.typeToUnion(ty).?; const union_obj = mod.typeToUnion(ty).?;
if (union_ty.layout == .Packed) { if (union_obj.getLayout(ip) == .Packed) {
return self.todo("packed union types", .{}); return self.todo("packed union types", .{});
} }
if (layout.payload_size == 0) { if (layout.payload_size == 0) {
// No payload, so represent this as just the tag type. // No payload, so represent this as just the tag type.
return try self.resolveType(union_ty.tag_ty, .indirect); return try self.resolveType(union_obj.enum_tag_ty.toType(), .indirect);
} }
var member_types = std.BoundedArray(CacheRef, 4){}; var member_types = std.BoundedArray(CacheRef, 4){};
@ -1182,13 +1184,13 @@ pub const DeclGen = struct {
const u8_ty_ref = try self.intType(.unsigned, 8); // TODO: What if Int8Type is not enabled? const u8_ty_ref = try self.intType(.unsigned, 8); // TODO: What if Int8Type is not enabled?
if (has_tag and tag_first) { if (has_tag and tag_first) {
const tag_ty_ref = try self.resolveType(union_ty.tag_ty, .indirect); const tag_ty_ref = try self.resolveType(union_obj.enum_tag_ty.toType(), .indirect);
member_types.appendAssumeCapacity(tag_ty_ref); member_types.appendAssumeCapacity(tag_ty_ref);
member_names.appendAssumeCapacity(try self.spv.resolveString("tag")); member_names.appendAssumeCapacity(try self.spv.resolveString("tag"));
} }
const active_field = maybe_active_field orelse layout.most_aligned_field; const active_field = maybe_active_field orelse layout.most_aligned_field;
const active_field_ty = union_ty.fields.values()[active_field].ty; const active_field_ty = union_obj.field_types.get(ip)[active_field].toType();
const active_field_size = if (active_field_ty.hasRuntimeBitsIgnoreComptime(mod)) blk: { const active_field_size = if (active_field_ty.hasRuntimeBitsIgnoreComptime(mod)) blk: {
const active_payload_ty_ref = try self.resolveType(active_field_ty, .indirect); const active_payload_ty_ref = try self.resolveType(active_field_ty, .indirect);
@ -1205,7 +1207,7 @@ pub const DeclGen = struct {
} }
if (has_tag and !tag_first) { if (has_tag and !tag_first) {
const tag_ty_ref = try self.resolveType(union_ty.tag_ty, .indirect); const tag_ty_ref = try self.resolveType(union_obj.enum_tag_ty.toType(), .indirect);
member_types.appendAssumeCapacity(tag_ty_ref); member_types.appendAssumeCapacity(tag_ty_ref);
member_names.appendAssumeCapacity(try self.spv.resolveString("tag")); member_names.appendAssumeCapacity(try self.spv.resolveString("tag"));
} }

View File

@ -166,6 +166,7 @@ pub const DeclState = struct {
const dbg_info_buffer = &self.dbg_info; const dbg_info_buffer = &self.dbg_info;
const target = mod.getTarget(); const target = mod.getTarget();
const target_endian = target.cpu.arch.endian(); const target_endian = target.cpu.arch.endian();
const ip = &mod.intern_pool;
switch (ty.zigTypeTag(mod)) { switch (ty.zigTypeTag(mod)) {
.NoReturn => unreachable, .NoReturn => unreachable,
@ -321,7 +322,7 @@ pub const DeclState = struct {
// DW.AT.byte_size, DW.FORM.udata // DW.AT.byte_size, DW.FORM.udata
try leb128.writeULEB128(dbg_info_buffer.writer(), ty.abiSize(mod)); try leb128.writeULEB128(dbg_info_buffer.writer(), ty.abiSize(mod));
switch (mod.intern_pool.indexToKey(ty.ip_index)) { switch (ip.indexToKey(ty.ip_index)) {
.anon_struct_type => |fields| { .anon_struct_type => |fields| {
// DW.AT.name, DW.FORM.string // DW.AT.name, DW.FORM.string
try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(mod)}); try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(mod)});
@ -357,7 +358,7 @@ pub const DeclState = struct {
0.., 0..,
) |field_name_ip, field, field_index| { ) |field_name_ip, field, field_index| {
if (!field.ty.hasRuntimeBits(mod)) continue; if (!field.ty.hasRuntimeBits(mod)) continue;
const field_name = mod.intern_pool.stringToSlice(field_name_ip); const field_name = ip.stringToSlice(field_name_ip);
// DW.AT.member // DW.AT.member
try dbg_info_buffer.ensureUnusedCapacity(field_name.len + 2); try dbg_info_buffer.ensureUnusedCapacity(field_name.len + 2);
dbg_info_buffer.appendAssumeCapacity(@intFromEnum(AbbrevKind.struct_member)); dbg_info_buffer.appendAssumeCapacity(@intFromEnum(AbbrevKind.struct_member));
@ -388,7 +389,6 @@ pub const DeclState = struct {
try ty.print(dbg_info_buffer.writer(), mod); try ty.print(dbg_info_buffer.writer(), mod);
try dbg_info_buffer.append(0); try dbg_info_buffer.append(0);
const ip = &mod.intern_pool;
const enum_type = ip.indexToKey(ty.ip_index).enum_type; const enum_type = ip.indexToKey(ty.ip_index).enum_type;
for (enum_type.names.get(ip), 0..) |field_name_index, field_i| { for (enum_type.names.get(ip), 0..) |field_name_index, field_i| {
const field_name = ip.stringToSlice(field_name_index); const field_name = ip.stringToSlice(field_name_index);
@ -414,8 +414,8 @@ pub const DeclState = struct {
try dbg_info_buffer.append(0); try dbg_info_buffer.append(0);
}, },
.Union => { .Union => {
const layout = ty.unionGetLayout(mod);
const union_obj = mod.typeToUnion(ty).?; const union_obj = mod.typeToUnion(ty).?;
const layout = mod.getUnionLayout(union_obj);
const payload_offset = if (layout.tag_align >= layout.payload_align) layout.tag_size else 0; const payload_offset = if (layout.tag_align >= layout.payload_align) layout.tag_size else 0;
const tag_offset = if (layout.tag_align >= layout.payload_align) 0 else layout.payload_size; const tag_offset = if (layout.tag_align >= layout.payload_align) 0 else layout.payload_size;
// TODO this is temporary to match current state of unions in Zig - we don't yet have // TODO this is temporary to match current state of unions in Zig - we don't yet have
@ -457,19 +457,17 @@ pub const DeclState = struct {
try dbg_info_buffer.append(0); try dbg_info_buffer.append(0);
} }
const fields = ty.unionFields(mod); for (union_obj.field_types.get(ip), union_obj.field_names.get(ip)) |field_ty, field_name| {
for (fields.keys()) |field_name| { if (!field_ty.toType().hasRuntimeBits(mod)) continue;
const field = fields.get(field_name).?;
if (!field.ty.hasRuntimeBits(mod)) continue;
// DW.AT.member // DW.AT.member
try dbg_info_buffer.append(@intFromEnum(AbbrevKind.struct_member)); try dbg_info_buffer.append(@intFromEnum(AbbrevKind.struct_member));
// DW.AT.name, DW.FORM.string // DW.AT.name, DW.FORM.string
try dbg_info_buffer.appendSlice(mod.intern_pool.stringToSlice(field_name)); try dbg_info_buffer.appendSlice(ip.stringToSlice(field_name));
try dbg_info_buffer.append(0); try dbg_info_buffer.append(0);
// DW.AT.type, DW.FORM.ref4 // DW.AT.type, DW.FORM.ref4
const index = dbg_info_buffer.items.len; const index = dbg_info_buffer.items.len;
try dbg_info_buffer.resize(index + 4); try dbg_info_buffer.resize(index + 4);
try self.addTypeRelocGlobal(atom_index, field.ty, @as(u32, @intCast(index))); try self.addTypeRelocGlobal(atom_index, field_ty.toType(), @intCast(index));
// DW.AT.data_member_location, DW.FORM.udata // DW.AT.data_member_location, DW.FORM.udata
try dbg_info_buffer.append(0); try dbg_info_buffer.append(0);
} }
@ -486,7 +484,7 @@ pub const DeclState = struct {
// DW.AT.type, DW.FORM.ref4 // DW.AT.type, DW.FORM.ref4
const index = dbg_info_buffer.items.len; const index = dbg_info_buffer.items.len;
try dbg_info_buffer.resize(index + 4); try dbg_info_buffer.resize(index + 4);
try self.addTypeRelocGlobal(atom_index, union_obj.tag_ty, @as(u32, @intCast(index))); try self.addTypeRelocGlobal(atom_index, union_obj.enum_tag_ty.toType(), @intCast(index));
// DW.AT.data_member_location, DW.FORM.udata // DW.AT.data_member_location, DW.FORM.udata
try leb128.writeULEB128(dbg_info_buffer.writer(), tag_offset); try leb128.writeULEB128(dbg_info_buffer.writer(), tag_offset);

View File

@ -349,8 +349,7 @@ pub const Type = struct {
}, },
.union_type => |union_type| { .union_type => |union_type| {
const union_obj = mod.unionPtr(union_type.index); const decl = mod.declPtr(union_type.decl);
const decl = mod.declPtr(union_obj.owner_decl);
try decl.renderFullyQualifiedName(mod, writer); try decl.renderFullyQualifiedName(mod, writer);
}, },
.opaque_type => |opaque_type| { .opaque_type => |opaque_type| {
@ -462,10 +461,11 @@ pub const Type = struct {
ignore_comptime_only: bool, ignore_comptime_only: bool,
strat: AbiAlignmentAdvancedStrat, strat: AbiAlignmentAdvancedStrat,
) RuntimeBitsError!bool { ) RuntimeBitsError!bool {
const ip = &mod.intern_pool;
return switch (ty.toIntern()) { return switch (ty.toIntern()) {
// False because it is a comptime-only type. // False because it is a comptime-only type.
.empty_struct_type => false, .empty_struct_type => false,
else => switch (mod.intern_pool.indexToKey(ty.toIntern())) { else => switch (ip.indexToKey(ty.toIntern())) {
.int_type => |int_type| int_type.bits != 0, .int_type => |int_type| int_type.bits != 0,
.ptr_type => |ptr_type| { .ptr_type => |ptr_type| {
// Pointers to zero-bit types still have a runtime address; however, pointers // Pointers to zero-bit types still have a runtime address; however, pointers
@ -595,29 +595,36 @@ pub const Type = struct {
}, },
.union_type => |union_type| { .union_type => |union_type| {
const union_obj = mod.unionPtr(union_type.index); switch (union_type.flagsPtr(ip).runtime_tag) {
switch (union_type.runtime_tag) {
.none => { .none => {
if (union_obj.status == .field_types_wip) { if (union_type.flagsPtr(ip).status == .field_types_wip) {
// In this case, we guess that hasRuntimeBits() for this type is true, // In this case, we guess that hasRuntimeBits() for this type is true,
// and then later if our guess was incorrect, we emit a compile error. // and then later if our guess was incorrect, we emit a compile error.
union_obj.assumed_runtime_bits = true; union_type.flagsPtr(ip).assumed_runtime_bits = true;
return true; return true;
} }
}, },
.safety, .tagged => { .safety, .tagged => {
if (try union_obj.tag_ty.hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat)) { const tag_ty = union_type.tagTypePtr(ip).*;
// tag_ty will be `none` if this union's tag type is not resolved yet,
// in which case we want control flow to continue down below.
if (tag_ty != .none and
try tag_ty.toType().hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat))
{
return true; return true;
} }
}, },
} }
switch (strat) { switch (strat) {
.sema => |sema| _ = try sema.resolveTypeFields(ty), .sema => |sema| _ = try sema.resolveTypeFields(ty),
.eager => assert(union_obj.haveFieldTypes()), .eager => assert(union_type.flagsPtr(ip).status.haveFieldTypes()),
.lazy => if (!union_obj.haveFieldTypes()) return error.NeedLazy, .lazy => if (!union_type.flagsPtr(ip).status.haveFieldTypes())
return error.NeedLazy,
} }
for (union_obj.fields.values()) |value| { const union_obj = ip.loadUnionType(union_type);
if (try value.ty.hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat)) for (0..union_obj.field_types.len) |field_index| {
const field_ty = union_obj.field_types.get(ip)[field_index].toType();
if (try field_ty.hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat))
return true; return true;
} else { } else {
return false; return false;
@ -656,7 +663,8 @@ pub const Type = struct {
/// readFrom/writeToMemory are supported only for types with a well- /// readFrom/writeToMemory are supported only for types with a well-
/// defined memory layout /// defined memory layout
pub fn hasWellDefinedLayout(ty: Type, mod: *Module) bool { pub fn hasWellDefinedLayout(ty: Type, mod: *Module) bool {
return switch (mod.intern_pool.indexToKey(ty.toIntern())) { const ip = &mod.intern_pool;
return switch (ip.indexToKey(ty.toIntern())) {
.int_type, .int_type,
.vector_type, .vector_type,
=> true, => true,
@ -728,8 +736,8 @@ pub const Type = struct {
}; };
return struct_obj.layout != .Auto; return struct_obj.layout != .Auto;
}, },
.union_type => |union_type| switch (union_type.runtime_tag) { .union_type => |union_type| switch (union_type.flagsPtr(ip).runtime_tag) {
.none, .safety => mod.unionPtr(union_type.index).layout != .Auto, .none, .safety => union_type.flagsPtr(ip).layout != .Auto,
.tagged => false, .tagged => false,
}, },
.enum_type => |enum_type| switch (enum_type.tag_mode) { .enum_type => |enum_type| switch (enum_type.tag_mode) {
@ -867,6 +875,7 @@ pub const Type = struct {
strat: AbiAlignmentAdvancedStrat, strat: AbiAlignmentAdvancedStrat,
) Module.CompileError!AbiAlignmentAdvanced { ) Module.CompileError!AbiAlignmentAdvanced {
const target = mod.getTarget(); const target = mod.getTarget();
const ip = &mod.intern_pool;
const opt_sema = switch (strat) { const opt_sema = switch (strat) {
.sema => |sema| sema, .sema => |sema| sema,
@ -875,7 +884,7 @@ pub const Type = struct {
switch (ty.toIntern()) { switch (ty.toIntern()) {
.empty_struct_type => return AbiAlignmentAdvanced{ .scalar = 0 }, .empty_struct_type => return AbiAlignmentAdvanced{ .scalar = 0 },
else => switch (mod.intern_pool.indexToKey(ty.toIntern())) { else => switch (ip.indexToKey(ty.toIntern())) {
.int_type => |int_type| { .int_type => |int_type| {
if (int_type.bits == 0) return AbiAlignmentAdvanced{ .scalar = 0 }; if (int_type.bits == 0) return AbiAlignmentAdvanced{ .scalar = 0 };
return AbiAlignmentAdvanced{ .scalar = intAbiAlignment(int_type.bits, target) }; return AbiAlignmentAdvanced{ .scalar = intAbiAlignment(int_type.bits, target) };
@ -1066,8 +1075,65 @@ pub const Type = struct {
}, },
.union_type => |union_type| { .union_type => |union_type| {
const union_obj = mod.unionPtr(union_type.index); if (opt_sema) |sema| {
return abiAlignmentAdvancedUnion(ty, mod, strat, union_obj, union_type.hasTag()); 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 AbiAlignmentAdvanced{ .scalar = @divExact(target.ptrBitWidth(), 8) };
}
_ = try sema.resolveTypeFields(ty);
}
if (!union_type.haveFieldTypes(ip)) switch (strat) {
.eager => unreachable, // union layout not resolved
.sema => unreachable, // handled above
.lazy => return .{ .val = (try mod.intern(.{ .int = .{
.ty = .comptime_int_type,
.storage = .{ .lazy_align = ty.toIntern() },
} })).toValue() },
};
const union_obj = ip.loadUnionType(union_type);
if (union_obj.field_names.len == 0) {
if (union_obj.hasTag(ip)) {
return abiAlignmentAdvanced(union_obj.enum_tag_ty.toType(), mod, strat);
} else {
return AbiAlignmentAdvanced{
.scalar = @intFromBool(union_obj.flagsPtr(ip).layout == .Extern),
};
}
}
var max_align: u32 = 0;
if (union_obj.hasTag(ip)) max_align = union_obj.enum_tag_ty.toType().abiAlignment(mod);
for (0..union_obj.field_names.len) |field_index| {
const field_ty = union_obj.field_types.get(ip)[field_index].toType();
const field_align = if (union_obj.field_aligns.len == 0)
.none
else
union_obj.field_aligns.get(ip)[field_index];
if (!(field_ty.hasRuntimeBitsAdvanced(mod, false, strat) catch |err| switch (err) {
error.NeedLazy => return .{ .val = (try mod.intern(.{ .int = .{
.ty = .comptime_int_type,
.storage = .{ .lazy_align = ty.toIntern() },
} })).toValue() },
else => |e| return e,
})) continue;
const field_align_bytes: u32 = @intCast(field_align.toByteUnitsOptional() orelse
switch (try field_ty.abiAlignmentAdvanced(mod, strat)) {
.scalar => |a| a,
.val => switch (strat) {
.eager => unreachable, // struct layout not resolved
.sema => unreachable, // handled above
.lazy => return .{ .val = (try mod.intern(.{ .int = .{
.ty = .comptime_int_type,
.storage = .{ .lazy_align = ty.toIntern() },
} })).toValue() },
},
});
max_align = @max(max_align, field_align_bytes);
}
return AbiAlignmentAdvanced{ .scalar = max_align };
}, },
.opaque_type => return AbiAlignmentAdvanced{ .scalar = 1 }, .opaque_type => return AbiAlignmentAdvanced{ .scalar = 1 },
.enum_type => |enum_type| return AbiAlignmentAdvanced{ .scalar = enum_type.tag_ty.toType().abiAlignment(mod) }, .enum_type => |enum_type| return AbiAlignmentAdvanced{ .scalar = enum_type.tag_ty.toType().abiAlignment(mod) },
@ -1177,71 +1243,6 @@ pub const Type = struct {
} }
} }
pub fn abiAlignmentAdvancedUnion(
ty: Type,
mod: *Module,
strat: AbiAlignmentAdvancedStrat,
union_obj: *Module.Union,
have_tag: bool,
) Module.CompileError!AbiAlignmentAdvanced {
const opt_sema = switch (strat) {
.sema => |sema| sema,
else => null,
};
if (opt_sema) |sema| {
if (union_obj.status == .field_types_wip) {
// We'll guess "pointer-aligned", if the union has an
// underaligned pointer field then some allocations
// might require explicit alignment.
const target = mod.getTarget();
return AbiAlignmentAdvanced{ .scalar = @divExact(target.ptrBitWidth(), 8) };
}
_ = try sema.resolveTypeFields(ty);
}
if (!union_obj.haveFieldTypes()) switch (strat) {
.eager => unreachable, // union layout not resolved
.sema => unreachable, // handled above
.lazy => return .{ .val = (try mod.intern(.{ .int = .{
.ty = .comptime_int_type,
.storage = .{ .lazy_align = ty.toIntern() },
} })).toValue() },
};
if (union_obj.fields.count() == 0) {
if (have_tag) {
return abiAlignmentAdvanced(union_obj.tag_ty, mod, strat);
} else {
return AbiAlignmentAdvanced{ .scalar = @intFromBool(union_obj.layout == .Extern) };
}
}
var max_align: u32 = 0;
if (have_tag) max_align = union_obj.tag_ty.abiAlignment(mod);
for (union_obj.fields.values()) |field| {
if (!(field.ty.hasRuntimeBitsAdvanced(mod, false, strat) catch |err| switch (err) {
error.NeedLazy => return .{ .val = (try mod.intern(.{ .int = .{
.ty = .comptime_int_type,
.storage = .{ .lazy_align = ty.toIntern() },
} })).toValue() },
else => |e| return e,
})) continue;
const field_align = @as(u32, @intCast(field.abi_align.toByteUnitsOptional() orelse
switch (try field.ty.abiAlignmentAdvanced(mod, strat)) {
.scalar => |a| a,
.val => switch (strat) {
.eager => unreachable, // struct layout not resolved
.sema => unreachable, // handled above
.lazy => return .{ .val = (try mod.intern(.{ .int = .{
.ty = .comptime_int_type,
.storage = .{ .lazy_align = ty.toIntern() },
} })).toValue() },
},
}));
max_align = @max(max_align, field_align);
}
return AbiAlignmentAdvanced{ .scalar = max_align };
}
/// May capture a reference to `ty`. /// May capture a reference to `ty`.
pub fn lazyAbiSize(ty: Type, mod: *Module) !Value { pub fn lazyAbiSize(ty: Type, mod: *Module) !Value {
switch (try ty.abiSizeAdvanced(mod, .lazy)) { switch (try ty.abiSizeAdvanced(mod, .lazy)) {
@ -1273,11 +1274,12 @@ pub const Type = struct {
strat: AbiAlignmentAdvancedStrat, strat: AbiAlignmentAdvancedStrat,
) Module.CompileError!AbiSizeAdvanced { ) Module.CompileError!AbiSizeAdvanced {
const target = mod.getTarget(); const target = mod.getTarget();
const ip = &mod.intern_pool;
switch (ty.toIntern()) { switch (ty.toIntern()) {
.empty_struct_type => return AbiSizeAdvanced{ .scalar = 0 }, .empty_struct_type => return AbiSizeAdvanced{ .scalar = 0 },
else => switch (mod.intern_pool.indexToKey(ty.toIntern())) { else => switch (ip.indexToKey(ty.toIntern())) {
.int_type => |int_type| { .int_type => |int_type| {
if (int_type.bits == 0) return AbiSizeAdvanced{ .scalar = 0 }; if (int_type.bits == 0) return AbiSizeAdvanced{ .scalar = 0 };
return AbiSizeAdvanced{ .scalar = intAbiSize(int_type.bits, target) }; return AbiSizeAdvanced{ .scalar = intAbiSize(int_type.bits, target) };
@ -1484,8 +1486,18 @@ pub const Type = struct {
}, },
.union_type => |union_type| { .union_type => |union_type| {
const union_obj = mod.unionPtr(union_type.index); switch (strat) {
return abiSizeAdvancedUnion(ty, mod, strat, union_obj, union_type.hasTag()); .sema => |sema| try sema.resolveTypeLayout(ty),
.lazy => if (!union_type.flagsPtr(ip).status.haveLayout()) return .{
.val = (try mod.intern(.{ .int = .{
.ty = .comptime_int_type,
.storage = .{ .lazy_size = ty.toIntern() },
} })).toValue(),
},
.eager => {},
}
const union_obj = ip.loadUnionType(union_type);
return AbiSizeAdvanced{ .scalar = mod.unionAbiSize(union_obj) };
}, },
.opaque_type => unreachable, // no size available .opaque_type => unreachable, // no size available
.enum_type => |enum_type| return AbiSizeAdvanced{ .scalar = enum_type.tag_ty.toType().abiSize(mod) }, .enum_type => |enum_type| return AbiSizeAdvanced{ .scalar = enum_type.tag_ty.toType().abiSize(mod) },
@ -1515,24 +1527,6 @@ pub const Type = struct {
} }
} }
pub fn abiSizeAdvancedUnion(
ty: Type,
mod: *Module,
strat: AbiAlignmentAdvancedStrat,
union_obj: *Module.Union,
have_tag: bool,
) Module.CompileError!AbiSizeAdvanced {
switch (strat) {
.sema => |sema| try sema.resolveTypeLayout(ty),
.lazy => if (!union_obj.haveLayout()) return .{ .val = (try mod.intern(.{ .int = .{
.ty = .comptime_int_type,
.storage = .{ .lazy_size = ty.toIntern() },
} })).toValue() },
.eager => {},
}
return AbiSizeAdvanced{ .scalar = union_obj.abiSize(mod, have_tag) };
}
fn abiSizeAdvancedOptional( fn abiSizeAdvancedOptional(
ty: Type, ty: Type,
mod: *Module, mod: *Module,
@ -1602,10 +1596,11 @@ pub const Type = struct {
opt_sema: ?*Sema, opt_sema: ?*Sema,
) Module.CompileError!u64 { ) Module.CompileError!u64 {
const target = mod.getTarget(); const target = mod.getTarget();
const ip = &mod.intern_pool;
const strat: AbiAlignmentAdvancedStrat = if (opt_sema) |sema| .{ .sema = sema } else .eager; const strat: AbiAlignmentAdvancedStrat = if (opt_sema) |sema| .{ .sema = sema } else .eager;
switch (mod.intern_pool.indexToKey(ty.toIntern())) { switch (ip.indexToKey(ty.toIntern())) {
.int_type => |int_type| return int_type.bits, .int_type => |int_type| return int_type.bits,
.ptr_type => |ptr_type| switch (ptr_type.flags.size) { .ptr_type => |ptr_type| switch (ptr_type.flags.size) {
.Slice => return target.ptrBitWidth() * 2, .Slice => return target.ptrBitWidth() * 2,
@ -1714,12 +1709,13 @@ pub const Type = struct {
if (ty.containerLayout(mod) != .Packed) { if (ty.containerLayout(mod) != .Packed) {
return (try ty.abiSizeAdvanced(mod, strat)).scalar * 8; return (try ty.abiSizeAdvanced(mod, strat)).scalar * 8;
} }
const union_obj = mod.unionPtr(union_type.index); const union_obj = ip.loadUnionType(union_type);
assert(union_obj.haveFieldTypes()); assert(union_obj.flagsPtr(ip).status.haveFieldTypes());
var size: u64 = 0; var size: u64 = 0;
for (union_obj.fields.values()) |field| { for (0..union_obj.field_types.len) |field_index| {
size = @max(size, try bitSizeAdvanced(field.ty, mod, opt_sema)); const field_ty = union_obj.field_types.get(ip)[field_index];
size = @max(size, try bitSizeAdvanced(field_ty.toType(), mod, opt_sema));
} }
return size; return size;
}, },
@ -1753,33 +1749,24 @@ pub const Type = struct {
/// Returns true if the type's layout is already resolved and it is safe /// Returns true if the type's layout is already resolved and it is safe
/// to use `abiSize`, `abiAlignment` and `bitSize` on it. /// to use `abiSize`, `abiAlignment` and `bitSize` on it.
pub fn layoutIsResolved(ty: Type, mod: *Module) bool { pub fn layoutIsResolved(ty: Type, mod: *Module) bool {
switch (ty.zigTypeTag(mod)) { const ip = &mod.intern_pool;
.Struct => { return switch (ip.indexToKey(ty.toIntern())) {
if (mod.typeToStruct(ty)) |struct_obj| { .struct_type => |struct_type| {
if (mod.structPtrUnwrap(struct_type.index)) |struct_obj| {
return struct_obj.haveLayout(); return struct_obj.haveLayout();
} else {
return true;
} }
return true;
}, },
.Union => { .union_type => |union_type| union_type.haveLayout(ip),
if (mod.typeToUnion(ty)) |union_obj| { .array_type => |array_type| {
return union_obj.haveLayout(); if ((array_type.len + @intFromBool(array_type.sentinel != .none)) == 0) return true;
} return array_type.child.toType().layoutIsResolved(mod);
return true;
}, },
.Array => { .opt_type => |child| child.toType().layoutIsResolved(mod),
if (ty.arrayLenIncludingSentinel(mod) == 0) return true; .error_union_type => |k| k.payload_type.toType().layoutIsResolved(mod),
return ty.childType(mod).layoutIsResolved(mod); else => true,
}, };
.Optional => {
const payload_ty = ty.optionalChild(mod);
return payload_ty.layoutIsResolved(mod);
},
.ErrorUnion => {
const payload_ty = ty.errorUnionPayload(mod);
return payload_ty.layoutIsResolved(mod);
},
else => return true,
}
} }
pub fn isSinglePointer(ty: Type, mod: *const Module) bool { pub fn isSinglePointer(ty: Type, mod: *const Module) bool {
@ -1970,12 +1957,12 @@ pub const Type = struct {
/// Returns the tag type of a union, if the type is a union and it has a tag type. /// Returns the tag type of a union, if the type is a union and it has a tag type.
/// Otherwise, returns `null`. /// Otherwise, returns `null`.
pub fn unionTagType(ty: Type, mod: *Module) ?Type { pub fn unionTagType(ty: Type, mod: *Module) ?Type {
return switch (mod.intern_pool.indexToKey(ty.toIntern())) { const ip = &mod.intern_pool;
.union_type => |union_type| switch (union_type.runtime_tag) { return switch (ip.indexToKey(ty.toIntern())) {
.union_type => |union_type| switch (union_type.flagsPtr(ip).runtime_tag) {
.tagged => { .tagged => {
const union_obj = mod.unionPtr(union_type.index); assert(union_type.flagsPtr(ip).status.haveFieldTypes());
assert(union_obj.haveFieldTypes()); return union_type.enum_tag_ty.toType();
return union_obj.tag_ty;
}, },
else => null, else => null,
}, },
@ -1986,12 +1973,12 @@ pub const Type = struct {
/// Same as `unionTagType` but includes safety tag. /// Same as `unionTagType` but includes safety tag.
/// Codegen should use this version. /// Codegen should use this version.
pub fn unionTagTypeSafety(ty: Type, mod: *Module) ?Type { pub fn unionTagTypeSafety(ty: Type, mod: *Module) ?Type {
return switch (mod.intern_pool.indexToKey(ty.toIntern())) { const ip = &mod.intern_pool;
return switch (ip.indexToKey(ty.toIntern())) {
.union_type => |union_type| { .union_type => |union_type| {
if (!union_type.hasTag()) return null; if (!union_type.hasTag(ip)) return null;
const union_obj = mod.unionPtr(union_type.index); assert(union_type.haveFieldTypes(ip));
assert(union_obj.haveFieldTypes()); return union_type.enum_tag_ty.toType();
return union_obj.tag_ty;
}, },
else => null, else => null,
}; };
@ -2001,52 +1988,46 @@ pub const Type = struct {
/// not be stored at runtime. /// not be stored at runtime.
pub fn unionTagTypeHypothetical(ty: Type, mod: *Module) Type { pub fn unionTagTypeHypothetical(ty: Type, mod: *Module) Type {
const union_obj = mod.typeToUnion(ty).?; const union_obj = mod.typeToUnion(ty).?;
assert(union_obj.haveFieldTypes()); return union_obj.enum_tag_ty.toType();
return union_obj.tag_ty;
}
pub fn unionFields(ty: Type, mod: *Module) Module.Union.Fields {
const union_obj = mod.typeToUnion(ty).?;
assert(union_obj.haveFieldTypes());
return union_obj.fields;
} }
pub fn unionFieldType(ty: Type, enum_tag: Value, mod: *Module) Type { pub fn unionFieldType(ty: Type, enum_tag: Value, mod: *Module) Type {
const ip = &mod.intern_pool;
const union_obj = mod.typeToUnion(ty).?; const union_obj = mod.typeToUnion(ty).?;
const index = ty.unionTagFieldIndex(enum_tag, mod).?; const index = mod.unionTagFieldIndex(union_obj, enum_tag).?;
assert(union_obj.haveFieldTypes()); return union_obj.field_types.get(ip)[index].toType();
return union_obj.fields.values()[index].ty;
} }
pub fn unionTagFieldIndex(ty: Type, enum_tag: Value, mod: *Module) ?usize { pub fn unionTagFieldIndex(ty: Type, enum_tag: Value, mod: *Module) ?u32 {
const union_obj = mod.typeToUnion(ty).?; const union_obj = mod.typeToUnion(ty).?;
const index = union_obj.tag_ty.enumTagFieldIndex(enum_tag, mod) orelse return null; return mod.unionTagFieldIndex(union_obj, enum_tag);
const name = union_obj.tag_ty.enumFieldName(index, mod);
return union_obj.fields.getIndex(name);
} }
pub fn unionHasAllZeroBitFieldTypes(ty: Type, mod: *Module) bool { pub fn unionHasAllZeroBitFieldTypes(ty: Type, mod: *Module) bool {
const ip = &mod.intern_pool;
const union_obj = mod.typeToUnion(ty).?; const union_obj = mod.typeToUnion(ty).?;
return union_obj.hasAllZeroBitFieldTypes(mod); for (union_obj.field_types.get(ip)) |field_ty| {
if (field_ty.toType().hasRuntimeBits(mod)) return false;
}
return true;
} }
pub fn unionGetLayout(ty: Type, mod: *Module) Module.Union.Layout { pub fn unionGetLayout(ty: Type, mod: *Module) Module.UnionLayout {
const union_type = mod.intern_pool.indexToKey(ty.toIntern()).union_type; const ip = &mod.intern_pool;
const union_obj = mod.unionPtr(union_type.index); const union_type = ip.indexToKey(ty.toIntern()).union_type;
return union_obj.getLayout(mod, union_type.hasTag()); const union_obj = ip.loadUnionType(union_type);
return mod.getUnionLayout(union_obj);
} }
pub fn containerLayout(ty: Type, mod: *Module) std.builtin.Type.ContainerLayout { pub fn containerLayout(ty: Type, mod: *Module) std.builtin.Type.ContainerLayout {
return switch (mod.intern_pool.indexToKey(ty.toIntern())) { const ip = &mod.intern_pool;
return switch (ip.indexToKey(ty.toIntern())) {
.struct_type => |struct_type| { .struct_type => |struct_type| {
const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse return .Auto; const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse return .Auto;
return struct_obj.layout; return struct_obj.layout;
}, },
.anon_struct_type => .Auto, .anon_struct_type => .Auto,
.union_type => |union_type| { .union_type => |union_type| union_type.flagsPtr(ip).layout,
const union_obj = mod.unionPtr(union_type.index);
return union_obj.layout;
},
else => unreachable, else => unreachable,
}; };
} }
@ -2570,14 +2551,16 @@ pub const Type = struct {
}, },
.union_type => |union_type| { .union_type => |union_type| {
const union_obj = mod.unionPtr(union_type.index); const union_obj = ip.loadUnionType(union_type);
const tag_val = (try union_obj.tag_ty.onePossibleValue(mod)) orelse return null; const tag_val = (try union_obj.enum_tag_ty.toType().onePossibleValue(mod)) orelse
if (union_obj.fields.count() == 0) { return null;
if (union_obj.field_names.len == 0) {
const only = try mod.intern(.{ .empty_enum_value = ty.toIntern() }); const only = try mod.intern(.{ .empty_enum_value = ty.toIntern() });
return only.toValue(); return only.toValue();
} }
const only_field = union_obj.fields.values()[0]; const only_field_ty = union_obj.field_types.get(ip)[0];
const val_val = (try only_field.ty.onePossibleValue(mod)) orelse return null; const val_val = (try only_field_ty.toType().onePossibleValue(mod)) orelse
return null;
const only = try mod.intern(.{ .un = .{ const only = try mod.intern(.{ .un = .{
.ty = ty.toIntern(), .ty = ty.toIntern(),
.tag = tag_val.toIntern(), .tag = tag_val.toIntern(),
@ -2657,10 +2640,11 @@ pub const Type = struct {
/// TODO merge these implementations together with the "advanced" pattern seen /// TODO merge these implementations together with the "advanced" pattern seen
/// elsewhere in this file. /// elsewhere in this file.
pub fn comptimeOnly(ty: Type, mod: *Module) bool { pub fn comptimeOnly(ty: Type, mod: *Module) bool {
const ip = &mod.intern_pool;
return switch (ty.toIntern()) { return switch (ty.toIntern()) {
.empty_struct_type => false, .empty_struct_type => false,
else => switch (mod.intern_pool.indexToKey(ty.toIntern())) { else => switch (ip.indexToKey(ty.toIntern())) {
.int_type => false, .int_type => false,
.ptr_type => |ptr_type| { .ptr_type => |ptr_type| {
const child_ty = ptr_type.child.toType(); const child_ty = ptr_type.child.toType();
@ -2704,6 +2688,7 @@ pub const Type = struct {
.c_longlong, .c_longlong,
.c_ulonglong, .c_ulonglong,
.c_longdouble, .c_longdouble,
.anyopaque,
.bool, .bool,
.void, .void,
.anyerror, .anyerror,
@ -2722,7 +2707,6 @@ pub const Type = struct {
.extern_options, .extern_options,
=> false, => false,
.anyopaque,
.type, .type,
.comptime_int, .comptime_int,
.comptime_float, .comptime_float,
@ -2756,8 +2740,7 @@ pub const Type = struct {
}, },
.union_type => |union_type| { .union_type => |union_type| {
const union_obj = mod.unionPtr(union_type.index); switch (union_type.flagsPtr(ip).requires_comptime) {
switch (union_obj.requires_comptime) {
.wip, .unknown => { .wip, .unknown => {
// Return false to avoid incorrect dependency loops. // Return false to avoid incorrect dependency loops.
// This will be handled correctly once merged with // This will be handled correctly once merged with
@ -2769,7 +2752,7 @@ pub const Type = struct {
} }
}, },
.opaque_type => true, .opaque_type => false,
.enum_type => |enum_type| enum_type.tag_ty.toType().comptimeOnly(mod), .enum_type => |enum_type| enum_type.tag_ty.toType().comptimeOnly(mod),
@ -2847,7 +2830,7 @@ pub const Type = struct {
return switch (mod.intern_pool.indexToKey(ty.toIntern())) { return switch (mod.intern_pool.indexToKey(ty.toIntern())) {
.opaque_type => |opaque_type| opaque_type.namespace.toOptional(), .opaque_type => |opaque_type| opaque_type.namespace.toOptional(),
.struct_type => |struct_type| struct_type.namespace, .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, .enum_type => |enum_type| enum_type.namespace,
else => .none, else => .none,
@ -2935,7 +2918,7 @@ pub const Type = struct {
/// Asserts the type is an enum or a union. /// Asserts the type is an enum or a union.
pub fn intTagType(ty: Type, mod: *Module) Type { pub fn intTagType(ty: Type, mod: *Module) Type {
return switch (mod.intern_pool.indexToKey(ty.toIntern())) { return switch (mod.intern_pool.indexToKey(ty.toIntern())) {
.union_type => |union_type| mod.unionPtr(union_type.index).tag_ty.intTagType(mod), .union_type => |union_type| union_type.enum_tag_ty.toType().intTagType(mod),
.enum_type => |enum_type| enum_type.tag_ty.toType(), .enum_type => |enum_type| enum_type.tag_ty.toType(),
else => unreachable, else => unreachable,
}; };
@ -3038,15 +3021,16 @@ pub const Type = struct {
/// Supports structs and unions. /// Supports structs and unions.
pub fn structFieldType(ty: Type, index: usize, mod: *Module) Type { pub fn structFieldType(ty: Type, index: usize, mod: *Module) Type {
return switch (mod.intern_pool.indexToKey(ty.toIntern())) { const ip = &mod.intern_pool;
return switch (ip.indexToKey(ty.toIntern())) {
.struct_type => |struct_type| { .struct_type => |struct_type| {
const struct_obj = mod.structPtrUnwrap(struct_type.index).?; const struct_obj = mod.structPtrUnwrap(struct_type.index).?;
assert(struct_obj.haveFieldTypes()); assert(struct_obj.haveFieldTypes());
return struct_obj.fields.values()[index].ty; return struct_obj.fields.values()[index].ty;
}, },
.union_type => |union_type| { .union_type => |union_type| {
const union_obj = mod.unionPtr(union_type.index); const union_obj = ip.loadUnionType(union_type);
return union_obj.fields.values()[index].ty; return union_obj.field_types.get(ip)[index].toType();
}, },
.anon_struct_type => |anon_struct| anon_struct.types[index].toType(), .anon_struct_type => |anon_struct| anon_struct.types[index].toType(),
else => unreachable, else => unreachable,
@ -3054,7 +3038,8 @@ pub const Type = struct {
} }
pub fn structFieldAlign(ty: Type, index: usize, mod: *Module) u32 { pub fn structFieldAlign(ty: Type, index: usize, mod: *Module) u32 {
switch (mod.intern_pool.indexToKey(ty.toIntern())) { const ip = &mod.intern_pool;
switch (ip.indexToKey(ty.toIntern())) {
.struct_type => |struct_type| { .struct_type => |struct_type| {
const struct_obj = mod.structPtrUnwrap(struct_type.index).?; const struct_obj = mod.structPtrUnwrap(struct_type.index).?;
assert(struct_obj.layout != .Packed); assert(struct_obj.layout != .Packed);
@ -3064,8 +3049,8 @@ pub const Type = struct {
return anon_struct.types[index].toType().abiAlignment(mod); return anon_struct.types[index].toType().abiAlignment(mod);
}, },
.union_type => |union_type| { .union_type => |union_type| {
const union_obj = mod.unionPtr(union_type.index); const union_obj = ip.loadUnionType(union_type);
return union_obj.fields.values()[index].normalAlignment(mod); return mod.unionFieldNormalAlignment(union_obj, @intCast(index));
}, },
else => unreachable, else => unreachable,
} }
@ -3198,7 +3183,8 @@ pub const Type = struct {
/// Supports structs and unions. /// Supports structs and unions.
pub fn structFieldOffset(ty: Type, index: usize, mod: *Module) u64 { pub fn structFieldOffset(ty: Type, index: usize, mod: *Module) u64 {
switch (mod.intern_pool.indexToKey(ty.toIntern())) { const ip = &mod.intern_pool;
switch (ip.indexToKey(ty.toIntern())) {
.struct_type => |struct_type| { .struct_type => |struct_type| {
const struct_obj = mod.structPtrUnwrap(struct_type.index).?; const struct_obj = mod.structPtrUnwrap(struct_type.index).?;
assert(struct_obj.haveLayout()); assert(struct_obj.haveLayout());
@ -3234,10 +3220,10 @@ pub const Type = struct {
}, },
.union_type => |union_type| { .union_type => |union_type| {
if (!union_type.hasTag()) if (!union_type.hasTag(ip))
return 0; return 0;
const union_obj = mod.unionPtr(union_type.index); const union_obj = ip.loadUnionType(union_type);
const layout = union_obj.getLayout(mod, true); const layout = mod.getUnionLayout(union_obj);
if (layout.tag_align >= layout.payload_align) { if (layout.tag_align >= layout.payload_align) {
// {Tag, Payload} // {Tag, Payload}
return std.mem.alignForward(u64, layout.tag_size, layout.payload_align); return std.mem.alignForward(u64, layout.tag_size, layout.payload_align);
@ -3262,8 +3248,7 @@ pub const Type = struct {
return struct_obj.srcLoc(mod); return struct_obj.srcLoc(mod);
}, },
.union_type => |union_type| { .union_type => |union_type| {
const union_obj = mod.unionPtr(union_type.index); return mod.declPtr(union_type.decl).srcLoc(mod);
return union_obj.srcLoc(mod);
}, },
.opaque_type => |opaque_type| mod.opaqueSrcLoc(opaque_type), .opaque_type => |opaque_type| mod.opaqueSrcLoc(opaque_type),
.enum_type => |enum_type| mod.declPtr(enum_type.decl).srcLoc(mod), .enum_type => |enum_type| mod.declPtr(enum_type.decl).srcLoc(mod),
@ -3281,10 +3266,7 @@ pub const Type = struct {
const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse return null; const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse return null;
return struct_obj.owner_decl; return struct_obj.owner_decl;
}, },
.union_type => |union_type| { .union_type => |union_type| union_type.decl,
const union_obj = mod.unionPtr(union_type.index);
return union_obj.owner_decl;
},
.opaque_type => |opaque_type| opaque_type.decl, .opaque_type => |opaque_type| opaque_type.decl,
.enum_type => |enum_type| enum_type.decl, .enum_type => |enum_type| enum_type.decl,
else => null, else => null,

View File

@ -734,6 +734,7 @@ pub const Value = struct {
buffer: []u8, buffer: []u8,
bit_offset: usize, bit_offset: usize,
) error{ ReinterpretDeclRef, OutOfMemory }!void { ) error{ ReinterpretDeclRef, OutOfMemory }!void {
const ip = &mod.intern_pool;
const target = mod.getTarget(); const target = mod.getTarget();
const endian = target.cpu.arch.endian(); const endian = target.cpu.arch.endian();
if (val.isUndef(mod)) { if (val.isUndef(mod)) {
@ -759,7 +760,7 @@ pub const Value = struct {
const bits = ty.intInfo(mod).bits; const bits = ty.intInfo(mod).bits;
if (bits == 0) return; if (bits == 0) return;
switch (mod.intern_pool.indexToKey((try val.intFromEnum(ty, mod)).toIntern()).int.storage) { switch (ip.indexToKey((try val.intFromEnum(ty, mod)).toIntern()).int.storage) {
inline .u64, .i64 => |int| std.mem.writeVarPackedInt(buffer, bit_offset, bits, int, endian), inline .u64, .i64 => |int| std.mem.writeVarPackedInt(buffer, bit_offset, bits, int, endian),
.big_int => |bigint| bigint.writePackedTwosComplement(buffer, bit_offset, bits, endian), .big_int => |bigint| bigint.writePackedTwosComplement(buffer, bit_offset, bits, endian),
else => unreachable, else => unreachable,
@ -794,7 +795,7 @@ pub const Value = struct {
.Packed => { .Packed => {
var bits: u16 = 0; var bits: u16 = 0;
const fields = ty.structFields(mod).values(); const fields = ty.structFields(mod).values();
const storage = mod.intern_pool.indexToKey(val.toIntern()).aggregate.storage; const storage = ip.indexToKey(val.toIntern()).aggregate.storage;
for (fields, 0..) |field, i| { for (fields, 0..) |field, i| {
const field_bits = @as(u16, @intCast(field.ty.bitSize(mod))); const field_bits = @as(u16, @intCast(field.ty.bitSize(mod)));
const field_val = switch (storage) { const field_val = switch (storage) {
@ -807,16 +808,19 @@ pub const Value = struct {
} }
}, },
}, },
.Union => switch (ty.containerLayout(mod)) { .Union => {
.Auto => unreachable, // Sema is supposed to have emitted a compile error already const union_obj = mod.typeToUnion(ty).?;
.Extern => unreachable, // Handled in non-packed writeToMemory switch (union_obj.getLayout(ip)) {
.Packed => { .Auto => unreachable, // Sema is supposed to have emitted a compile error already
const field_index = ty.unionTagFieldIndex(val.unionTag(mod), mod); .Extern => unreachable, // Handled in non-packed writeToMemory
const field_type = ty.unionFields(mod).values()[field_index.?].ty; .Packed => {
const field_val = try val.fieldValue(mod, field_index.?); const field_index = mod.unionTagFieldIndex(union_obj, val.unionTag(mod)).?;
const field_type = union_obj.field_types.get(ip)[field_index].toType();
const field_val = try val.fieldValue(mod, field_index);
return field_val.writeToPackedMemory(field_type, mod, buffer, bit_offset); return field_val.writeToPackedMemory(field_type, mod, buffer, bit_offset);
}, },
}
}, },
.Pointer => { .Pointer => {
assert(!ty.isSlice(mod)); // No well defined layout. assert(!ty.isSlice(mod)); // No well defined layout.

View File

@ -1347,31 +1347,6 @@ test "noreturn field in union" {
try expect(count == 6); try expect(count == 6);
} }
test "union and enum field order doesn't match" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
const MyTag = enum(u32) {
b = 1337,
a = 1666,
};
const MyUnion = union(MyTag) {
a: f32,
b: void,
};
var x: MyUnion = .{ .a = 666 };
switch (x) {
.a => |my_f32| {
try expect(@TypeOf(my_f32) == f32);
},
.b => unreachable,
}
x = .b;
try expect(x == .b);
}
test "@unionInit uses tag value instead of field index" { test "@unionInit uses tag value instead of field index" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
@ -1383,8 +1358,8 @@ test "@unionInit uses tag value instead of field index" {
a = 3, a = 3,
}; };
const U = union(E) { const U = union(E) {
a: usize,
b: isize, b: isize,
a: usize,
}; };
var i: isize = -1; var i: isize = -1;
var u = @unionInit(U, "b", i); var u = @unionInit(U, "b", i);

View File

@ -1,4 +1,4 @@
const Enum = enum(u32) { a, b }; const Enum = enum(u32) { b, a };
const TaggedUnion = union(Enum) { const TaggedUnion = union(Enum) {
b: []const u8, b: []const u8,
a: []const u8, a: []const u8,

View File

@ -45,8 +45,7 @@ pub export fn entry() void {
// backend=llvm // backend=llvm
// //
// :11:22: error: comparison of 'void' with null // :11:22: error: comparison of 'void' with null
// :25:51: error: values of type 'anyopaque' must be comptime-known, but operand value is runtime-known // :25:51: error: cannot load opaque type 'anyopaque'
// :25:51: note: opaque type 'anyopaque' has undefined size
// :25:51: error: values of type 'fn(*anyopaque, usize, u8, usize) ?[*]u8' must be comptime-known, but operand value is runtime-known // :25:51: error: values of type 'fn(*anyopaque, usize, u8, usize) ?[*]u8' must be comptime-known, but operand value is runtime-known
// :25:51: note: use '*const fn(*anyopaque, usize, u8, usize) ?[*]u8' for a function pointer type // :25:51: note: use '*const fn(*anyopaque, usize, u8, usize) ?[*]u8' for a function pointer type
// :25:51: error: values of type 'fn(*anyopaque, []u8, u8, usize, usize) bool' must be comptime-known, but operand value is runtime-known // :25:51: error: values of type 'fn(*anyopaque, []u8, u8, usize, usize) bool' must be comptime-known, but operand value is runtime-known

View File

@ -15,12 +15,12 @@ export fn b() void {
_ = bar; _ = bar;
} }
export fn c() void { export fn c() void {
const baz = &@as(opaque {}, undefined); const baz = &@as(O, undefined);
const qux = .{baz.*}; const qux = .{baz.*};
_ = qux; _ = qux;
} }
export fn d() void { export fn d() void {
const baz = &@as(opaque {}, undefined); const baz = &@as(O, undefined);
const qux = .{ .a = baz.* }; const qux = .{ .a = baz.* };
_ = qux; _ = qux;
} }
@ -33,7 +33,5 @@ export fn d() void {
// :1:11: note: opaque declared here // :1:11: note: opaque declared here
// :7:10: error: opaque types have unknown size and therefore cannot be directly embedded in unions // :7:10: error: opaque types have unknown size and therefore cannot be directly embedded in unions
// :1:11: note: opaque declared here // :1:11: note: opaque declared here
// :19:18: error: opaque types have unknown size and therefore cannot be directly embedded in structs // :19:22: error: cannot load opaque type 'tmp.O'
// :18:22: note: opaque declared here // :24:28: error: cannot load opaque type 'tmp.O'
// :24:23: error: opaque types have unknown size and therefore cannot be directly embedded in structs
// :23:22: note: opaque declared here

View File

@ -27,6 +27,10 @@ export fn entry7() void {
_ = f; _ = f;
} }
const Opaque = opaque {}; const Opaque = opaque {};
export fn entry8() void {
var e: Opaque = undefined;
_ = &e;
}
// error // error
// backend=stage2 // backend=stage2
@ -39,7 +43,7 @@ const Opaque = opaque {};
// :14:9: error: variable of type 'comptime_float' must be const or comptime // :14:9: error: variable of type 'comptime_float' must be const or comptime
// :14:9: note: to modify this variable at runtime, it must be given an explicit fixed-size number type // :14:9: note: to modify this variable at runtime, it must be given an explicit fixed-size number type
// :18:9: error: variable of type '@TypeOf(null)' must be const or comptime // :18:9: error: variable of type '@TypeOf(null)' must be const or comptime
// :22:20: error: values of type 'tmp.Opaque' must be comptime-known, but operand value is runtime-known // :22:20: error: cannot load opaque type 'tmp.Opaque'
// :22:20: note: opaque type 'tmp.Opaque' has undefined size
// :26:9: error: variable of type 'type' must be const or comptime // :26:9: error: variable of type 'type' must be const or comptime
// :26:9: note: types are not available at runtime // :26:9: note: types are not available at runtime
// :31:12: error: non-extern variable with opaque type 'tmp.Opaque'