From ada0010471163a3accca8976185fbb6bb59c914f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 21 Aug 2023 14:27:34 -0700 Subject: [PATCH] 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. --- lib/std/dwarf/call_frame.zig | 14 +- lib/std/meta.zig | 4 +- src/AstGen.zig | 5 + src/InternPool.zig | 510 +++++++--- src/Module.zig | 402 +++----- src/Sema.zig | 913 ++++++++---------- src/TypedValue.zig | 4 +- src/Zir.zig | 3 +- src/arch/aarch64/abi.zig | 14 +- src/arch/arm/abi.zig | 17 +- src/arch/wasm/CodeGen.zig | 62 +- src/arch/wasm/abi.zig | 29 +- src/arch/x86_64/CodeGen.zig | 5 +- src/arch/x86_64/abi.zig | 15 +- src/codegen.zig | 22 +- src/codegen/c.zig | 101 +- src/codegen/c/type.zig | 26 +- src/codegen/llvm.zig | 110 +-- src/codegen/spirv.zig | 28 +- src/link/Dwarf.zig | 20 +- src/type.zig | 372 ++++--- src/value.zig | 28 +- test/behavior/union.zig | 27 +- .../access_inactive_union_field_comptime.zig | 2 +- .../compile_errors/dereference_anyopaque.zig | 3 +- ...edding_opaque_type_in_struct_and_union.zig | 10 +- ...of_things_that_require_const_variables.zig | 8 +- 27 files changed, 1396 insertions(+), 1358 deletions(-) diff --git a/lib/std/dwarf/call_frame.zig b/lib/std/dwarf/call_frame.zig index f92e6315142d..f238fd178e2a 100644 --- a/lib/std/dwarf/call_frame.zig +++ b/lib/std/dwarf/call_frame.zig @@ -69,16 +69,9 @@ pub const Instruction = union(Opcode) { register: u8, offset: u64, }, - offset_extended: struct { - register: u8, - offset: u64, - }, restore: struct { register: u8, }, - restore_extended: struct { - register: u8, - }, nop: void, set_loc: struct { address: u64, @@ -92,6 +85,13 @@ pub const Instruction = union(Opcode) { advance_loc4: struct { delta: u32, }, + offset_extended: struct { + register: u8, + offset: u64, + }, + restore_extended: struct { + register: u8, + }, undefined: struct { register: u8, }, diff --git a/lib/std/meta.zig b/lib/std/meta.zig index 3bc5fc986484..b10ecd273155 100644 --- a/lib/std/meta.zig +++ b/lib/std/meta.zig @@ -614,9 +614,9 @@ test "std.meta.FieldEnum" { const Tagged = union(enum) { a: u8, b: void, c: f32 }; 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 }; - try testing.expect(Tag(Tagged2) != FieldEnum(Tagged2)); + try testing.expect(Tag(Tagged2) == FieldEnum(Tagged2)); const Tag3 = enum(u8) { a, b, c = 7 }; const Tagged3 = union(Tag3) { a: u8, b: void, c: f32 }; diff --git a/src/AstGen.zig b/src/AstGen.zig index 590e2a5eddd3..0116e00f8b3c 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -4696,6 +4696,7 @@ fn unionDeclInner( const bits_per_field = 4; 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); defer wip_members.deinit(); @@ -4733,6 +4734,7 @@ fn unionDeclInner( if (have_align) { const align_inst = try expr(&block_scope, &block_scope.base, .{ .rl = .{ .ty = .u32_type } }, member.ast.align_expr); wip_members.appendToField(@intFromEnum(align_inst)); + any_aligned_fields = true; } if (have_value) { if (arg_inst == .none) { @@ -4783,6 +4785,7 @@ fn unionDeclInner( .fields_len = field_count, .decls_len = decl_count, .auto_enum_tag = auto_enum_tok != null, + .any_aligned_fields = any_aligned_fields, }); wip_members.finishBits(bits_per_field); @@ -11754,6 +11757,7 @@ const GenZir = struct { decls_len: u32, layout: std.builtin.Type.ContainerLayout, auto_enum_tag: bool, + any_aligned_fields: bool, }) !void { const astgen = gz.astgen; const gpa = astgen.gpa; @@ -11790,6 +11794,7 @@ const GenZir = struct { .name_strategy = gz.anon_name_strategy, .layout = args.layout, .auto_enum_tag = args.auto_enum_tag, + .any_aligned_fields = args.any_aligned_fields, }), .operand = payload_index, } }, diff --git a/src/InternPool.zig b/src/InternPool.zig index 92acf10bd544..3c7c25ffddc0 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -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. 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 /// 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, @@ -241,7 +234,7 @@ pub const Key = union(enum) { /// declaration. It is used for types that have no `struct` keyword in the /// source code, and were not created via `@Type`. anon_struct_type: AnonStructType, - union_type: UnionType, + union_type: Key.UnionType, opaque_type: OpaqueType, enum_type: EnumType, 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 { - index: Module.Union.Index, - runtime_tag: RuntimeTag, + /// The Decl that corresponds to the union itself. + 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, + + /// 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 const RuntimeTag = enum { none, safety, tagged }; + pub fn haveFieldTypes(self: @This(), ip: *const InternPool) bool { + return self.flagsPtr(ip).status.haveFieldTypes(); + } - pub fn hasTag(self: UnionType) bool { - return switch (self.runtime_tag) { - .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)), .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) { .err_name => |y| Hash.hash(seed + 0, asBytes(&x.ty) ++ asBytes(&y)), @@ -845,6 +892,7 @@ pub const Key = union(enum) { inline .opaque_type, .enum_type, .variable, + .union_type, => |x| Hash.hash(seed, asBytes(&x.decl)), .int => |int| { @@ -1079,10 +1127,6 @@ pub const Key = union(enum) { const b_info = b.struct_type; 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| { const b_info = b.un; return std.meta.eql(a_info, b_info); @@ -1250,6 +1294,10 @@ pub const Key = union(enum) { const b_info = b.enum_type; 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| { const b_info = b.aggregate; 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 { tag: Tag, /// 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_anon: DataIsExtraIndexOfTypeStructAnon, type_tuple_anon: DataIsExtraIndexOfTypeStructAnon, - type_union_tagged: struct { data: Module.Union.Index }, - type_union_untagged: struct { data: Module.Union.Index }, - type_union_safety: struct { data: Module.Union.Index }, + type_union: struct { data: *Tag.TypeUnion }, type_function: struct { const @"data.flags.has_comptime_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. /// data is extra index of `TypeStructAnon`. type_tuple_anon, - /// A tagged union type. - /// `data` is `Module.Union.Index`. - type_union_tagged, - /// 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 union type. + /// `data` is extra index of `TypeUnion`. + type_union, /// A function body type. /// `data` is extra index to `TypeFunction`. type_function, @@ -2273,9 +2465,7 @@ pub const Tag = enum(u8) { .type_struct_ns => unreachable, .type_struct_anon => TypeStructAnon, .type_tuple_anon => TypeStructAnon, - .type_union_tagged => unreachable, - .type_union_untagged => unreachable, - .type_union_safety => unreachable, + .type_union => TypeUnion, .type_function => TypeFunction, .undef => unreachable, @@ -2425,6 +2615,30 @@ pub const Tag = enum(u8) { _: 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 @@ -2582,6 +2796,21 @@ pub const Alignment = enum(u6) { assert(lhs != .none and rhs != .none); 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 @@ -2829,9 +3058,6 @@ pub fn deinit(ip: *InternPool, gpa: Allocator) void { ip.structs_free_list.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.allocated_decls.deinit(gpa); @@ -2953,18 +3179,7 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key { } }; }, - .type_union_untagged => .{ .union_type = .{ - .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_union => .{ .union_type = extraUnionType(ip, data) }, .type_enum_auto => { 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_explicit, - .type_union_tagged, - .type_union_untagged, - .type_union_safety, + .type_union, => .{ .empty_enum_value = ty }, 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 { const type_function = ip.extraDataTrail(Tag.TypeFunction, extra_index); 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); }, - .union_type => |union_type| { - 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), - }); - }, + .union_type => unreachable, // use getUnionType() instead .opaque_type => |opaque_type| { ip.items.appendAssumeCapacity(.{ @@ -3791,9 +4007,10 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { assert(ptr.addr == .field); 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(base_index.index < ip.unionPtrConst(union_type.index).fields.count()); + assert(base_index.index < union_type.field_names.len); }, .ptr_type => |slice_type| { 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); } +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`. pub const GetFuncTypeKey = struct { param_types: []Index, @@ -5310,6 +5597,7 @@ fn addExtraAssumeCapacity(ip: *InternPool, extra: anytype) u32 { Tag.TypeFunction.Flags, Tag.TypePointer.PackedOffset, Tag.Variable.Flags, + Tag.TypeUnion.Flags, => @bitCast(@field(extra, field.name)), 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.TypeFunction.Flags, Tag.TypePointer.PackedOffset, + Tag.TypeUnion.Flags, Tag.Variable.Flags, FuncAnalysis, => @bitCast(int32), @@ -5893,7 +6182,7 @@ pub fn indexToUnionType(ip: *const InternPool, val: Index) Module.Union.Optional assert(val != .none); const tags = ip.items.items(.tag); switch (tags[@intFromEnum(val)]) { - .type_union_tagged, .type_union_untagged, .type_union_safety => {}, + .type_union => {}, else => return .none, } 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 { 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; // TODO: fields size is not taken into account const structs_size = ip.allocated_structs.len * - (@sizeOf(Module.Struct) + @sizeOf(Module.Namespace) + @sizeOf(Module.Decl)); - const unions_size = ip.allocated_unions.len * - (@sizeOf(Module.Union) + @sizeOf(Module.Namespace) + @sizeOf(Module.Decl)); + (@sizeOf(Module.Struct) + @sizeOf(Module.Namespace)); + const decls_size = ip.allocated_decls.len * @sizeOf(Module.Decl); // TODO: map overhead size is not taken into account - const total_size = @sizeOf(InternPool) + items_size + extra_size + limbs_size + - structs_size + unions_size; + const total_size = @sizeOf(InternPool) + items_size + extra_size + limbs_size + structs_size + decls_size; std.debug.print( \\InternPool size: {d} bytes @@ -6024,7 +6315,7 @@ fn dumpStatsFallible(ip: *const InternPool, arena: Allocator) anyerror!void { \\ {d} extra: {d} bytes \\ {d} limbs: {d} bytes \\ {d} structs: {d} bytes - \\ {d} unions: {d} bytes + \\ {d} decls: {d} bytes \\ , .{ total_size, @@ -6036,8 +6327,8 @@ fn dumpStatsFallible(ip: *const InternPool, arena: Allocator) anyerror!void { limbs_size, ip.allocated_structs.len, structs_size, - ip.allocated_unions.len, - unions_size, + ip.allocated_decls.len, + decls_size, }); 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); break :b @sizeOf(Module.Struct) + @sizeOf(Module.Namespace) + - @sizeOf(Module.Decl) + (struct_obj.fields.count() * @sizeOf(Module.Struct.Field)); }, .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); }, - .type_union_tagged, - .type_union_untagged, - .type_union_safety, - => @sizeOf(Module.Union) + @sizeOf(Module.Namespace) + @sizeOf(Module.Decl), + .type_union => b: { + const info = ip.extraData(Tag.TypeUnion, data); + const enum_info = ip.indexToKey(info.tag_ty).enum_type; + 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: { 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_f128 => @sizeOf(Float128), .float_comptime_float => @sizeOf(Float128), - .variable => @sizeOf(Tag.Variable) + @sizeOf(Module.Decl), - .extern_func => @sizeOf(Tag.ExternFunc) + @sizeOf(Module.Decl), - .func_decl => @sizeOf(Tag.FuncDecl) + @sizeOf(Module.Decl), + .variable => @sizeOf(Tag.Variable), + .extern_func => @sizeOf(Tag.ExternFunc), + .func_decl => @sizeOf(Tag.FuncDecl), .func_instance => b: { const info = ip.extraData(Tag.FuncInstance, data); const ty = ip.typeOf(info.generic_owner); const params_len = ip.indexToKey(ty).func_type.param_types.len; - break :b @sizeOf(Tag.FuncInstance) + @sizeOf(Index) * params_len + - @sizeOf(Module.Decl); + break :b @sizeOf(Tag.FuncInstance) + @sizeOf(Index) * params_len; }, .func_coerced => @sizeOf(Tag.FuncCoerced), .only_possible_value => 0, @@ -6230,9 +6527,7 @@ fn dumpAllFallible(ip: *const InternPool) anyerror!void { .type_struct_ns, .type_struct_anon, .type_tuple_anon, - .type_union_tagged, - .type_union_untagged, - .type_union_safety, + .type_union, .type_function, .undef, .runtime_value, @@ -6358,14 +6653,6 @@ pub fn structPtrUnwrapConst(ip: *const InternPool, index: Module.Struct.Optional 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 { 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( ip: *InternPool, gpa: Allocator, @@ -6667,9 +6932,7 @@ pub fn typeOf(ip: *const InternPool, index: Index) Index { .type_struct_ns, .type_struct_anon, .type_tuple_anon, - .type_union_tagged, - .type_union_untagged, - .type_union_safety, + .type_union, .type_function, => .type_type, @@ -7005,10 +7268,7 @@ pub fn zigTypeTagOrPoison(ip: *const InternPool, index: Index) error{GenericPois .type_tuple_anon, => .Struct, - .type_union_tagged, - .type_union_untagged, - .type_union_safety, - => .Union, + .type_union => .Union, .type_function => .Fn, diff --git a/src/Module.zig b/src/Module.zig index 2c78ddd88157..37ec79796db5 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -96,7 +96,7 @@ intern_pool: InternPool = .{}, /// Current uses that must be eliminated: /// * Struct comptime_args /// * Struct optimized_order -/// * Union fields +/// * comptime pointer mutation /// This memory lives until the Module is destroyed. tmp_hack_arena: std.heap.ArenaAllocator, @@ -736,7 +736,7 @@ pub const Decl = struct { /// If the Decl owns its value and it is a union, return it, /// otherwise null. - pub fn getOwnedUnion(decl: Decl, mod: *Module) ?*Union { + pub fn getOwnedUnion(decl: Decl, mod: *Module) ?InternPool.UnionType { if (!decl.owns_tv) return null; if (decl.val.ip_index == .none) return null; return mod.typeToUnion(decl.val.toType()); @@ -778,7 +778,7 @@ pub const Decl = struct { else => switch (mod.intern_pool.indexToKey(decl.val.toIntern())) { .opaque_type => |opaque_type| opaque_type.namespace.toOptional(), .struct_type => |struct_type| struct_type.namespace, - .union_type => |union_type| mod.unionPtr(union_type.index).namespace.toOptional(), + .union_type => |union_type| union_type.namespace.toOptional(), .enum_type => |enum_type| enum_type.namespace, else => .none, }, @@ -1064,246 +1064,6 @@ pub const Struct = struct { } }; -pub const Union = struct { - /// An enum type which is used for the tag of the union. - /// This type is created even for untagged unions, even when the memory - /// layout does not store the tag. - /// Whether zig chooses this type or the user specifies it, it is stored here. - /// This will be set to the null type until status is `have_field_types`. - tag_ty: Type, - /// Set of field names in declaration order. - fields: Fields, - /// Represents the declarations inside this union. - namespace: Namespace.Index, - /// The Decl that corresponds to the union itself. - owner_decl: Decl.Index, - /// Index of the union_decl ZIR instruction. - zir_index: Zir.Inst.Index, - - layout: std.builtin.Type.ContainerLayout, - status: enum { - none, - field_types_wip, - have_field_types, - layout_wip, - have_layout, - fully_resolved_wip, - // The types and all its fields have had their layout resolved. Even through pointer, - // which `have_layout` does not ensure. - fully_resolved, - }, - requires_comptime: PropertyBoolean = .unknown, - assumed_runtime_bits: bool = false, - - pub const Index = enum(u32) { - _, - - pub fn toOptional(i: Index) OptionalIndex { - return @as(OptionalIndex, @enumFromInt(@intFromEnum(i))); - } - }; - - pub const OptionalIndex = enum(u32) { - none = std.math.maxInt(u32), - _, - - pub fn init(oi: ?Index) OptionalIndex { - return @as(OptionalIndex, @enumFromInt(@intFromEnum(oi orelse return .none))); - } - - pub fn unwrap(oi: OptionalIndex) ?Index { - if (oi == .none) return null; - return @as(Index, @enumFromInt(@intFromEnum(oi))); - } - }; - - pub const Field = struct { - /// undefined until `status` is `have_field_types` or `have_layout`. - ty: Type, - /// 0 means the ABI alignment of the type. - abi_align: Alignment, - - /// Returns the field alignment, assuming the union is not packed. - /// Keep implementation in sync with `Sema.unionFieldAlignment`. - /// Prefer to call that function instead of this one during Sema. - pub fn normalAlignment(field: Field, mod: *Module) u32 { - return @as(u32, @intCast(field.abi_align.toByteUnitsOptional() orelse field.ty.abiAlignment(mod))); - } - }; - - pub const Fields = std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, Field); - - pub fn getFullyQualifiedName(s: *Union, mod: *Module) !InternPool.NullTerminatedString { - return mod.declPtr(s.owner_decl).getFullyQualifiedName(mod); - } - - pub fn srcLoc(self: Union, mod: *Module) SrcLoc { - const owner_decl = mod.declPtr(self.owner_decl); - return .{ - .file_scope = owner_decl.getFileScope(mod), - .parent_decl_node = owner_decl.src_node, - .lazy = LazySrcLoc.nodeOffset(0), - }; - } - - pub fn haveFieldTypes(u: Union) bool { - return switch (u.status) { - .none, - .field_types_wip, - => false, - .have_field_types, - .layout_wip, - .have_layout, - .fully_resolved_wip, - .fully_resolved, - => true, - }; - } - - pub fn hasAllZeroBitFieldTypes(u: Union, mod: *Module) bool { - assert(u.haveFieldTypes()); - for (u.fields.values()) |field| { - if (field.ty.hasRuntimeBits(mod)) return false; - } - return true; - } - - pub fn mostAlignedField(u: Union, mod: *Module) u32 { - assert(u.haveFieldTypes()); - var most_alignment: u32 = 0; - var most_index: usize = undefined; - for (u.fields.values(), 0..) |field, i| { - if (!field.ty.hasRuntimeBits(mod)) continue; - - const field_align = field.normalAlignment(mod); - if (field_align > most_alignment) { - most_alignment = field_align; - most_index = i; - } - } - return @as(u32, @intCast(most_index)); - } - - /// Returns 0 if the union is represented with 0 bits at runtime. - pub fn abiAlignment(u: Union, mod: *Module, have_tag: bool) u32 { - var max_align: u32 = 0; - if (have_tag) max_align = u.tag_ty.abiAlignment(mod); - for (u.fields.values()) |field| { - if (!field.ty.hasRuntimeBits(mod)) continue; - - const field_align = field.normalAlignment(mod); - max_align = @max(max_align, field_align); - } - return max_align; - } - - pub fn abiSize(u: Union, mod: *Module, have_tag: bool) u64 { - return u.getLayout(mod, have_tag).abi_size; - } - - pub const Layout = struct { - abi_size: u64, - abi_align: u32, - most_aligned_field: u32, - most_aligned_field_size: u64, - biggest_field: u32, - payload_size: u64, - payload_align: u32, - tag_align: u32, - tag_size: u64, - padding: u32, - }; - - pub fn haveLayout(u: Union) bool { - return switch (u.status) { - .none, - .field_types_wip, - .have_field_types, - .layout_wip, - => false, - .have_layout, - .fully_resolved_wip, - .fully_resolved, - => true, - }; - } - - pub fn getLayout(u: Union, mod: *Module, have_tag: bool) Layout { - assert(u.haveLayout()); - var most_aligned_field: u32 = undefined; - var most_aligned_field_size: u64 = undefined; - var biggest_field: u32 = undefined; - var payload_size: u64 = 0; - var payload_align: u32 = 0; - const fields = u.fields.values(); - for (fields, 0..) |field, i| { - if (!field.ty.hasRuntimeBitsIgnoreComptime(mod)) continue; - - const field_align = field.abi_align.toByteUnitsOptional() orelse field.ty.abiAlignment(mod); - const field_size = field.ty.abiSize(mod); - if (field_size > payload_size) { - payload_size = field_size; - biggest_field = @as(u32, @intCast(i)); - } - if (field_align > payload_align) { - payload_align = @as(u32, @intCast(field_align)); - most_aligned_field = @as(u32, @intCast(i)); - most_aligned_field_size = field_size; - } - } - payload_align = @max(payload_align, 1); - if (!have_tag or !u.tag_ty.hasRuntimeBits(mod)) { - return .{ - .abi_size = std.mem.alignForward(u64, payload_size, payload_align), - .abi_align = payload_align, - .most_aligned_field = most_aligned_field, - .most_aligned_field_size = most_aligned_field_size, - .biggest_field = biggest_field, - .payload_size = payload_size, - .payload_align = payload_align, - .tag_align = 0, - .tag_size = 0, - .padding = 0, - }; - } - // Put the tag before or after the payload depending on which one's - // alignment is greater. - const tag_size = u.tag_ty.abiSize(mod); - const tag_align = @max(1, u.tag_ty.abiAlignment(mod)); - var size: u64 = 0; - var padding: u32 = undefined; - if (tag_align >= payload_align) { - // {Tag, Payload} - size += tag_size; - size = std.mem.alignForward(u64, size, payload_align); - size += payload_size; - const prev_size = size; - size = std.mem.alignForward(u64, size, tag_align); - padding = @as(u32, @intCast(size - prev_size)); - } else { - // {Payload, Tag} - size += payload_size; - size = std.mem.alignForward(u64, size, tag_align); - size += tag_size; - const prev_size = size; - size = std.mem.alignForward(u64, size, payload_align); - padding = @as(u32, @intCast(size - prev_size)); - } - return .{ - .abi_size = size, - .abi_align = @max(tag_align, payload_align), - .most_aligned_field = most_aligned_field, - .most_aligned_field_size = most_aligned_field_size, - .biggest_field = biggest_field, - .payload_size = payload_size, - .payload_align = payload_align, - .tag_align = tag_align, - .tag_size = tag_size, - .padding = padding, - }; - } -}; - pub const DeclAdapter = struct { mod: *Module, @@ -3182,10 +2942,6 @@ pub fn namespacePtr(mod: *Module, index: Namespace.Index) *Namespace { return mod.intern_pool.namespacePtr(index); } -pub fn unionPtr(mod: *Module, index: Union.Index) *Union { - return mod.intern_pool.unionPtr(index); -} - pub fn structPtr(mod: *Module, index: Struct.Index) *Struct { return mod.intern_pool.structPtr(index); } @@ -3651,11 +3407,11 @@ fn updateZirRefs(mod: *Module, file: *File, old_zir: Zir) !void { }; } - if (decl.getOwnedUnion(mod)) |union_obj| { - union_obj.zir_index = inst_map.get(union_obj.zir_index) orelse { + if (decl.getOwnedUnion(mod)) |union_type| { + union_type.setZirIndex(ip, inst_map.get(union_type.zir_index) orelse { try file.deleted_decls.append(gpa, decl_index); continue; - }; + }); } if (decl.getOwnedFunction(mod)) |func| { @@ -5550,14 +5306,6 @@ pub fn destroyStruct(mod: *Module, index: Struct.Index) void { return mod.intern_pool.destroyStruct(mod.gpa, index); } -pub fn createUnion(mod: *Module, initialization: Union) Allocator.Error!Union.Index { - return mod.intern_pool.createUnion(mod.gpa, initialization); -} - -pub fn destroyUnion(mod: *Module, index: Union.Index) void { - return mod.intern_pool.destroyUnion(mod.gpa, index); -} - pub fn allocateNewDecl( mod: *Module, namespace: Namespace.Index, @@ -6956,10 +6704,14 @@ pub fn typeToStruct(mod: *Module, ty: Type) ?*Struct { return mod.structPtr(struct_index); } -pub fn typeToUnion(mod: *Module, ty: Type) ?*Union { +/// This asserts that the union's enum tag type has been resolved. +pub fn typeToUnion(mod: *Module, ty: Type) ?InternPool.UnionType { if (ty.ip_index == .none) return null; - const union_index = mod.intern_pool.indexToUnionType(ty.toIntern()).unwrap() orelse return null; - return mod.unionPtr(union_index); + const ip = &mod.intern_pool; + switch (ip.indexToKey(ty.ip_index)) { + .union_type => |k| return ip.loadUnionType(k), + else => return null, + } } pub fn typeToFunc(mod: *Module, ty: Type) ?InternPool.Key.FuncType { @@ -7045,3 +6797,131 @@ pub fn getParamName(mod: *Module, func_index: InternPool.Index, index: u32) [:0] else => unreachable, }; } + +pub const UnionLayout = struct { + abi_size: u64, + abi_align: u32, + most_aligned_field: u32, + most_aligned_field_size: u64, + biggest_field: u32, + payload_size: u64, + payload_align: u32, + tag_align: u32, + tag_size: u64, + padding: u32, +}; + +pub fn getUnionLayout(mod: *Module, u: InternPool.UnionType) UnionLayout { + const ip = &mod.intern_pool; + assert(u.haveLayout(ip)); + var most_aligned_field: u32 = undefined; + var most_aligned_field_size: u64 = undefined; + var biggest_field: u32 = undefined; + var payload_size: u64 = 0; + var payload_align: u32 = 0; + for (u.field_types.get(ip), 0..) |field_ty, i| { + if (!field_ty.toType().hasRuntimeBitsIgnoreComptime(mod)) continue; + + const field_align = u.fieldAlign(ip, @intCast(i)).toByteUnitsOptional() orelse + field_ty.toType().abiAlignment(mod); + const field_size = field_ty.toType().abiSize(mod); + if (field_size > payload_size) { + payload_size = field_size; + biggest_field = @intCast(i); + } + if (field_align > payload_align) { + payload_align = @intCast(field_align); + most_aligned_field = @intCast(i); + most_aligned_field_size = field_size; + } + } + payload_align = @max(payload_align, 1); + const have_tag = u.flagsPtr(ip).runtime_tag.hasTag(); + if (!have_tag or !u.enum_tag_ty.toType().hasRuntimeBits(mod)) { + return .{ + .abi_size = std.mem.alignForward(u64, payload_size, payload_align), + .abi_align = payload_align, + .most_aligned_field = most_aligned_field, + .most_aligned_field_size = most_aligned_field_size, + .biggest_field = biggest_field, + .payload_size = payload_size, + .payload_align = payload_align, + .tag_align = 0, + .tag_size = 0, + .padding = 0, + }; + } + // Put the tag before or after the payload depending on which one's + // alignment is greater. + const tag_size = u.enum_tag_ty.toType().abiSize(mod); + const tag_align = @max(1, u.enum_tag_ty.toType().abiAlignment(mod)); + var size: u64 = 0; + var padding: u32 = undefined; + if (tag_align >= payload_align) { + // {Tag, Payload} + size += tag_size; + size = std.mem.alignForward(u64, size, payload_align); + size += payload_size; + const prev_size = size; + size = std.mem.alignForward(u64, size, tag_align); + padding = @as(u32, @intCast(size - prev_size)); + } else { + // {Payload, Tag} + size += payload_size; + size = std.mem.alignForward(u64, size, tag_align); + size += tag_size; + const prev_size = size; + size = std.mem.alignForward(u64, size, payload_align); + padding = @as(u32, @intCast(size - prev_size)); + } + return .{ + .abi_size = size, + .abi_align = @max(tag_align, payload_align), + .most_aligned_field = most_aligned_field, + .most_aligned_field_size = most_aligned_field_size, + .biggest_field = biggest_field, + .payload_size = payload_size, + .payload_align = payload_align, + .tag_align = tag_align, + .tag_size = tag_size, + .padding = padding, + }; +} + +pub fn unionAbiSize(mod: *Module, u: InternPool.UnionType) u64 { + return mod.getUnionLayout(u).abi_size; +} + +/// Returns 0 if the union is represented with 0 bits at runtime. +/// TODO: this returns alignment in byte units should should be a u64 +pub fn unionAbiAlignment(mod: *Module, u: InternPool.UnionType) u32 { + const ip = &mod.intern_pool; + const have_tag = u.flagsPtr(ip).runtime_tag.hasTag(); + var max_align: u32 = 0; + if (have_tag) max_align = u.enum_tag_ty.toType().abiAlignment(mod); + for (u.field_types.get(ip), 0..) |field_ty, field_index| { + if (!field_ty.toType().hasRuntimeBits(mod)) continue; + + const field_align = mod.unionFieldNormalAlignment(u, @intCast(field_index)); + max_align = @max(max_align, field_align); + } + return max_align; +} + +/// Returns the field alignment, assuming the union is not packed. +/// Keep implementation in sync with `Sema.unionFieldAlignment`. +/// Prefer to call that function instead of this one during Sema. +/// TODO: this returns alignment in byte units should should be a u64 +pub fn unionFieldNormalAlignment(mod: *Module, u: InternPool.UnionType, field_index: u32) u32 { + const ip = &mod.intern_pool; + if (u.fieldAlign(ip, field_index).toByteUnitsOptional()) |a| return @intCast(a); + const field_ty = u.field_types.get(ip)[field_index].toType(); + return field_ty.abiAlignment(mod); +} + +pub fn unionTagFieldIndex(mod: *Module, u: InternPool.UnionType, enum_tag: Value) ?u32 { + const ip = &mod.intern_pool; + assert(ip.typeOf(enum_tag.toIntern()) == u.enum_tag_ty); + const enum_type = ip.indexToKey(u.enum_tag_ty).enum_type; + return enum_type.tagValueIndex(ip, enum_tag.toIntern()); +} diff --git a/src/Sema.zig b/src/Sema.zig index 504a335b53b6..f8ae2aee1b3c 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -3022,18 +3022,18 @@ fn zirEnumDecl( const mod = sema.mod; const gpa = sema.gpa; - const small = @as(Zir.Inst.EnumDecl.Small, @bitCast(extended.small)); + const small: Zir.Inst.EnumDecl.Small = @bitCast(extended.small); var extra_index: usize = extended.operand; const src: LazySrcLoc = if (small.has_src_node) blk: { - const node_offset = @as(i32, @bitCast(sema.code.extra[extra_index])); + const node_offset: i32 = @bitCast(sema.code.extra[extra_index]); extra_index += 1; break :blk LazySrcLoc.nodeOffset(node_offset); } else sema.src; const tag_ty_src: LazySrcLoc = .{ .node_offset_container_tag = src.node_offset.x }; const tag_type_ref = if (small.has_tag_type) blk: { - const tag_type_ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index])); + const tag_type_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); extra_index += 1; break :blk tag_type_ref; } else .none; @@ -3310,7 +3310,11 @@ fn zirUnionDecl( extra_index += @intFromBool(small.has_tag_type); extra_index += @intFromBool(small.has_body_len); - extra_index += @intFromBool(small.has_fields_len); + const fields_len = if (small.has_fields_len) blk: { + const fields_len = sema.code.extra[extra_index]; + extra_index += 1; + break :blk fields_len; + } else 0; const decls_len = if (small.has_decls_len) blk: { const decls_len = sema.code.extra[extra_index]; @@ -3338,29 +3342,31 @@ fn zirUnionDecl( const new_namespace = mod.namespacePtr(new_namespace_index); errdefer mod.destroyNamespace(new_namespace_index); - const union_index = try mod.createUnion(.{ - .owner_decl = new_decl_index, - .tag_ty = Type.null, - .fields = .{}, - .zir_index = inst, - .layout = small.layout, - .status = .none, - .namespace = new_namespace_index, - }); - errdefer mod.destroyUnion(union_index); - const union_ty = ty: { - const ty = try mod.intern_pool.get(gpa, .{ .union_type = .{ - .index = union_index, - .runtime_tag = if (small.has_tag_type or small.auto_enum_tag) - .tagged - else if (small.layout != .Auto) - .none - else switch (block.sema.mod.optimizeMode()) { - .Debug, .ReleaseSafe => .safety, - .ReleaseFast, .ReleaseSmall => .none, + const ty = try mod.intern_pool.getUnionType(gpa, .{ + .flags = .{ + .layout = small.layout, + .status = .none, + .runtime_tag = if (small.has_tag_type or small.auto_enum_tag) + .tagged + else if (small.layout != .Auto) + .none + else switch (block.wantSafety()) { + true => .safety, + false => .none, + }, + .any_aligned_fields = small.any_aligned_fields, + .requires_comptime = .unknown, + .assumed_runtime_bits = false, }, - } }); + .decl = new_decl_index, + .namespace = new_namespace_index, + .zir_index = inst, + .fields_len = fields_len, + .enum_tag_ty = .none, + .field_types = &.{}, + .field_aligns = &.{}, + }); if (sema.builtin_type_target_index != .none) { mod.intern_pool.resolveBuiltinType(sema.builtin_type_target_index, ty); break :ty sema.builtin_type_target_index; @@ -4505,8 +4511,7 @@ fn validateUnionInit( const field_src: LazySrcLoc = .{ .node_offset_initializer = field_ptr_data.src_node }; const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data; const field_name = try mod.intern_pool.getOrPutString(gpa, sema.code.nullTerminatedString(field_ptr_extra.field_name_start)); - // Validate the field access but ignore the index since we want the tag enum field index. - _ = try sema.unionFieldIndex(block, union_ty, field_name, field_src); + const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_src); const air_tags = sema.air_instructions.items(.tag); const air_datas = sema.air_instructions.items(.data); const field_ptr_ref = sema.inst_map.get(field_ptr).?; @@ -4563,8 +4568,7 @@ fn validateUnionInit( } const tag_ty = union_ty.unionTagTypeHypothetical(mod); - const enum_field_index = @as(u32, @intCast(tag_ty.enumFieldIndex(field_name, mod).?)); - const tag_val = try mod.enumValueFieldIndex(tag_ty, enum_field_index); + const tag_val = try mod.enumValueFieldIndex(tag_ty, field_index); if (init_val) |val| { // Our task is to delete all the `field_ptr` and `store` instructions, and insert @@ -5227,14 +5231,15 @@ fn failWithBadStructFieldAccess( fn failWithBadUnionFieldAccess( sema: *Sema, block: *Block, - union_obj: *Module.Union, + union_obj: InternPool.UnionType, field_src: LazySrcLoc, field_name: InternPool.NullTerminatedString, ) CompileError { const mod = sema.mod; const gpa = sema.gpa; - const fqn = try union_obj.getFullyQualifiedName(mod); + const decl = mod.declPtr(union_obj.decl); + const fqn = try decl.getFullyQualifiedName(mod); const msg = msg: { const msg = try sema.errMsg( @@ -5244,7 +5249,7 @@ fn failWithBadUnionFieldAccess( .{ field_name.fmt(&mod.intern_pool), fqn.fmt(&mod.intern_pool) }, ); errdefer msg.destroy(gpa); - try mod.errNoteNonLazy(union_obj.srcLoc(mod), msg, "union declared here", .{}); + try mod.errNoteNonLazy(decl.srcLoc(mod), msg, "union declared here", .{}); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); @@ -10500,6 +10505,7 @@ const SwitchProngAnalysis = struct { ) CompileError!Air.Inst.Ref { const sema = spa.sema; const mod = sema.mod; + const ip = &mod.intern_pool; const zir_datas = sema.code.instructions.items(.data); const switch_node_offset = zir_datas[spa.switch_block_inst].pl_node.src_node; @@ -10511,9 +10517,9 @@ const SwitchProngAnalysis = struct { if (inline_case_capture != .none) { const item_val = sema.resolveConstValue(block, .unneeded, inline_case_capture, "") catch unreachable; if (operand_ty.zigTypeTag(mod) == .Union) { - const field_index = @as(u32, @intCast(operand_ty.unionTagFieldIndex(item_val, mod).?)); + const field_index: u32 = @intCast(operand_ty.unionTagFieldIndex(item_val, mod).?); const union_obj = mod.typeToUnion(operand_ty).?; - const field_ty = union_obj.fields.values()[field_index].ty; + const field_ty = union_obj.field_types.get(ip)[field_index].toType(); if (capture_byref) { const ptr_field_ty = try mod.ptrType(.{ .child = field_ty.toIntern(), @@ -10535,7 +10541,7 @@ const SwitchProngAnalysis = struct { return block.addStructFieldPtr(spa.operand_ptr, field_index, ptr_field_ty); } else { if (try sema.resolveDefinedValue(block, sema.src, spa.operand)) |union_val| { - const tag_and_val = mod.intern_pool.indexToKey(union_val.toIntern()).un; + const tag_and_val = ip.indexToKey(union_val.toIntern()).un; return Air.internedToRef(tag_and_val.val); } return block.addStructFieldVal(spa.operand, field_index, field_ty); @@ -10568,14 +10574,14 @@ const SwitchProngAnalysis = struct { const union_obj = mod.typeToUnion(operand_ty).?; const first_item_val = sema.resolveConstValue(block, .unneeded, case_vals[0], "") catch unreachable; - const first_field_index = @as(u32, @intCast(operand_ty.unionTagFieldIndex(first_item_val, mod).?)); - const first_field = union_obj.fields.values()[first_field_index]; + const first_field_index: u32 = mod.unionTagFieldIndex(union_obj, first_item_val).?; + const first_field_ty = union_obj.field_types.get(ip)[first_field_index].toType(); const field_tys = try sema.arena.alloc(Type, case_vals.len); for (case_vals, field_tys) |item, *field_ty| { const item_val = sema.resolveConstValue(block, .unneeded, item, "") catch unreachable; - const field_idx = @as(u32, @intCast(operand_ty.unionTagFieldIndex(item_val, sema.mod).?)); - field_ty.* = union_obj.fields.values()[field_idx].ty; + const field_idx = mod.unionTagFieldIndex(union_obj, item_val).?; + field_ty.* = union_obj.field_types.get(ip)[field_idx].toType(); } // Fast path: if all the operands are the same type already, we don't need to hit @@ -10682,7 +10688,7 @@ const SwitchProngAnalysis = struct { if (try sema.resolveDefinedValue(block, operand_src, spa.operand)) |operand_val| { if (operand_val.isUndef(mod)) return mod.undefRef(capture_ty); - const union_val = mod.intern_pool.indexToKey(operand_val.toIntern()).un; + const union_val = ip.indexToKey(operand_val.toIntern()).un; if (union_val.tag.toValue().isUndef(mod)) return mod.undefRef(capture_ty); const uncoerced = Air.internedToRef(union_val.val); return sema.coerce(block, capture_ty, uncoerced, operand_src); @@ -10704,7 +10710,7 @@ const SwitchProngAnalysis = struct { } // All fields are in-memory coercible to the resolved type! // Just take the first field and bitcast the result. - const uncoerced = try block.addStructFieldVal(spa.operand, first_field_index, first_field.ty); + const uncoerced = try block.addStructFieldVal(spa.operand, first_field_index, first_field_ty); return block.addBitCast(capture_ty, uncoerced); }; @@ -12287,7 +12293,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r for (seen_enum_fields, 0..) |seen_field, index| { if (seen_field != null) continue; const union_obj = mod.typeToUnion(maybe_union_ty).?; - const field_ty = union_obj.fields.values()[index].ty; + const field_ty = union_obj.field_types.get(ip)[index].toType(); if (field_ty.zigTypeTag(mod) != .NoReturn) break true; } else false else @@ -12800,9 +12806,8 @@ fn zirHasField(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai break :hf struct_obj.fields.contains(field_name); }, .union_type => |union_type| { - const union_obj = mod.unionPtr(union_type.index); - assert(union_obj.haveFieldTypes()); - break :hf union_obj.fields.contains(field_name); + const union_obj = ip.loadUnionType(union_type); + break :hf union_obj.nameIndex(ip, field_name) != null; }, .enum_type => |enum_type| { break :hf enum_type.nameIndex(ip, field_name) != null; @@ -17271,16 +17276,15 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai }; try sema.resolveTypeLayout(ty); // Getting alignment requires type layout - const layout = ty.containerLayout(mod); + const union_obj = mod.typeToUnion(ty).?; + const layout = union_obj.getLayout(ip); - const union_fields = ty.unionFields(mod); - const union_field_vals = try gpa.alloc(InternPool.Index, union_fields.count()); + const union_field_vals = try gpa.alloc(InternPool.Index, union_obj.field_names.len); defer gpa.free(union_field_vals); for (union_field_vals, 0..) |*field_val, i| { - const field = union_fields.values()[i]; // TODO: write something like getCoercedInts to avoid needing to dupe - const name = try sema.arena.dupe(u8, ip.stringToSlice(union_fields.keys()[i])); + const name = try sema.arena.dupe(u8, ip.stringToSlice(union_obj.field_names.get(ip)[i])); const name_val = v: { var anon_decl = try block.startAnonDecl(); defer anon_decl.deinit(); @@ -17304,15 +17308,16 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai }; const alignment = switch (layout) { - .Auto, .Extern => try sema.unionFieldAlignment(field), + .Auto, .Extern => try sema.unionFieldAlignment(union_obj, @intCast(i)), .Packed => 0, }; + const field_ty = union_obj.field_types.get(ip)[i]; const union_field_fields = .{ // name: []const u8, name_val, // type: type, - field.ty.toIntern(), + field_ty, // alignment: comptime_int, (try mod.intValue(Type.comptime_int, alignment)).toIntern(), }; @@ -18929,18 +18934,18 @@ fn unionInit( field_src: LazySrcLoc, ) CompileError!Air.Inst.Ref { const mod = sema.mod; + const ip = &mod.intern_pool; const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_src); - const field = union_ty.unionFields(mod).values()[field_index]; - const init = try sema.coerce(block, field.ty, uncasted_init, init_src); + const field_ty = mod.typeToUnion(union_ty).?.field_types.get(ip)[field_index].toType(); + const init = try sema.coerce(block, field_ty, uncasted_init, init_src); if (try sema.resolveMaybeUndefVal(init)) |init_val| { const tag_ty = union_ty.unionTagTypeHypothetical(mod); - const enum_field_index = @as(u32, @intCast(tag_ty.enumFieldIndex(field_name, mod).?)); - const tag_val = try mod.enumValueFieldIndex(tag_ty, enum_field_index); + const tag_val = try mod.enumValueFieldIndex(tag_ty, field_index); return Air.internedToRef((try mod.intern(.{ .un = .{ .ty = union_ty.toIntern(), .tag = try tag_val.intern(tag_ty, mod), - .val = try init_val.intern(field.ty, mod), + .val = try init_val.intern(field_ty, mod), } }))); } @@ -18963,6 +18968,7 @@ fn zirStructInit( const src = inst_data.src(); const mod = sema.mod; + const ip = &mod.intern_pool; const first_item = sema.code.extraData(Zir.Inst.StructInit.Item, extra.end).data; const first_field_type_data = zir_datas[first_item.field_type].pl_node; const first_field_type_extra = sema.code.extraData(Zir.Inst.FieldType, first_field_type_data.payload_index).data; @@ -18999,7 +19005,7 @@ fn zirStructInit( const field_type_data = zir_datas[item.data.field_type].pl_node; const field_src: LazySrcLoc = .{ .node_offset_initializer = field_type_data.src_node }; const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data; - const field_name = try mod.intern_pool.getOrPutString(gpa, sema.code.nullTerminatedString(field_type_extra.name_start)); + const field_name = try ip.getOrPutString(gpa, sema.code.nullTerminatedString(field_type_extra.name_start)); const field_index = if (resolved_ty.isTuple(mod)) try sema.tupleFieldIndex(block, resolved_ty, field_name, field_src) else @@ -19040,19 +19046,18 @@ fn zirStructInit( const field_type_data = zir_datas[item.data.field_type].pl_node; const field_src: LazySrcLoc = .{ .node_offset_initializer = field_type_data.src_node }; const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data; - const field_name = try mod.intern_pool.getOrPutString(gpa, sema.code.nullTerminatedString(field_type_extra.name_start)); + const field_name = try ip.getOrPutString(gpa, sema.code.nullTerminatedString(field_type_extra.name_start)); const field_index = try sema.unionFieldIndex(block, resolved_ty, field_name, field_src); const tag_ty = resolved_ty.unionTagTypeHypothetical(mod); - const enum_field_index = @as(u32, @intCast(tag_ty.enumFieldIndex(field_name, mod).?)); - const tag_val = try mod.enumValueFieldIndex(tag_ty, enum_field_index); + const tag_val = try mod.enumValueFieldIndex(tag_ty, field_index); const init_inst = try sema.resolveInst(item.data.init); if (try sema.resolveMaybeUndefVal(init_inst)) |val| { - const field = resolved_ty.unionFields(mod).values()[field_index]; + const field_ty = mod.typeToUnion(resolved_ty).?.field_types.get(ip)[field_index].toType(); return sema.addConstantMaybeRef(block, resolved_ty, (try mod.intern(.{ .un = .{ .ty = resolved_ty.toIntern(), .tag = try tag_val.intern(tag_ty, mod), - .val = try val.intern(field.ty, mod), + .val = try val.intern(field_ty, mod), } })).toValue(), is_ref); } @@ -19662,11 +19667,12 @@ fn fieldType( ty_src: LazySrcLoc, ) CompileError!Air.Inst.Ref { const mod = sema.mod; + const ip = &mod.intern_pool; var cur_ty = aggregate_ty; while (true) { try sema.resolveTypeFields(cur_ty); switch (cur_ty.zigTypeTag(mod)) { - .Struct => switch (mod.intern_pool.indexToKey(cur_ty.toIntern())) { + .Struct => switch (ip.indexToKey(cur_ty.toIntern())) { .anon_struct_type => |anon_struct| { const field_index = try sema.anonStructFieldIndex(block, cur_ty, field_name, field_src); return Air.internedToRef(anon_struct.types[field_index]); @@ -19681,14 +19687,15 @@ fn fieldType( }, .Union => { const union_obj = mod.typeToUnion(cur_ty).?; - const field = union_obj.fields.get(field_name) orelse + const field_index = union_obj.nameIndex(ip, field_name) orelse return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name); - return Air.internedToRef(field.ty.toIntern()); + const field_ty = union_obj.field_types.get(ip)[field_index]; + return Air.internedToRef(field_ty); }, .Optional => { // Struct/array init through optional requires the child type to not be a pointer. // If the child of .optional is a pointer it'll error on the next loop. - cur_ty = mod.intern_pool.indexToKey(cur_ty.toIntern()).opt_type.toType(); + cur_ty = ip.indexToKey(cur_ty.toIntern()).opt_type.toType(); continue; }, .ErrorUnion => { @@ -20396,68 +20403,16 @@ fn zirReify( return sema.fail(block, src, "reified unions must have no decls", .{}); } const layout = mod.toEnum(std.builtin.Type.ContainerLayout, layout_val); - - // Because these three things each reference each other, `undefined` - // placeholders are used before being set after the union type gains an - // InternPool index. - - const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ - .ty = Type.noreturn, - .val = Value.@"unreachable", - }, name_strategy, "union", inst); - const new_decl = mod.declPtr(new_decl_index); - new_decl.owns_tv = true; - errdefer { - new_decl.has_tv = false; // namespace and val were destroyed by later errdefers - mod.abortAnonDecl(new_decl_index); - } - - const new_namespace_index = try mod.createNamespace(.{ - .parent = block.namespace.toOptional(), - .ty = undefined, - .file_scope = block.getFileScope(mod), - }); - const new_namespace = mod.namespacePtr(new_namespace_index); - errdefer mod.destroyNamespace(new_namespace_index); - - const union_index = try mod.createUnion(.{ - .owner_decl = new_decl_index, - .tag_ty = Type.null, - .fields = .{}, - .zir_index = inst, - .layout = layout, - .status = .have_field_types, - .namespace = new_namespace_index, - }); - const union_obj = mod.unionPtr(union_index); - errdefer mod.destroyUnion(union_index); - - const union_ty = try ip.get(gpa, .{ .union_type = .{ - .index = union_index, - .runtime_tag = if (!tag_type_val.isNull(mod)) - .tagged - else if (layout != .Auto) - .none - else switch (mod.optimizeMode()) { - .Debug, .ReleaseSafe => .safety, - .ReleaseFast, .ReleaseSmall => .none, - }, - } }); - // TODO: figure out InternPool removals for incremental compilation - //errdefer ip.remove(union_ty); - - new_decl.ty = Type.type; - new_decl.val = union_ty.toValue(); - new_namespace.ty = union_ty.toType(); + const fields_len: u32 = @intCast(try sema.usizeCast(block, src, fields_val.sliceLen(mod))); // Tag type - const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(mod)); var explicit_tags_seen: []bool = &.{}; var enum_field_names: []InternPool.NullTerminatedString = &.{}; + var enum_tag_ty: InternPool.Index = .none; if (tag_type_val.optionalValue(mod)) |payload_val| { - union_obj.tag_ty = payload_val.toType(); + enum_tag_ty = payload_val.toType().toIntern(); - const enum_type = switch (ip.indexToKey(union_obj.tag_ty.toIntern())) { + const enum_type = switch (ip.indexToKey(enum_tag_ty)) { .enum_type => |x| x, else => return sema.fail(block, src, "Type.Union.tag_type must be an enum type", .{}), }; @@ -20469,7 +20424,13 @@ fn zirReify( } // Fields - try union_obj.fields.ensureTotalCapacity(mod.tmp_hack_arena.allocator(), fields_len); + var any_aligned_fields: bool = false; + var union_fields: std.MultiArrayList(struct { + type: InternPool.Index, + alignment: InternPool.Alignment, + }) = .{}; + var field_name_table: std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void) = .{}; + try field_name_table.ensureTotalCapacity(sema.arena, fields_len); for (0..fields_len) |i| { const elem_val = try fields_val.elemValue(mod, i); @@ -20491,15 +20452,15 @@ fn zirReify( } if (explicit_tags_seen.len > 0) { - const tag_info = ip.indexToKey(union_obj.tag_ty.toIntern()).enum_type; + const tag_info = ip.indexToKey(enum_tag_ty).enum_type; const enum_index = tag_info.nameIndex(ip, field_name) orelse { const msg = msg: { const msg = try sema.errMsg(block, src, "no field named '{}' in enum '{}'", .{ field_name.fmt(ip), - union_obj.tag_ty.fmt(mod), + enum_tag_ty.toType().fmt(mod), }); errdefer msg.destroy(gpa); - try sema.addDeclaredHereNote(msg, union_obj.tag_ty); + try sema.addDeclaredHereNote(msg, enum_tag_ty.toType()); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); @@ -20510,17 +20471,20 @@ fn zirReify( explicit_tags_seen[enum_index] = true; } - const gop = union_obj.fields.getOrPutAssumeCapacity(field_name); + const gop = field_name_table.getOrPutAssumeCapacity(field_name); if (gop.found_existing) { // TODO: better source location return sema.fail(block, src, "duplicate union field {}", .{field_name.fmt(ip)}); } const field_ty = type_val.toType(); - gop.value_ptr.* = .{ - .ty = field_ty, - .abi_align = Alignment.fromByteUnits((try alignment_val.getUnsignedIntAdvanced(mod, sema)).?), - }; + const field_align = Alignment.fromByteUnits((try alignment_val.getUnsignedIntAdvanced(mod, sema)).?); + any_aligned_fields = any_aligned_fields or field_align != .none; + + try union_fields.append(sema.arena, .{ + .type = field_ty.toIntern(), + .alignment = field_align, + }); if (field_ty.zigTypeTag(mod) == .Opaque) { const msg = msg: { @@ -20532,7 +20496,7 @@ fn zirReify( }; return sema.failWithOwnedErrorMsg(msg); } - if (union_obj.layout == .Extern and !try sema.validateExternType(field_ty, .union_field)) { + if (layout == .Extern and !try sema.validateExternType(field_ty, .union_field)) { const msg = msg: { const msg = try sema.errMsg(block, src, "extern unions cannot contain fields of type '{}'", .{field_ty.fmt(mod)}); errdefer msg.destroy(gpa); @@ -20544,7 +20508,7 @@ fn zirReify( break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); - } else if (union_obj.layout == .Packed and !(validatePackedType(field_ty, mod))) { + } else if (layout == .Packed and !(validatePackedType(field_ty, mod))) { const msg = msg: { const msg = try sema.errMsg(block, src, "packed unions cannot contain fields of type '{}'", .{field_ty.fmt(mod)}); errdefer msg.destroy(gpa); @@ -20560,28 +20524,79 @@ fn zirReify( } if (explicit_tags_seen.len > 0) { - const tag_info = ip.indexToKey(union_obj.tag_ty.toIntern()).enum_type; + const tag_info = ip.indexToKey(enum_tag_ty).enum_type; if (tag_info.names.len > fields_len) { const msg = msg: { const msg = try sema.errMsg(block, src, "enum field(s) missing in union", .{}); errdefer msg.destroy(gpa); - const enum_ty = union_obj.tag_ty; for (tag_info.names.get(ip), 0..) |field_name, field_index| { if (explicit_tags_seen[field_index]) continue; - try sema.addFieldErrNote(enum_ty, field_index, msg, "field '{}' missing, declared here", .{ + try sema.addFieldErrNote(enum_tag_ty.toType(), field_index, msg, "field '{}' missing, declared here", .{ field_name.fmt(ip), }); } - try sema.addDeclaredHereNote(msg, union_obj.tag_ty); + try sema.addDeclaredHereNote(msg, enum_tag_ty.toType()); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); } } else { - union_obj.tag_ty = try sema.generateUnionTagTypeSimple(block, enum_field_names, null); + enum_tag_ty = try sema.generateUnionTagTypeSimple(block, enum_field_names, .none); + } + + // Because these three things each reference each other, `undefined` + // placeholders are used before being set after the union type gains an + // InternPool index. + + const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ + .ty = Type.noreturn, + .val = Value.@"unreachable", + }, name_strategy, "union", inst); + const new_decl = mod.declPtr(new_decl_index); + new_decl.owns_tv = true; + errdefer { + new_decl.has_tv = false; // namespace and val were destroyed by later errdefers + mod.abortAnonDecl(new_decl_index); } + const new_namespace_index = try mod.createNamespace(.{ + .parent = block.namespace.toOptional(), + .ty = undefined, + .file_scope = block.getFileScope(mod), + }); + const new_namespace = mod.namespacePtr(new_namespace_index); + errdefer mod.destroyNamespace(new_namespace_index); + + const union_ty = try ip.getUnionType(gpa, .{ + .decl = new_decl_index, + .namespace = new_namespace_index, + .enum_tag_ty = enum_tag_ty, + .fields_len = fields_len, + .zir_index = inst, + .flags = .{ + .layout = layout, + .status = .have_field_types, + .runtime_tag = if (!tag_type_val.isNull(mod)) + .tagged + else if (layout != .Auto) + .none + else switch (block.wantSafety()) { + true => .safety, + false => .none, + }, + .any_aligned_fields = any_aligned_fields, + .requires_comptime = .unknown, + .assumed_runtime_bits = false, + }, + .field_types = union_fields.items(.type), + .field_aligns = if (any_aligned_fields) union_fields.items(.alignment) else &.{}, + }); + + new_decl.ty = Type.type; + new_decl.val = union_ty.toValue(); + new_namespace.ty = union_ty.toType(); + const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); try mod.finalizeAnonDecl(new_decl_index); return decl_val; @@ -23341,7 +23356,7 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr if (mod.typeToStruct(parent_ty)) |struct_obj| { break :blk struct_obj.fields.values()[field_index].abi_align; } else if (mod.typeToUnion(parent_ty)) |union_obj| { - break :blk union_obj.fields.values()[field_index].abi_align; + break :blk union_obj.fieldAlign(ip, field_index); } else { break :blk .none; } @@ -24683,18 +24698,28 @@ fn validateVarType( is_extern: bool, ) CompileError!void { const mod = sema.mod; - if (is_extern and !try sema.validateExternType(var_ty, .other)) { - const msg = msg: { - const msg = try sema.errMsg(block, src, "extern variable cannot have type '{}'", .{var_ty.fmt(mod)}); - errdefer msg.destroy(sema.gpa); - const src_decl = mod.declPtr(block.src_decl); - try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), var_ty, .other); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(msg); + if (is_extern) { + if (!try sema.validateExternType(var_ty, .other)) { + const msg = msg: { + const msg = try sema.errMsg(block, src, "extern variable cannot have type '{}'", .{var_ty.fmt(mod)}); + errdefer msg.destroy(sema.gpa); + const src_decl = mod.declPtr(block.src_decl); + try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), var_ty, .other); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(msg); + } + } else { + if (var_ty.zigTypeTag(mod) == .Opaque) { + return sema.fail( + block, + src, + "non-extern variable with opaque type '{}'", + .{var_ty.fmt(mod)}, + ); + } } - if (is_extern and var_ty.zigTypeTag(mod) == .Opaque) return; if (!try sema.typeRequiresComptime(var_ty)) return; const msg = msg: { @@ -24735,6 +24760,7 @@ fn explainWhyTypeIsComptimeInner( type_set: *TypeSet, ) CompileError!void { const mod = sema.mod; + const ip = &mod.intern_pool; switch (ty.zigTypeTag(mod)) { .Bool, .Int, @@ -24820,15 +24846,16 @@ fn explainWhyTypeIsComptimeInner( if ((try type_set.getOrPut(sema.gpa, ty.toIntern())).found_existing) return; if (mod.typeToUnion(ty)) |union_obj| { - for (union_obj.fields.values(), 0..) |field, i| { - const field_src_loc = mod.fieldSrcLoc(union_obj.owner_decl, .{ + for (0..union_obj.field_types.len) |i| { + const field_ty = union_obj.field_types.get(ip)[i].toType(); + const field_src_loc = mod.fieldSrcLoc(union_obj.decl, .{ .index = i, .range = .type, }); - if (try sema.typeRequiresComptime(field.ty)) { + if (try sema.typeRequiresComptime(field_ty)) { try mod.errNoteNonLazy(field_src_loc, msg, "union requires comptime because of this field", .{}); - try sema.explainWhyTypeIsComptimeInner(msg, field_src_loc, field.ty, type_set); + try sema.explainWhyTypeIsComptimeInner(msg, field_src_loc, field_ty, type_set); } } } @@ -25886,12 +25913,11 @@ fn fieldCallBind( }, .Union => { try sema.resolveTypeFields(concrete_ty); - const fields = concrete_ty.unionFields(mod); - const field_index_usize = fields.getIndex(field_name) orelse break :find_field; - const field_index = @as(u32, @intCast(field_index_usize)); - const field = fields.values()[field_index]; + const union_obj = mod.typeToUnion(concrete_ty).?; + const field_index = union_obj.nameIndex(ip, field_name) orelse break :find_field; + const field_ty = union_obj.field_types.get(ip)[field_index].toType(); - return sema.finishFieldCallBind(block, src, ptr_ty, field.ty, field_index, object_ptr); + return sema.finishFieldCallBind(block, src, ptr_ty, field_ty, field_index, object_ptr); }, .Type => { const namespace = try sema.analyzeLoad(block, src, object_ptr, src); @@ -26378,24 +26404,24 @@ fn unionFieldPtr( try sema.resolveTypeFields(union_ty); const union_obj = mod.typeToUnion(union_ty).?; const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src); - const field = union_obj.fields.values()[field_index]; + const field_ty = union_obj.field_types.get(ip)[field_index].toType(); const ptr_field_ty = try mod.ptrType(.{ - .child = field.ty.toIntern(), + .child = field_ty.toIntern(), .flags = .{ .is_const = union_ptr_info.flags.is_const, .is_volatile = union_ptr_info.flags.is_volatile, .address_space = union_ptr_info.flags.address_space, - .alignment = if (union_obj.layout == .Auto) blk: { + .alignment = if (union_obj.getLayout(ip) == .Auto) blk: { const union_align = union_ptr_info.flags.alignment.toByteUnitsOptional() orelse try sema.typeAbiAlignment(union_ty); - const field_align = try sema.unionFieldAlignment(field); + const field_align = try sema.unionFieldAlignment(union_obj, field_index); break :blk InternPool.Alignment.fromByteUnits(@min(union_align, field_align)); } else union_ptr_info.flags.alignment, }, .packed_offset = union_ptr_info.packed_offset, }); - const enum_field_index = @as(u32, @intCast(union_obj.tag_ty.enumFieldIndex(field_name, mod).?)); + const enum_field_index: u32 = @intCast(union_obj.enum_tag_ty.toType().enumFieldIndex(field_name, mod).?); - if (initializing and field.ty.zigTypeTag(mod) == .NoReturn) { + if (initializing and field_ty.zigTypeTag(mod) == .NoReturn) { const msg = msg: { const msg = try sema.errMsg(block, src, "cannot initialize 'noreturn' field of union", .{}); errdefer msg.destroy(sema.gpa); @@ -26410,7 +26436,7 @@ fn unionFieldPtr( } if (try sema.resolveDefinedValue(block, src, union_ptr)) |union_ptr_val| ct: { - switch (union_obj.layout) { + switch (union_obj.getLayout(ip)) { .Auto => if (!initializing) { const union_val = (try sema.pointerDeref(block, src, union_ptr_val, union_ptr_ty)) orelse break :ct; @@ -26418,12 +26444,12 @@ fn unionFieldPtr( return sema.failWithUseOfUndef(block, src); } const un = ip.indexToKey(union_val.toIntern()).un; - const field_tag = try mod.enumValueFieldIndex(union_obj.tag_ty, enum_field_index); + const field_tag = try mod.enumValueFieldIndex(union_obj.enum_tag_ty.toType(), enum_field_index); const tag_matches = un.tag == field_tag.toIntern(); if (!tag_matches) { const msg = msg: { - const active_index = union_obj.tag_ty.enumTagFieldIndex(un.tag.toValue(), mod).?; - const active_field_name = union_obj.tag_ty.enumFieldName(active_index, mod); + const active_index = union_obj.enum_tag_ty.toType().enumTagFieldIndex(un.tag.toValue(), mod).?; + const active_field_name = union_obj.enum_tag_ty.toType().enumFieldName(active_index, mod); const msg = try sema.errMsg(block, src, "access of union field '{}' while field '{}' is active", .{ field_name.fmt(ip), active_field_name.fmt(ip), @@ -26447,17 +26473,17 @@ fn unionFieldPtr( } try sema.requireRuntimeBlock(block, src, null); - if (!initializing and union_obj.layout == .Auto and block.wantSafety() and - union_ty.unionTagTypeSafety(mod) != null and union_obj.fields.count() > 1) + if (!initializing and union_obj.getLayout(ip) == .Auto and block.wantSafety() and + union_ty.unionTagTypeSafety(mod) != null and union_obj.field_names.len > 1) { - const wanted_tag_val = try mod.enumValueFieldIndex(union_obj.tag_ty, enum_field_index); + const wanted_tag_val = try mod.enumValueFieldIndex(union_obj.enum_tag_ty.toType(), enum_field_index); const wanted_tag = Air.internedToRef(wanted_tag_val.toIntern()); // TODO would it be better if get_union_tag supported pointers to unions? const union_val = try block.addTyOp(.load, union_ty, union_ptr); - const active_tag = try block.addTyOp(.get_union_tag, union_obj.tag_ty, union_val); + const active_tag = try block.addTyOp(.get_union_tag, union_obj.enum_tag_ty.toType(), union_val); try sema.panicInactiveUnionField(block, src, active_tag, wanted_tag); } - if (field.ty.zigTypeTag(mod) == .NoReturn) { + if (field_ty.zigTypeTag(mod) == .NoReturn) { _ = try block.addNoOp(.unreach); return Air.Inst.Ref.unreachable_value; } @@ -26480,23 +26506,23 @@ fn unionFieldVal( try sema.resolveTypeFields(union_ty); const union_obj = mod.typeToUnion(union_ty).?; const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src); - const field = union_obj.fields.values()[field_index]; - const enum_field_index = @as(u32, @intCast(union_obj.tag_ty.enumFieldIndex(field_name, mod).?)); + const field_ty = union_obj.field_types.get(ip)[field_index].toType(); + const enum_field_index: u32 = @intCast(union_obj.enum_tag_ty.toType().enumFieldIndex(field_name, mod).?); if (try sema.resolveMaybeUndefVal(union_byval)) |union_val| { - if (union_val.isUndef(mod)) return mod.undefRef(field.ty); + if (union_val.isUndef(mod)) return mod.undefRef(field_ty); const un = ip.indexToKey(union_val.toIntern()).un; - const field_tag = try mod.enumValueFieldIndex(union_obj.tag_ty, enum_field_index); + const field_tag = try mod.enumValueFieldIndex(union_obj.enum_tag_ty.toType(), enum_field_index); const tag_matches = un.tag == field_tag.toIntern(); - switch (union_obj.layout) { + switch (union_obj.getLayout(ip)) { .Auto => { if (tag_matches) { return Air.internedToRef(un.val); } else { const msg = msg: { - const active_index = union_obj.tag_ty.enumTagFieldIndex(un.tag.toValue(), mod).?; - const active_field_name = union_obj.tag_ty.enumFieldName(active_index, mod); + const active_index = union_obj.enum_tag_ty.toType().enumTagFieldIndex(un.tag.toValue(), mod).?; + const active_field_name = union_obj.enum_tag_ty.toType().enumFieldName(active_index, mod); const msg = try sema.errMsg(block, src, "access of union field '{}' while field '{}' is active", .{ field_name.fmt(ip), active_field_name.fmt(ip), }); @@ -26512,7 +26538,7 @@ fn unionFieldVal( return Air.internedToRef(un.val); } else { const old_ty = union_ty.unionFieldType(un.tag.toValue(), mod); - if (try sema.bitCastVal(block, src, un.val.toValue(), old_ty, field.ty, 0)) |new_val| { + if (try sema.bitCastVal(block, src, un.val.toValue(), old_ty, field_ty, 0)) |new_val| { return Air.internedToRef(new_val.toIntern()); } } @@ -26521,19 +26547,19 @@ fn unionFieldVal( } try sema.requireRuntimeBlock(block, src, null); - if (union_obj.layout == .Auto and block.wantSafety() and - union_ty.unionTagTypeSafety(mod) != null and union_obj.fields.count() > 1) + if (union_obj.getLayout(ip) == .Auto and block.wantSafety() and + union_ty.unionTagTypeSafety(mod) != null and union_obj.field_names.len > 1) { - const wanted_tag_val = try mod.enumValueFieldIndex(union_obj.tag_ty, enum_field_index); + const wanted_tag_val = try mod.enumValueFieldIndex(union_obj.enum_tag_ty.toType(), enum_field_index); const wanted_tag = Air.internedToRef(wanted_tag_val.toIntern()); - const active_tag = try block.addTyOp(.get_union_tag, union_obj.tag_ty, union_byval); + const active_tag = try block.addTyOp(.get_union_tag, union_obj.enum_tag_ty.toType(), union_byval); try sema.panicInactiveUnionField(block, src, active_tag, wanted_tag); } - if (field.ty.zigTypeTag(mod) == .NoReturn) { + if (field_ty.zigTypeTag(mod) == .NoReturn) { _ = try block.addNoOp(.unreach); return Air.Inst.Ref.unreachable_value; } - return block.addStructFieldVal(union_byval, field_index, field.ty); + return block.addStructFieldVal(union_byval, field_index, field_ty); } fn elemPtr( @@ -30048,14 +30074,14 @@ fn coerceEnumToUnion( }; const union_obj = mod.typeToUnion(union_ty).?; - const field = union_obj.fields.values()[field_index]; - try sema.resolveTypeFields(field.ty); - if (field.ty.zigTypeTag(mod) == .NoReturn) { + const field_ty = union_obj.field_types.get(ip)[field_index].toType(); + try sema.resolveTypeFields(field_ty); + if (field_ty.zigTypeTag(mod) == .NoReturn) { const msg = msg: { const msg = try sema.errMsg(block, inst_src, "cannot initialize 'noreturn' field of union", .{}); errdefer msg.destroy(sema.gpa); - const field_name = union_obj.fields.keys()[field_index]; + const field_name = union_obj.field_names.get(ip)[field_index]; try sema.addFieldErrNote(union_ty, field_index, msg, "field '{}' declared here", .{ field_name.fmt(ip), }); @@ -30064,12 +30090,12 @@ fn coerceEnumToUnion( }; return sema.failWithOwnedErrorMsg(msg); } - const opv = (try sema.typeHasOnePossibleValue(field.ty)) orelse { + const opv = (try sema.typeHasOnePossibleValue(field_ty)) orelse { const msg = msg: { - const field_name = union_obj.fields.keys()[field_index]; + const field_name = union_obj.field_names.get(ip)[field_index]; const msg = try sema.errMsg(block, inst_src, "coercion from enum '{}' to union '{}' must initialize '{}' field '{}'", .{ inst_ty.fmt(sema.mod), union_ty.fmt(sema.mod), - field.ty.fmt(sema.mod), field_name.fmt(ip), + field_ty.fmt(sema.mod), field_name.fmt(ip), }); errdefer msg.destroy(sema.gpa); @@ -30104,8 +30130,8 @@ fn coerceEnumToUnion( var msg: ?*Module.ErrorMsg = null; errdefer if (msg) |some| some.destroy(sema.gpa); - for (union_obj.fields.values(), 0..) |field, i| { - if (field.ty.zigTypeTag(mod) == .NoReturn) { + for (union_obj.field_types.get(ip), 0..) |field_ty, field_index| { + if (field_ty.toType().zigTypeTag(mod) == .NoReturn) { const err_msg = msg orelse try sema.errMsg( block, inst_src, @@ -30114,7 +30140,7 @@ fn coerceEnumToUnion( ); msg = err_msg; - try sema.addFieldErrNote(union_ty, i, err_msg, "'noreturn' field here", .{}); + try sema.addFieldErrNote(union_ty, field_index, err_msg, "'noreturn' field here", .{}); } } if (msg) |some| { @@ -30138,11 +30164,9 @@ fn coerceEnumToUnion( ); errdefer msg.destroy(sema.gpa); - var it = union_obj.fields.iterator(); - var field_index: usize = 0; - while (it.next()) |field| : (field_index += 1) { - const field_name = field.key_ptr.*; - const field_ty = field.value_ptr.ty; + for (0..union_obj.field_names.len) |field_index| { + const field_name = union_obj.field_names.get(ip)[field_index]; + const field_ty = union_obj.field_types.get(ip)[field_index].toType(); if (!(try sema.typeHasRuntimeBits(field_ty))) continue; try sema.addFieldErrNote(union_ty, field_index, msg, "field '{}' has type '{}'", .{ field_name.fmt(ip), @@ -30886,6 +30910,9 @@ fn analyzeLoad( .Pointer => ptr_ty.childType(mod), else => return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ty.fmt(sema.mod)}), }; + if (elem_ty.zigTypeTag(mod) == .Opaque) { + return sema.fail(block, ptr_src, "cannot load opaque type '{}'", .{elem_ty.fmt(mod)}); + } if (try sema.typeHasOnePossibleValue(elem_ty)) |opv| { return Air.internedToRef(opv.toIntern()); @@ -33816,7 +33843,7 @@ fn resolveStructLayout(sema: *Sema, ty: Type) CompileError!void { } struct_obj.status = .have_layout; - _ = try sema.resolveTypeRequiresComptime(ty); + _ = try sema.typeRequiresComptime(ty); if (struct_obj.assumed_runtime_bits and !(try sema.typeHasRuntimeBits(ty))) { const msg = try Module.ErrorMsg.create( @@ -34030,44 +34057,46 @@ fn checkMemOperand(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void fn resolveUnionLayout(sema: *Sema, ty: Type) CompileError!void { const mod = sema.mod; + const ip = &mod.intern_pool; try sema.resolveTypeFields(ty); const union_obj = mod.typeToUnion(ty).?; - switch (union_obj.status) { + switch (union_obj.flagsPtr(ip).status) { .none, .have_field_types => {}, .field_types_wip, .layout_wip => { const msg = try Module.ErrorMsg.create( sema.gpa, - union_obj.srcLoc(sema.mod), + mod.declPtr(union_obj.decl).srcLoc(mod), "union '{}' depends on itself", - .{ty.fmt(sema.mod)}, + .{ty.fmt(mod)}, ); return sema.failWithOwnedErrorMsg(msg); }, .have_layout, .fully_resolved_wip, .fully_resolved => return, } - const prev_status = union_obj.status; - errdefer if (union_obj.status == .layout_wip) { - union_obj.status = prev_status; + const prev_status = union_obj.flagsPtr(ip).status; + errdefer if (union_obj.flagsPtr(ip).status == .layout_wip) { + union_obj.flagsPtr(ip).status = prev_status; }; - union_obj.status = .layout_wip; - for (union_obj.fields.values(), 0..) |field, i| { - sema.resolveTypeLayout(field.ty) catch |err| switch (err) { + union_obj.flagsPtr(ip).status = .layout_wip; + for (0..union_obj.field_types.len) |field_index| { + const field_ty = union_obj.field_types.get(ip)[field_index].toType(); + sema.resolveTypeLayout(field_ty) catch |err| switch (err) { error.AnalysisFail => { const msg = sema.err orelse return err; - try sema.addFieldErrNote(ty, i, msg, "while checking this field", .{}); + try sema.addFieldErrNote(ty, field_index, msg, "while checking this field", .{}); return err; }, else => return err, }; } - union_obj.status = .have_layout; - _ = try sema.resolveTypeRequiresComptime(ty); + union_obj.flagsPtr(ip).status = .have_layout; + _ = try sema.typeRequiresComptime(ty); - if (union_obj.assumed_runtime_bits and !(try sema.typeHasRuntimeBits(ty))) { + if (union_obj.flagsPtr(ip).assumed_runtime_bits and !(try sema.typeHasRuntimeBits(ty))) { const msg = try Module.ErrorMsg.create( sema.gpa, - union_obj.srcLoc(sema.mod), + mod.declPtr(union_obj.decl).srcLoc(mod), "union layout depends on it having runtime bits", .{}, ); @@ -34075,163 +34104,6 @@ fn resolveUnionLayout(sema: *Sema, ty: Type) CompileError!void { } } -// In case of querying the ABI alignment of this struct, we will ask -// for hasRuntimeBits() of each field, so we need "requires comptime" -// to be known already before this function returns. -pub fn resolveTypeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool { - const mod = sema.mod; - - return switch (ty.toIntern()) { - .empty_struct_type => false, - else => switch (mod.intern_pool.indexToKey(ty.toIntern())) { - .int_type => false, - .ptr_type => |ptr_type| { - const child_ty = ptr_type.child.toType(); - if (child_ty.zigTypeTag(mod) == .Fn) { - return mod.typeToFunc(child_ty).?.is_generic; - } else { - return sema.resolveTypeRequiresComptime(child_ty); - } - }, - .anyframe_type => |child| { - if (child == .none) return false; - return sema.resolveTypeRequiresComptime(child.toType()); - }, - .array_type => |array_type| return sema.resolveTypeRequiresComptime(array_type.child.toType()), - .vector_type => |vector_type| return sema.resolveTypeRequiresComptime(vector_type.child.toType()), - .opt_type => |child| return sema.resolveTypeRequiresComptime(child.toType()), - .error_union_type => |error_union_type| return sema.resolveTypeRequiresComptime(error_union_type.payload_type.toType()), - .error_set_type, .inferred_error_set_type => false, - - .func_type => true, - - .simple_type => |t| switch (t) { - .f16, - .f32, - .f64, - .f80, - .f128, - .usize, - .isize, - .c_char, - .c_short, - .c_ushort, - .c_int, - .c_uint, - .c_long, - .c_ulong, - .c_longlong, - .c_ulonglong, - .c_longdouble, - .anyopaque, - .bool, - .void, - .anyerror, - .adhoc_inferred_error_set, - .noreturn, - .generic_poison, - .atomic_order, - .atomic_rmw_op, - .calling_convention, - .address_space, - .float_mode, - .reduce_op, - .call_modifier, - .prefetch_options, - .export_options, - .extern_options, - => false, - - .type, - .comptime_int, - .comptime_float, - .null, - .undefined, - .enum_literal, - .type_info, - => true, - }, - .struct_type => |struct_type| { - const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse return false; - switch (struct_obj.requires_comptime) { - .no, .wip => return false, - .yes => return true, - .unknown => { - var requires_comptime = false; - struct_obj.requires_comptime = .wip; - for (struct_obj.fields.values()) |field| { - if (try sema.resolveTypeRequiresComptime(field.ty)) requires_comptime = true; - } - if (requires_comptime) { - struct_obj.requires_comptime = .yes; - } else { - struct_obj.requires_comptime = .no; - } - return requires_comptime; - }, - } - }, - - .anon_struct_type => |tuple| { - for (tuple.types, tuple.values) |field_ty, field_val| { - const have_comptime_val = field_val != .none; - if (!have_comptime_val and try sema.resolveTypeRequiresComptime(field_ty.toType())) { - return true; - } - } - return false; - }, - - .union_type => |union_type| { - const union_obj = mod.unionPtr(union_type.index); - switch (union_obj.requires_comptime) { - .no, .wip => return false, - .yes => return true, - .unknown => { - var requires_comptime = false; - union_obj.requires_comptime = .wip; - for (union_obj.fields.values()) |field| { - if (try sema.resolveTypeRequiresComptime(field.ty)) requires_comptime = true; - } - if (requires_comptime) { - union_obj.requires_comptime = .yes; - } else { - union_obj.requires_comptime = .no; - } - return requires_comptime; - }, - } - }, - - .opaque_type => false, - - .enum_type => |enum_type| try sema.resolveTypeRequiresComptime(enum_type.tag_ty.toType()), - - // values, not types - .undef, - .runtime_value, - .simple_value, - .variable, - .extern_func, - .func, - .int, - .err, - .error_union, - .enum_literal, - .enum_tag, - .empty_enum_value, - .float, - .ptr, - .opt, - .aggregate, - .un, - // memoization, not types - .memoized_call, - => unreachable, - }, - }; -} - /// Returns `error.AnalysisFail` if any of the types (recursively) failed to /// be resolved. pub fn resolveTypeFully(sema: *Sema, ty: Type) CompileError!void { @@ -34306,11 +34178,12 @@ fn resolveStructFully(sema: *Sema, ty: Type) CompileError!void { fn resolveUnionFully(sema: *Sema, ty: Type) CompileError!void { try sema.resolveUnionLayout(ty); + try sema.resolveTypeFields(ty); const mod = sema.mod; - try sema.resolveTypeFields(ty); + const ip = &mod.intern_pool; const union_obj = mod.typeToUnion(ty).?; - switch (union_obj.status) { + switch (union_obj.flagsPtr(ip).status) { .none, .have_field_types, .field_types_wip, .layout_wip, .have_layout => {}, .fully_resolved_wip, .fully_resolved => return, } @@ -34319,14 +34192,15 @@ fn resolveUnionFully(sema: *Sema, ty: Type) CompileError!void { // After we have resolve union layout we have to go over the fields again to // make sure pointer fields get their child types resolved as well. // See also similar code for structs. - const prev_status = union_obj.status; - errdefer union_obj.status = prev_status; + const prev_status = union_obj.flagsPtr(ip).status; + errdefer union_obj.flagsPtr(ip).status = prev_status; - union_obj.status = .fully_resolved_wip; - for (union_obj.fields.values()) |field| { - try sema.resolveTypeFully(field.ty); + union_obj.flagsPtr(ip).status = .fully_resolved_wip; + for (0..union_obj.field_types.len) |field_index| { + const field_ty = union_obj.field_types.get(ip)[field_index].toType(); + try sema.resolveTypeFully(field_ty); } - union_obj.status = .fully_resolved; + union_obj.flagsPtr(ip).status = .fully_resolved; } // And let's not forget comptime-only status. @@ -34420,19 +34294,14 @@ pub fn resolveTypeFields(sema: *Sema, ty: Type) CompileError!void { else => switch (mod.intern_pool.items.items(.tag)[@intFromEnum(ty.toIntern())]) { .type_struct, .type_struct_ns, - .type_union_tagged, - .type_union_untagged, - .type_union_safety, + .type_union, .simple_type, => switch (mod.intern_pool.indexToKey(ty.toIntern())) { .struct_type => |struct_type| { const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse return; try sema.resolveTypeFieldsStruct(ty, struct_obj); }, - .union_type => |union_type| { - const union_obj = mod.unionPtr(union_type.index); - try sema.resolveTypeFieldsUnion(ty, union_obj); - }, + .union_type => |union_type| try sema.resolveTypeFieldsUnion(ty, union_type), .simple_type => |simple_type| try sema.resolveSimpleType(simple_type), else => unreachable, }, @@ -34504,27 +34373,30 @@ fn resolveTypeFieldsStruct( try semaStructFields(sema.mod, struct_obj); } -fn resolveTypeFieldsUnion(sema: *Sema, ty: Type, union_obj: *Module.Union) CompileError!void { - switch (sema.mod.declPtr(union_obj.owner_decl).analysis) { +fn resolveTypeFieldsUnion(sema: *Sema, ty: Type, union_type: InternPool.Key.UnionType) CompileError!void { + const mod = sema.mod; + const ip = &mod.intern_pool; + const owner_decl = mod.declPtr(union_type.decl); + switch (owner_decl.analysis) { .file_failure, .dependency_failure, .sema_failure, .sema_failure_retryable, => { sema.owner_decl.analysis = .dependency_failure; - sema.owner_decl.generation = sema.mod.generation; + sema.owner_decl.generation = mod.generation; return error.AnalysisFail; }, else => {}, } - switch (union_obj.status) { + switch (union_type.flagsPtr(ip).status) { .none => {}, .field_types_wip => { const msg = try Module.ErrorMsg.create( sema.gpa, - union_obj.srcLoc(sema.mod), + owner_decl.srcLoc(mod), "union '{}' depends on itself", - .{ty.fmt(sema.mod)}, + .{ty.fmt(mod)}, ); return sema.failWithOwnedErrorMsg(msg); }, @@ -34536,10 +34408,10 @@ fn resolveTypeFieldsUnion(sema: *Sema, ty: Type, union_obj: *Module.Union) Compi => return, } - union_obj.status = .field_types_wip; - errdefer union_obj.status = .none; - try semaUnionFields(sema.mod, union_obj); - union_obj.status = .have_field_types; + union_type.flagsPtr(ip).status = .field_types_wip; + errdefer union_type.flagsPtr(ip).status = .none; + try semaUnionFields(mod, sema.arena, union_type); + union_type.flagsPtr(ip).status = .have_field_types; } /// Returns a normal error set corresponding to the fully populated inferred @@ -35027,24 +34899,24 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void struct_obj.have_field_inits = true; } -fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { +fn semaUnionFields(mod: *Module, arena: Allocator, union_type: InternPool.Key.UnionType) CompileError!void { const tracy = trace(@src()); defer tracy.end(); const gpa = mod.gpa; const ip = &mod.intern_pool; - const decl_index = union_obj.owner_decl; - const zir = mod.namespacePtr(union_obj.namespace).file_scope.zir; - const extended = zir.instructions.items(.data)[union_obj.zir_index].extended; + const decl_index = union_type.decl; + const zir = mod.namespacePtr(union_type.namespace).file_scope.zir; + const extended = zir.instructions.items(.data)[union_type.zir_index].extended; assert(extended.opcode == .union_decl); - const small = @as(Zir.Inst.UnionDecl.Small, @bitCast(extended.small)); + const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small); var extra_index: usize = extended.operand; const src = LazySrcLoc.nodeOffset(0); extra_index += @intFromBool(small.has_src_node); const tag_type_ref: Zir.Inst.Ref = if (small.has_tag_type) blk: { - const ty_ref = @as(Zir.Inst.Ref, @enumFromInt(zir.extra[extra_index])); + const ty_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]); extra_index += 1; break :blk ty_ref; } else .none; @@ -35077,16 +34949,13 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { const decl = mod.declPtr(decl_index); - var analysis_arena = std.heap.ArenaAllocator.init(gpa); - defer analysis_arena.deinit(); - var comptime_mutable_decls = std.ArrayList(Decl.Index).init(gpa); defer comptime_mutable_decls.deinit(); var sema: Sema = .{ .mod = mod, .gpa = gpa, - .arena = analysis_arena.allocator(), + .arena = arena, .code = zir, .owner_decl = decl, .owner_decl_index = decl_index, @@ -35106,7 +34975,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { .parent = null, .sema = &sema, .src_decl = decl_index, - .namespace = union_obj.namespace, + .namespace = union_type.namespace, .wip_capture_scope = wip_captures.scope, .instructions = .{}, .inlining = null, @@ -35124,8 +34993,6 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { _ = try ct_decl.internValue(mod); } - try union_obj.fields.ensureTotalCapacity(mod.tmp_hack_arena.allocator(), fields_len); - var int_tag_ty: Type = undefined; var enum_field_names: []InternPool.NullTerminatedString = &.{}; var enum_field_vals: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .{}; @@ -35159,10 +35026,10 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { } } else { // The provided type is the enum tag type. - union_obj.tag_ty = provided_ty; - const enum_type = switch (ip.indexToKey(union_obj.tag_ty.toIntern())) { + union_type.tagTypePtr(ip).* = provided_ty.toIntern(); + const enum_type = switch (ip.indexToKey(provided_ty.toIntern())) { .enum_type => |x| x, - else => return sema.fail(&block_scope, tag_ty_src, "expected enum tag type, found '{}'", .{union_obj.tag_ty.fmt(mod)}), + else => return sema.fail(&block_scope, tag_ty_src, "expected enum tag type, found '{}'", .{provided_ty.fmt(mod)}), }; // The fields of the union must match the enum exactly. // A flag per field is used to check for missing and extraneous fields. @@ -35176,6 +35043,15 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { enum_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, fields_len); } + var field_types: std.ArrayListUnmanaged(InternPool.Index) = .{}; + var field_aligns: std.ArrayListUnmanaged(InternPool.Alignment) = .{}; + var field_name_table: std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void) = .{}; + + try field_types.ensureTotalCapacityPrecise(sema.arena, fields_len); + if (small.any_aligned_fields) + try field_aligns.ensureTotalCapacityPrecise(sema.arena, fields_len); + try field_name_table.ensureTotalCapacity(sema.arena, fields_len); + const bits_per_field = 4; const fields_per_u32 = 32 / bits_per_field; const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; @@ -35206,19 +35082,19 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { extra_index += 1; const field_type_ref: Zir.Inst.Ref = if (has_type) blk: { - const field_type_ref = @as(Zir.Inst.Ref, @enumFromInt(zir.extra[extra_index])); + const field_type_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]); extra_index += 1; break :blk field_type_ref; } else .none; const align_ref: Zir.Inst.Ref = if (has_align) blk: { - const align_ref = @as(Zir.Inst.Ref, @enumFromInt(zir.extra[extra_index])); + const align_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]); extra_index += 1; break :blk align_ref; } else .none; const tag_ref: Air.Inst.Ref = if (has_tag) blk: { - const tag_ref = @as(Zir.Inst.Ref, @enumFromInt(zir.extra[extra_index])); + const tag_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]); extra_index += 1; break :blk try sema.resolveInst(tag_ref); } else .none; @@ -35227,7 +35103,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { const enum_tag_val = if (tag_ref != .none) blk: { const val = sema.semaUnionFieldVal(&block_scope, .unneeded, int_tag_ty, tag_ref) catch |err| switch (err) { error.NeededSourceLocation => { - const val_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ + const val_src = mod.fieldSrcLoc(union_type.decl, .{ .index = field_i, .range = .value, }).lazy; @@ -35250,8 +35126,8 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { }; const gop = enum_field_vals.getOrPutAssumeCapacity(enum_tag_val.toIntern()); if (gop.found_existing) { - const field_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = field_i }).lazy; - const other_field_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = gop.index }).lazy; + const field_src = mod.fieldSrcLoc(union_type.decl, .{ .index = field_i }).lazy; + const other_field_src = mod.fieldSrcLoc(union_type.decl, .{ .index = gop.index }).lazy; const msg = msg: { const msg = try sema.errMsg(&block_scope, field_src, "enum tag value {} already taken", .{enum_tag_val.fmtValue(int_tag_ty, mod)}); errdefer msg.destroy(gpa); @@ -35275,7 +35151,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { else sema.resolveType(&block_scope, .unneeded, field_type_ref) catch |err| switch (err) { error.NeededSourceLocation => { - const ty_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ + const ty_src = mod.fieldSrcLoc(union_type.decl, .{ .index = field_i, .range = .type, }).lazy; @@ -35289,17 +35165,16 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { return error.GenericPoison; } - const gop = union_obj.fields.getOrPutAssumeCapacity(field_name); + const gop = field_name_table.getOrPutAssumeCapacity(field_name); if (gop.found_existing) { const msg = msg: { - const field_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = field_i }).lazy; + const field_src = mod.fieldSrcLoc(union_type.decl, .{ .index = field_i }).lazy; const msg = try sema.errMsg(&block_scope, field_src, "duplicate union field: '{}'", .{ field_name.fmt(ip), }); errdefer msg.destroy(gpa); - const prev_field_index = union_obj.fields.getIndex(field_name).?; - const prev_field_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = prev_field_index }).lazy; + const prev_field_src = mod.fieldSrcLoc(union_type.decl, .{ .index = gop.index }).lazy; try mod.errNoteNonLazy(prev_field_src.toSrcLoc(decl, mod), msg, "other field here", .{}); try sema.errNote(&block_scope, src, msg, "union declared here", .{}); break :msg msg; @@ -35308,18 +35183,18 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { } if (explicit_tags_seen.len > 0) { - const tag_info = ip.indexToKey(union_obj.tag_ty.toIntern()).enum_type; + const tag_info = ip.indexToKey(union_type.tagTypePtr(ip).*).enum_type; const enum_index = tag_info.nameIndex(ip, field_name) orelse { const msg = msg: { - const ty_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ + const ty_src = mod.fieldSrcLoc(union_type.decl, .{ .index = field_i, .range = .type, }).lazy; const msg = try sema.errMsg(&block_scope, ty_src, "no field named '{}' in enum '{}'", .{ - field_name.fmt(ip), union_obj.tag_ty.fmt(mod), + field_name.fmt(ip), union_type.tagTypePtr(ip).toType().fmt(mod), }); errdefer msg.destroy(sema.gpa); - try sema.addDeclaredHereNote(msg, union_obj.tag_ty); + try sema.addDeclaredHereNote(msg, union_type.tagTypePtr(ip).toType()); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); @@ -35328,11 +35203,29 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { // to create the enum type in the first place. assert(!explicit_tags_seen[enum_index]); explicit_tags_seen[enum_index] = true; + + // Enforce the enum fields and the union fields being in the same order. + if (enum_index != field_i) { + const msg = msg: { + const ty_src = mod.fieldSrcLoc(union_type.decl, .{ + .index = field_i, + .range = .type, + }).lazy; + const enum_field_src = mod.fieldSrcLoc(tag_info.decl, .{ .index = enum_index }).lazy; + const msg = try sema.errMsg(&block_scope, ty_src, "union field '{}' ordered differently than corresponding enum field", .{ + field_name.fmt(ip), + }); + errdefer msg.destroy(sema.gpa); + try sema.errNote(&block_scope, enum_field_src, msg, "enum field here", .{}); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(msg); + } } if (field_ty.zigTypeTag(mod) == .Opaque) { const msg = msg: { - const ty_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ + const ty_src = mod.fieldSrcLoc(union_type.decl, .{ .index = field_i, .range = .type, }).lazy; @@ -35344,9 +35237,12 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { }; return sema.failWithOwnedErrorMsg(msg); } - if (union_obj.layout == .Extern and !try sema.validateExternType(field_ty, .union_field)) { + const layout = union_type.getLayout(ip); + if (layout == .Extern and + !try sema.validateExternType(field_ty, .union_field)) + { const msg = msg: { - const ty_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ + const ty_src = mod.fieldSrcLoc(union_type.decl, .{ .index = field_i, .range = .type, }); @@ -35359,9 +35255,9 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); - } else if (union_obj.layout == .Packed and !(validatePackedType(field_ty, mod))) { + } else if (layout == .Packed and !validatePackedType(field_ty, mod)) { const msg = msg: { - const ty_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ + const ty_src = mod.fieldSrcLoc(union_type.decl, .{ .index = field_i, .range = .type, }); @@ -35376,51 +35272,55 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { return sema.failWithOwnedErrorMsg(msg); } - gop.value_ptr.* = .{ - .ty = field_ty, - .abi_align = .none, - }; + field_types.appendAssumeCapacity(field_ty.toIntern()); - if (align_ref != .none) { - gop.value_ptr.abi_align = sema.resolveAlign(&block_scope, .unneeded, align_ref) catch |err| switch (err) { - error.NeededSourceLocation => { - const align_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ - .index = field_i, - .range = .alignment, - }).lazy; - _ = try sema.resolveAlign(&block_scope, align_src, align_ref); - unreachable; - }, - else => |e| return e, - }; + if (small.any_aligned_fields) { + field_aligns.appendAssumeCapacity(if (align_ref != .none) + sema.resolveAlign(&block_scope, .unneeded, align_ref) catch |err| switch (err) { + error.NeededSourceLocation => { + const align_src = mod.fieldSrcLoc(union_type.decl, .{ + .index = field_i, + .range = .alignment, + }).lazy; + _ = try sema.resolveAlign(&block_scope, align_src, align_ref); + unreachable; + }, + else => |e| return e, + } + else + .none); } else { - gop.value_ptr.abi_align = .none; + assert(align_ref == .none); } } + union_type.setFieldTypes(ip, field_types.items); + union_type.setFieldAligns(ip, field_aligns.items); + if (explicit_tags_seen.len > 0) { - const tag_info = ip.indexToKey(union_obj.tag_ty.toIntern()).enum_type; + const tag_info = ip.indexToKey(union_type.tagTypePtr(ip).*).enum_type; if (tag_info.names.len > fields_len) { const msg = msg: { const msg = try sema.errMsg(&block_scope, src, "enum field(s) missing in union", .{}); errdefer msg.destroy(sema.gpa); - const enum_ty = union_obj.tag_ty; for (tag_info.names.get(ip), 0..) |field_name, field_index| { if (explicit_tags_seen[field_index]) continue; - try sema.addFieldErrNote(enum_ty, field_index, msg, "field '{}' missing, declared here", .{ + try sema.addFieldErrNote(union_type.tagTypePtr(ip).toType(), field_index, msg, "field '{}' missing, declared here", .{ field_name.fmt(ip), }); } - try sema.addDeclaredHereNote(msg, union_obj.tag_ty); + try sema.addDeclaredHereNote(msg, union_type.tagTypePtr(ip).toType()); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); } } else if (enum_field_vals.count() > 0) { - union_obj.tag_ty = try sema.generateUnionTagTypeNumbered(&block_scope, enum_field_names, enum_field_vals.keys(), union_obj); + const enum_ty = try sema.generateUnionTagTypeNumbered(&block_scope, enum_field_names, enum_field_vals.keys(), mod.declPtr(union_type.decl)); + union_type.tagTypePtr(ip).* = enum_ty; } else { - union_obj.tag_ty = try sema.generateUnionTagTypeSimple(&block_scope, enum_field_names, union_obj); + const enum_ty = try sema.generateUnionTagTypeSimple(&block_scope, enum_field_names, union_type.decl.toOptional()); + union_type.tagTypePtr(ip).* = enum_ty; } } @@ -35434,8 +35334,8 @@ fn generateUnionTagTypeNumbered( block: *Block, enum_field_names: []const InternPool.NullTerminatedString, enum_field_vals: []const InternPool.Index, - union_obj: *Module.Union, -) !Type { + decl: *Module.Decl, +) !InternPool.Index { const mod = sema.mod; const gpa = sema.gpa; const ip = &mod.intern_pool; @@ -35443,7 +35343,7 @@ fn generateUnionTagTypeNumbered( const src_decl = mod.declPtr(block.src_decl); const new_decl_index = try mod.allocateNewDecl(block.namespace, src_decl.src_node, block.wip_capture_scope); errdefer mod.destroyDecl(new_decl_index); - const fqn = try union_obj.getFullyQualifiedName(mod); + const fqn = try decl.getFullyQualifiedName(mod); const name = try ip.getOrPutStringFmt(gpa, "@typeInfo({}).Union.tag_type.?", .{fqn.fmt(ip)}); try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, .{ .ty = Type.noreturn, @@ -35472,30 +35372,30 @@ fn generateUnionTagTypeNumbered( new_decl.val = enum_ty.toValue(); try mod.finalizeAnonDecl(new_decl_index); - return enum_ty.toType(); + return enum_ty; } fn generateUnionTagTypeSimple( sema: *Sema, block: *Block, enum_field_names: []const InternPool.NullTerminatedString, - maybe_union_obj: ?*Module.Union, -) !Type { + maybe_decl_index: Module.Decl.OptionalIndex, +) !InternPool.Index { const mod = sema.mod; const ip = &mod.intern_pool; const gpa = sema.gpa; const new_decl_index = new_decl_index: { - const union_obj = maybe_union_obj orelse { + const decl_index = maybe_decl_index.unwrap() orelse { break :new_decl_index try mod.createAnonymousDecl(block, .{ .ty = Type.noreturn, .val = Value.@"unreachable", }); }; + const fqn = try mod.declPtr(decl_index).getFullyQualifiedName(mod); const src_decl = mod.declPtr(block.src_decl); const new_decl_index = try mod.allocateNewDecl(block.namespace, src_decl.src_node, block.wip_capture_scope); errdefer mod.destroyDecl(new_decl_index); - const fqn = try union_obj.getFullyQualifiedName(mod); const name = try ip.getOrPutStringFmt(gpa, "@typeInfo({}).Union.tag_type.?", .{fqn.fmt(ip)}); try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, .{ .ty = Type.noreturn, @@ -35524,7 +35424,7 @@ fn generateUnionTagTypeSimple( new_decl.val = enum_ty.toValue(); try mod.finalizeAnonDecl(new_decl_index); - return enum_ty.toType(); + return enum_ty; } fn getBuiltin(sema: *Sema, name: []const u8) CompileError!Air.Inst.Ref { @@ -35787,9 +35687,7 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { .type_struct_ns, .type_struct_anon, .type_tuple_anon, - .type_union_tagged, - .type_union_untagged, - .type_union_safety, + .type_union, => switch (ip.indexToKey(ty.toIntern())) { inline .array_type, .vector_type => |seq_type, seq_tag| { const has_sentinel = seq_tag == .array_type and seq_type.sentinel != .none; @@ -35816,12 +35714,12 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { field_val.* = field.default_val; continue; } - if (field.ty.eql(ty, sema.mod)) { + if (field.ty.eql(ty, mod)) { const msg = try Module.ErrorMsg.create( sema.gpa, - s.srcLoc(sema.mod), + s.srcLoc(mod), "struct '{}' depends on itself", - .{ty.fmt(sema.mod)}, + .{ty.fmt(mod)}, ); try sema.addFieldErrNote(ty, i, msg, "while checking this field", .{}); return sema.failWithOwnedErrorMsg(msg); @@ -35862,26 +35760,25 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { .union_type => |union_type| { try sema.resolveTypeFields(ty); - const union_obj = mod.unionPtr(union_type.index); - const tag_val = (try sema.typeHasOnePossibleValue(union_obj.tag_ty)) orelse + const union_obj = ip.loadUnionType(union_type); + const tag_val = (try sema.typeHasOnePossibleValue(union_obj.enum_tag_ty.toType())) orelse return null; - const fields = union_obj.fields.values(); - if (fields.len == 0) { + if (union_obj.field_types.len == 0) { const only = try mod.intern(.{ .empty_enum_value = ty.toIntern() }); return only.toValue(); } - const only_field = fields[0]; - if (only_field.ty.eql(ty, sema.mod)) { + const only_field_ty = union_obj.field_types.get(ip)[0].toType(); + if (only_field_ty.eql(ty, mod)) { const msg = try Module.ErrorMsg.create( sema.gpa, - union_obj.srcLoc(sema.mod), + mod.declPtr(union_obj.decl).srcLoc(mod), "union '{}' depends on itself", - .{ty.fmt(sema.mod)}, + .{ty.fmt(mod)}, ); try sema.addFieldErrNote(ty, 0, msg, "while checking this field", .{}); return sema.failWithOwnedErrorMsg(msg); } - const val_val = (try sema.typeHasOnePossibleValue(only_field.ty)) orelse + const val_val = (try sema.typeHasOnePossibleValue(only_field_ty)) orelse return null; const only = try mod.intern(.{ .un = .{ .ty = ty.toIntern(), @@ -36225,10 +36122,11 @@ fn typePtrOrOptionalPtrTy(sema: *Sema, ty: Type) !?Type { /// elsewhere in value.zig pub fn typeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool { const mod = sema.mod; + const ip = &mod.intern_pool; return switch (ty.toIntern()) { .empty_struct_type => false, - else => switch (mod.intern_pool.indexToKey(ty.toIntern())) { + else => switch (ip.indexToKey(ty.toIntern())) { .int_type => return false, .ptr_type => |ptr_type| { const child_ty = ptr_type.child.toType(); @@ -36254,7 +36152,7 @@ pub fn typeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool { .func_type => true, - .simple_type => |t| return switch (t) { + .simple_type => |t| switch (t) { .f16, .f32, .f64, @@ -36272,9 +36170,11 @@ pub fn typeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool { .c_longlong, .c_ulonglong, .c_longdouble, + .anyopaque, .bool, .void, .anyerror, + .adhoc_inferred_error_set, .noreturn, .generic_poison, .atomic_order, @@ -36287,10 +36187,8 @@ pub fn typeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool { .prefetch_options, .export_options, .extern_options, - .adhoc_inferred_error_set, => false, - .anyopaque, .type, .comptime_int, .comptime_float, @@ -36335,30 +36233,31 @@ pub fn typeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool { }, .union_type => |union_type| { - const union_obj = mod.unionPtr(union_type.index); - switch (union_obj.requires_comptime) { + switch (union_type.flagsPtr(ip).requires_comptime) { .no, .wip => return false, .yes => return true, .unknown => { - if (union_obj.status == .field_types_wip) + if (union_type.flagsPtr(ip).status == .field_types_wip) return false; - try sema.resolveTypeFieldsUnion(ty, union_obj); + try sema.resolveTypeFieldsUnion(ty, union_type); + const union_obj = ip.loadUnionType(union_type); - union_obj.requires_comptime = .wip; - for (union_obj.fields.values()) |field| { - if (try sema.typeRequiresComptime(field.ty)) { - union_obj.requires_comptime = .yes; + union_obj.flagsPtr(ip).requires_comptime = .wip; + for (0..union_obj.field_types.len) |field_index| { + const field_ty = union_obj.field_types.get(ip)[field_index]; + if (try sema.typeRequiresComptime(field_ty.toType())) { + union_obj.flagsPtr(ip).requires_comptime = .yes; return true; } } - union_obj.requires_comptime = .no; + union_obj.flagsPtr(ip).requires_comptime = .no; return false; }, } }, - .opaque_type => true, + .opaque_type => false, .enum_type => |enum_type| try sema.typeRequiresComptime(enum_type.tag_ty.toType()), // values, not types @@ -36404,12 +36303,15 @@ fn typeAbiAlignment(sema: *Sema, ty: Type) CompileError!u32 { } /// Not valid to call for packed unions. -/// Keep implementation in sync with `Module.Union.Field.normalAlignment`. -fn unionFieldAlignment(sema: *Sema, field: Module.Union.Field) !u32 { - return @as(u32, @intCast(if (field.ty.isNoReturn(sema.mod)) - 0 - else - field.abi_align.toByteUnitsOptional() orelse try sema.typeAbiAlignment(field.ty))); +/// Keep implementation in sync with `Module.unionFieldNormalAlignment`. +/// TODO: this returns alignment in byte units should should be a u64 +fn unionFieldAlignment(sema: *Sema, u: InternPool.UnionType, field_index: u32) !u32 { + const mod = sema.mod; + 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(); + if (field_ty.isNoReturn(sema.mod)) return 0; + return @intCast(try sema.typeAbiAlignment(field_ty)); } /// Keep implementation in sync with `Module.Struct.Field.alignment`. @@ -36459,11 +36361,12 @@ fn unionFieldIndex( field_src: LazySrcLoc, ) !u32 { const mod = sema.mod; + const ip = &mod.intern_pool; try sema.resolveTypeFields(union_ty); const union_obj = mod.typeToUnion(union_ty).?; - const field_index_usize = union_obj.fields.getIndex(field_name) orelse + const field_index = union_obj.nameIndex(ip, field_name) orelse return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name); - return @as(u32, @intCast(field_index_usize)); + return @intCast(field_index); } fn structFieldIndex( diff --git a/src/TypedValue.zig b/src/TypedValue.zig index 415d54d7e1af..950613f4ff56 100644 --- a/src/TypedValue.zig +++ b/src/TypedValue.zig @@ -88,7 +88,7 @@ pub fn print( try writer.writeAll(".{ "); 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, }, writer, level - 1, mod); try writer.writeAll(" = "); @@ -357,7 +357,7 @@ pub fn print( try writer.print(".{i}", .{field_name.fmt(ip)}); }, .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)}); }, .Pointer => { diff --git a/src/Zir.zig b/src/Zir.zig index d38b2d01e375..5b149d02d99e 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -2956,7 +2956,8 @@ pub const Inst = struct { /// true | true | union(enum(T)) { } /// true | false | union(T) { } auto_enum_tag: bool, - _: u6 = undefined, + any_aligned_fields: bool, + _: u5 = undefined, }; }; diff --git a/src/arch/aarch64/abi.zig b/src/arch/aarch64/abi.zig index 72a617289544..e848fdac3fa7 100644 --- a/src/arch/aarch64/abi.zig +++ b/src/arch/aarch64/abi.zig @@ -75,14 +75,15 @@ pub fn classifyType(ty: Type, mod: *Module) Class { const sret_float_count = 4; fn countFloats(ty: Type, mod: *Module, maybe_float_bits: *?u16) u8 { + const ip = &mod.intern_pool; const target = mod.getTarget(); const invalid = std.math.maxInt(u8); switch (ty.zigTypeTag(mod)) { .Union => { - const fields = ty.unionFields(mod); + const union_obj = mod.typeToUnion(ty).?; var max_count: u8 = 0; - for (fields.values()) |field| { - const field_count = countFloats(field.ty, mod, maybe_float_bits); + for (union_obj.field_types.get(ip)) |field_ty| { + const field_count = countFloats(field_ty.toType(), mod, maybe_float_bits); if (field_count == invalid) return invalid; if (field_count > max_count) max_count = field_count; 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 { + const ip = &mod.intern_pool; switch (ty.zigTypeTag(mod)) { .Union => { - const fields = ty.unionFields(mod); - for (fields.values()) |field| { - if (getFloatArrayType(field.ty, mod)) |some| return some; + const union_obj = mod.typeToUnion(ty).?; + for (union_obj.field_types.get(ip)) |field_ty| { + if (getFloatArrayType(field_ty.toType(), mod)) |some| return some; } return null; }, diff --git a/src/arch/arm/abi.zig b/src/arch/arm/abi.zig index 2e1e26d22045..7889fc09c01c 100644 --- a/src/arch/arm/abi.zig +++ b/src/arch/arm/abi.zig @@ -29,6 +29,7 @@ pub fn classifyType(ty: Type, mod: *Module, ctx: Context) Class { var maybe_float_bits: ?u16 = null; const max_byval_size = 512; + const ip = &mod.intern_pool; switch (ty.zigTypeTag(mod)) { .Struct => { const bit_size = ty.bitSize(mod); @@ -54,7 +55,8 @@ pub fn classifyType(ty: Type, mod: *Module, ctx: Context) Class { }, .Union => { 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; 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); if (float_count <= byval_float_count) return .byval; - for (ty.unionFields(mod).values()) |field| { - if (field.ty.bitSize(mod) > 32 or field.normalAlignment(mod) > 32) { + for (union_obj.field_types.get(ip), 0..) |field_ty, field_index| { + if (field_ty.toType().bitSize(mod) > 32 or + mod.unionFieldNormalAlignment(union_obj, @intCast(field_index)) > 32) + { 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; fn countFloats(ty: Type, mod: *Module, maybe_float_bits: *?u16) u32 { + const ip = &mod.intern_pool; const target = mod.getTarget(); const invalid = std.math.maxInt(u32); switch (ty.zigTypeTag(mod)) { .Union => { - const fields = ty.unionFields(mod); + const union_obj = mod.typeToUnion(ty).?; var max_count: u32 = 0; - for (fields.values()) |field| { - const field_count = countFloats(field.ty, mod, maybe_float_bits); + for (union_obj.field_types.get(ip)) |field_ty| { + const field_count = countFloats(field_ty.toType(), mod, maybe_float_bits); if (field_count == invalid) return invalid; if (field_count > max_count) max_count = field_count; if (max_count > byval_float_count) return invalid; diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 98fe8c8b7c38..2fd5a50d0b3b 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -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 /// by reference, rather than by value fn isByRef(ty: Type, mod: *Module) bool { + const ip = &mod.intern_pool; const target = mod.getTarget(); switch (ty.zigTypeTag(mod)) { .Type, @@ -1742,7 +1743,7 @@ fn isByRef(ty: Type, mod: *Module) bool { => return ty.hasRuntimeBitsIgnoreComptime(mod), .Union => { if (mod.typeToUnion(ty)) |union_obj| { - if (union_obj.layout == .Packed) { + if (union_obj.getLayout(ip) == .Packed) { 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)) { .Packed => 0, 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_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 { const mod = func.bin_file.base.options.module.?; + const ip = &mod.intern_pool; 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(), else => {}, } @@ -3110,7 +3112,7 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue { => unreachable, // comptime-only types }; - switch (mod.intern_pool.indexToKey(val.ip_index)) { + switch (ip.indexToKey(val.ip_index)) { .int_type, .ptr_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", .{}); }, .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()); }, .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) { .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), - .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), 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 { 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)}), .vector_type => { assert(determineSimdStoreStrategy(ty, mod) == .direct); @@ -3245,11 +3247,12 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue { }, else => unreachable, }, - .un => |union_obj| { + .un => |un| { // 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 field_ty = ty.unionFields(mod).values()[field_index].ty; - return func.lowerConstant(union_obj.val.toValue(), field_ty); + const union_obj = mod.typeToUnion(ty).?; + const field_index = mod.unionTagFieldIndex(union_obj, un.tag.toValue()).?; + const field_ty = union_obj.field_types.get(ip)[field_index].toType(); + return func.lowerConstant(un.val.toValue(), field_ty); }, .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 { 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 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 layout = union_ty.unionGetLayout(mod); const union_obj = mod.typeToUnion(union_ty).?; - const field = union_obj.fields.values()[extra.field_index]; - const field_name = union_obj.fields.keys()[extra.field_index]; + const field_ty = union_obj.field_types.get(ip)[extra.field_index].toType(); + const field_name = union_obj.field_names.get(ip)[extra.field_index]; const tag_int = blk: { 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 payload = try func.resolveInst(extra.init); 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); - try func.store(payload_ptr, payload, field.ty, 0); + try func.store(payload_ptr, payload, field_ty, 0); } 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) { - 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 { - try func.store(result_ptr, payload, field.ty, 0); + try func.store(result_ptr, payload, field_ty, 0); if (layout.tag_size > 0) { try func.store( result_ptr, tag_int, - union_obj.tag_ty, - @as(u32, @intCast(layout.payload_size)), + union_obj.enum_tag_ty.toType(), + @intCast(layout.payload_size), ); } } @@ -5216,18 +5220,18 @@ fn airUnionInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { } else { const operand = try func.resolveInst(extra.init); const union_int_type = try mod.intType(.unsigned, @as(u16, @intCast(union_ty.bitSize(mod)))); - if (field.ty.zigTypeTag(mod) == .Float) { - const int_type = try mod.intType(.unsigned, @as(u16, @intCast(field.ty.bitSize(mod)))); - const bitcasted = try func.bitcast(field.ty, int_type, operand); + if (field_ty.zigTypeTag(mod) == .Float) { + const int_type = try mod.intType(.unsigned, @intCast(field_ty.bitSize(mod))); + const bitcasted = try func.bitcast(field_ty, int_type, operand); const casted = try func.trunc(bitcasted, int_type, union_int_type); - break :result try casted.toLocal(func, field.ty); - } else if (field.ty.isPtrAtRuntime(mod)) { - const int_type = try mod.intType(.unsigned, @as(u16, @intCast(field.ty.bitSize(mod)))); + break :result try casted.toLocal(func, field_ty); + } else if (field_ty.isPtrAtRuntime(mod)) { + const int_type = try mod.intType(.unsigned, @intCast(field_ty.bitSize(mod))); 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); - break :result try casted.toLocal(func, field.ty); + const casted = try func.intcast(operand, field_ty, union_int_type); + break :result try casted.toLocal(func, field_ty); } }; diff --git a/src/arch/wasm/abi.zig b/src/arch/wasm/abi.zig index 9fc1c0478f3d..6245f0ef5bfe 100644 --- a/src/arch/wasm/abi.zig +++ b/src/arch/wasm/abi.zig @@ -6,6 +6,7 @@ const std = @import("std"); const Target = std.Target; +const assert = std.debug.assert; const Type = @import("../../type.zig").Type; const Module = @import("../../Module.zig"); @@ -22,6 +23,7 @@ const direct: [2]Class = .{ .direct, .none }; /// or returned as value within a wasm function. /// When all elements result in `.none`, no value must be passed in or returned. pub fn classifyType(ty: Type, mod: *Module) [2]Class { + const ip = &mod.intern_pool; const target = mod.getTarget(); if (!ty.hasRuntimeBitsIgnoreComptime(mod)) return none; switch (ty.zigTypeTag(mod)) { @@ -56,22 +58,24 @@ pub fn classifyType(ty: Type, mod: *Module) [2]Class { .Bool => return direct, .Array => return memory, .Optional => { - std.debug.assert(ty.isPtrLikeOptional(mod)); + assert(ty.isPtrLikeOptional(mod)); return direct; }, .Pointer => { - std.debug.assert(!ty.isSlice(mod)); + assert(!ty.isSlice(mod)); return direct; }, .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; return .{ .direct, .direct }; } const layout = ty.unionGetLayout(mod); - std.debug.assert(layout.tag_size == 0); - if (ty.unionFields(mod).count() > 1) return memory; - return classifyType(ty.unionFields(mod).values()[0].ty, mod); + assert(layout.tag_size == 0); + if (union_obj.field_names.len > 1) return memory; + const first_field_ty = union_obj.field_types.get(ip)[0].toType(); + return classifyType(first_field_ty, mod); }, .ErrorUnion, .Frame, @@ -94,6 +98,7 @@ pub fn classifyType(ty: Type, mod: *Module) [2]Class { /// Asserts given type can be represented as scalar, such as /// a struct with a single scalar field. pub fn scalarType(ty: Type, mod: *Module) Type { + const ip = &mod.intern_pool; switch (ty.zigTypeTag(mod)) { .Struct => { switch (ty.containerLayout(mod)) { @@ -102,20 +107,22 @@ pub fn scalarType(ty: Type, mod: *Module) Type { return scalarType(struct_obj.backing_int_ty, mod); }, else => { - std.debug.assert(ty.structFieldCount(mod) == 1); + assert(ty.structFieldCount(mod) == 1); return scalarType(ty.structFieldType(0, mod), mod); }, } }, .Union => { - if (ty.containerLayout(mod) != .Packed) { - const layout = ty.unionGetLayout(mod); + const union_obj = mod.typeToUnion(ty).?; + if (union_obj.getLayout(ip) != .Packed) { + const layout = mod.getUnionLayout(union_obj); if (layout.payload_size == 0 and layout.tag_size != 0) { 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, } diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index b30f60228073..aec28b992c9c 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -11534,6 +11534,7 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { fn airUnionInit(self: *Self, inst: Air.Inst.Index) !void { const mod = self.bin_file.options.module.?; + const ip = &mod.intern_pool; const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data; 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 union_obj = mod.typeToUnion(union_ty).?; - const field_name = union_obj.fields.keys()[extra.field_index]; - const tag_ty = union_obj.tag_ty; + const field_name = union_obj.field_names.get(ip)[extra.field_index]; + const tag_ty = union_obj.enum_tag_ty.toType(); const field_index = tag_ty.enumFieldIndex(field_name, mod).?; const tag_val = try mod.enumValueFieldIndex(tag_ty, field_index); const tag_int_val = try tag_val.intFromEnum(tag_ty, mod); diff --git a/src/arch/x86_64/abi.zig b/src/arch/x86_64/abi.zig index f1ce3ebeb8b2..cf77a783eb06 100644 --- a/src/arch/x86_64/abi.zig +++ b/src/arch/x86_64/abi.zig @@ -69,6 +69,7 @@ pub const Context = enum { ret, arg, other }; /// There are a maximum of 8 possible return slots. Returned values are in /// the beginning of the array; unused slots are filled with .none. pub fn classifySystemV(ty: Type, mod: *Module, ctx: Context) [8]Class { + const ip = &mod.intern_pool; const target = mod.getTarget(); const memory_class = [_]Class{ .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" // "If the size of the aggregate exceeds a single eightbyte, each is classified // separately.". - const ty_size = ty.abiSize(mod); - if (ty.containerLayout(mod) == .Packed) { + const union_obj = mod.typeToUnion(ty).?; + const ty_size = mod.unionAbiSize(union_obj); + if (union_obj.getLayout(ip) == .Packed) { assert(ty_size <= 128); result[0] = .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) return memory_class; - const fields = ty.unionFields(mod); - for (fields.values()) |field| { - if (field.abi_align != .none) { - if (field.abi_align.toByteUnitsOptional().? < field.ty.abiAlignment(mod)) { + for (union_obj.field_types.get(ip), 0..) |field_ty, field_index| { + if (union_obj.fieldAlign(ip, @intCast(field_index)).toByteUnitsOptional()) |a| { + if (a < field_ty.toType().abiAlignment(mod)) { return memory_class; } } // 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| { const field_item = field_class[i]; // "If both classes are equal, this is the resulting class." diff --git a/src/codegen.zig b/src/codegen.zig index c1a956a76563..3a802f4d862c 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -185,8 +185,9 @@ pub fn generateSymbol( defer tracy.end(); const mod = bin_file.options.module.?; + const ip = &mod.intern_pool; 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(), else => {}, } @@ -205,7 +206,7 @@ pub fn generateSymbol( return .ok; } - switch (mod.intern_pool.indexToKey(typed_value.val.toIntern())) { + switch (ip.indexToKey(typed_value.val.toIntern())) { .int_type, .ptr_type, .array_type, @@ -385,7 +386,7 @@ pub fn generateSymbol( 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) { .bytes => |bytes| try code.appendSlice(bytes), .elems, .repeated_elem => { @@ -442,7 +443,7 @@ pub fn generateSymbol( if (!field_ty.toType().hasRuntimeBits(mod)) continue; 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, .storage = .{ .u64 = bytes[index] }, } }), @@ -484,7 +485,7 @@ pub fn generateSymbol( const field_ty = field.ty; 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(), .storage = .{ .u64 = bytes[index] }, } }), @@ -522,8 +523,8 @@ pub fn generateSymbol( if (!field_ty.hasRuntimeBits(mod)) continue; - const field_val = switch (mod.intern_pool.indexToKey(typed_value.val.toIntern()).aggregate.storage) { - .bytes => |bytes| try mod.intern_pool.get(mod.gpa, .{ .int = .{ + const field_val = switch (ip.indexToKey(typed_value.val.toIntern()).aggregate.storage) { + .bytes => |bytes| try ip.get(mod.gpa, .{ .int = .{ .ty = field_ty.toIntern(), .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).?; - assert(union_ty.haveFieldTypes()); - const field_ty = union_ty.fields.values()[field_index].ty; + const field_ty = union_obj.field_types.get(ip)[field_index].toType(); if (!field_ty.hasRuntimeBits(mod)) { try code.appendNTimes(0xaa, math.cast(usize, layout.payload_size) orelse return error.Overflow); } else { @@ -593,7 +593,7 @@ pub fn generateSymbol( if (layout.tag_size > 0 and layout.tag_align < layout.payload_align) { switch (try generateSymbol(bin_file, src_loc, .{ - .ty = union_ty.tag_ty, + .ty = union_obj.enum_tag_ty.toType(), .val = un.tag.toValue(), }, code, debug_output, reloc_info)) { .ok => {}, diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 330a9b33e89b..02f59df8d6b6 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -708,8 +708,10 @@ pub const DeclGen = struct { location: ValueRenderLocation, ) error{ OutOfMemory, AnalysisFail }!void { const mod = dg.module; + const ip = &mod.intern_pool; + 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(), else => {}, } @@ -836,9 +838,10 @@ pub const DeclGen = struct { if (layout.tag_size != 0) try writer.writeByte(','); try writer.writeAll(" .payload = {"); } - for (ty.unionFields(mod).values()) |field| { - if (!field.ty.hasRuntimeBits(mod)) continue; - try dg.renderValue(writer, field.ty, val, initializer_type); + const union_obj = mod.typeToUnion(ty).?; + for (union_obj.field_types.get(ip)) |field_ty| { + if (!field_ty.toType().hasRuntimeBits(mod)) continue; + try dg.renderValue(writer, field_ty.toType(), val, initializer_type); break; } if (ty.unionTagTypeSafety(mod)) |_| try writer.writeByte('}'); @@ -912,7 +915,7 @@ pub const DeclGen = struct { unreachable; } - switch (mod.intern_pool.indexToKey(val.ip_index)) { + switch (ip.indexToKey(val.ip_index)) { // types, not values .int_type, .ptr_type, @@ -962,7 +965,7 @@ pub const DeclGen = struct { }, }, .err => |err| try writer.print("zig_error_{}", .{ - fmtIdent(mod.intern_pool.stringToSlice(err.name)), + fmtIdent(ip.stringToSlice(err.name)), }), .error_union => |error_union| { const payload_ty = ty.errorUnionPayload(mod); @@ -1024,8 +1027,8 @@ pub const DeclGen = struct { try writer.writeAll(" }"); }, .enum_tag => { - const enum_tag = mod.intern_pool.indexToKey(val.ip_index).enum_tag; - const int_tag_ty = mod.intern_pool.typeOf(enum_tag.int); + const enum_tag = ip.indexToKey(val.ip_index).enum_tag; + const int_tag_ty = ip.typeOf(enum_tag.int); try dg.renderValue(writer, int_tag_ty.toType(), enum_tag.int.toValue(), location); }, .float => { @@ -1205,7 +1208,7 @@ pub const DeclGen = struct { try dg.renderValue(writer, Type.bool, is_null_val, initializer_type); try writer.writeAll(" }"); }, - .aggregate => switch (mod.intern_pool.indexToKey(ty.ip_index)) { + .aggregate => switch (ip.indexToKey(ty.ip_index)) { .array_type, .vector_type => { if (location == .FunctionArgument) { try writer.writeByte('('); @@ -1278,8 +1281,8 @@ pub const DeclGen = struct { if (!empty) try writer.writeByte(','); - const field_val = switch (mod.intern_pool.indexToKey(val.ip_index).aggregate.storage) { - .bytes => |bytes| try mod.intern_pool.get(mod.gpa, .{ .int = .{ + const field_val = switch (ip.indexToKey(val.ip_index).aggregate.storage) { + .bytes => |bytes| try ip.get(mod.gpa, .{ .int = .{ .ty = field_ty, .storage = .{ .u64 = bytes[field_i] }, } }), @@ -1309,8 +1312,8 @@ pub const DeclGen = struct { if (!field.ty.hasRuntimeBitsIgnoreComptime(mod)) continue; if (!empty) try writer.writeByte(','); - const field_val = switch (mod.intern_pool.indexToKey(val.ip_index).aggregate.storage) { - .bytes => |bytes| try mod.intern_pool.get(mod.gpa, .{ .int = .{ + const field_val = switch (ip.indexToKey(val.ip_index).aggregate.storage) { + .bytes => |bytes| try ip.get(mod.gpa, .{ .int = .{ .ty = field.ty.toIntern(), .storage = .{ .u64 = bytes[field_i] }, } }), @@ -1358,8 +1361,8 @@ pub const DeclGen = struct { if (field.is_comptime) continue; if (!field.ty.hasRuntimeBitsIgnoreComptime(mod)) continue; - const field_val = switch (mod.intern_pool.indexToKey(val.ip_index).aggregate.storage) { - .bytes => |bytes| try mod.intern_pool.get(mod.gpa, .{ .int = .{ + const field_val = switch (ip.indexToKey(val.ip_index).aggregate.storage) { + .bytes => |bytes| try ip.get(mod.gpa, .{ .int = .{ .ty = field.ty.toIntern(), .storage = .{ .u64 = bytes[field_i] }, } }), @@ -1400,8 +1403,8 @@ pub const DeclGen = struct { try dg.renderType(writer, ty); try writer.writeByte(')'); - const field_val = switch (mod.intern_pool.indexToKey(val.ip_index).aggregate.storage) { - .bytes => |bytes| try mod.intern_pool.get(mod.gpa, .{ .int = .{ + const field_val = switch (ip.indexToKey(val.ip_index).aggregate.storage) { + .bytes => |bytes| try ip.get(mod.gpa, .{ .int = .{ .ty = field.ty.toIntern(), .storage = .{ .u64 = bytes[field_i] }, } }), @@ -1435,10 +1438,11 @@ pub const DeclGen = struct { try writer.writeByte(')'); } - const field_i = ty.unionTagFieldIndex(un.tag.toValue(), mod).?; - const field_ty = ty.unionFields(mod).values()[field_i].ty; - const field_name = ty.unionFields(mod).keys()[field_i]; - if (ty.containerLayout(mod) == .Packed) { + const union_obj = mod.typeToUnion(ty).?; + const field_i = mod.unionTagFieldIndex(union_obj, un.tag.toValue()).?; + const field_ty = union_obj.field_types.get(ip)[field_i].toType(); + 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.isPtrAtRuntime(mod)) { try writer.writeByte('('); @@ -1458,7 +1462,7 @@ pub const DeclGen = struct { try writer.writeByte('{'); if (ty.unionTagTypeSafety(mod)) |tag_ty| { - const layout = ty.unionGetLayout(mod); + const layout = mod.getUnionLayout(union_obj); if (layout.tag_size != 0) { try writer.writeAll(" .tag = "); try dg.renderValue(writer, tag_ty, un.tag.toValue(), initializer_type); @@ -1468,12 +1472,12 @@ pub const DeclGen = struct { try writer.writeAll(" .payload = {"); } 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 writer.writeByte(' '); - } else for (ty.unionFields(mod).values()) |field| { - if (!field.ty.hasRuntimeBits(mod)) continue; - try dg.renderValue(writer, field.ty, Value.undef, initializer_type); + } else for (union_obj.field_types.get(ip)) |this_field_ty| { + if (!this_field_ty.toType().hasRuntimeBits(mod)) continue; + try dg.renderValue(writer, this_field_ty.toType(), Value.undef, initializer_type); break; } if (ty.unionTagTypeSafety(mod)) |_| try writer.writeByte('}'); @@ -5237,22 +5241,25 @@ fn fieldLocation( else .begin, }, - .Union => switch (container_ty.containerLayout(mod)) { - .Auto, .Extern => { - const field_ty = container_ty.structFieldType(field_index, mod); - if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) - return if (container_ty.unionTagTypeSafety(mod) != null and - !container_ty.unionHasAllZeroBitFieldTypes(mod)) - .{ .field = .{ .identifier = "payload" } } + .Union => { + const union_obj = mod.typeToUnion(container_ty).?; + return switch (union_obj.getLayout(ip)) { + .Auto, .Extern => { + const field_ty = union_obj.field_types.get(ip)[field_index].toType(); + if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) + 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 - .begin; - const field_name = container_ty.unionFields(mod).keys()[field_index]; - return .{ .field = if (container_ty.unionTagTypeSafety(mod)) |_| - .{ .payload_identifier = ip.stringToSlice(field_name) } - else - .{ .identifier = ip.stringToSlice(field_name) } }; - }, - .Packed => .begin, + .{ .identifier = ip.stringToSlice(field_name) } }; + }, + .Packed => .begin, + }; }, .Pointer => switch (container_ty.ptrSize(mod)) { .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)) }, .union_type => |union_type| field_name: { - const union_obj = mod.unionPtr(union_type.index); - if (union_obj.layout == .Packed) { + const union_obj = ip.loadUnionType(union_type); + if (union_obj.flagsPtr(ip).layout == .Packed) { const operand_lval = if (struct_byval == .constant) blk: { const operand_local = try f.allocLocal(inst, struct_ty); try f.writeCValue(writer, operand_local, .Other); @@ -5505,8 +5512,8 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { return local; } else { - const name = union_obj.fields.keys()[extra.field_index]; - break :field_name if (union_type.hasTag()) .{ + const name = union_obj.field_names.get(ip)[extra.field_index]; + break :field_name if (union_type.hasTag(ip)) .{ .payload_identifier = ip.stringToSlice(name), } else .{ .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_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 = try f.resolveInst(extra.init); try reap(f, inst, &.{extra.init}); const writer = f.object.writer(); 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 writer.writeAll(" = "); try f.writeCValue(writer, payload, .Initializer); diff --git a/src/codegen/c/type.zig b/src/codegen/c/type.zig index 01540a9f2df9..06d80b7c8255 100644 --- a/src/codegen/c/type.zig +++ b/src/codegen/c/type.zig @@ -303,7 +303,7 @@ pub const CType = extern union { } pub fn unionPayloadAlign(union_ty: Type, mod: *Module) AlignAs { 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); } @@ -1499,7 +1499,7 @@ pub const CType = extern union { if (lookup.isMutable()) { for (0..switch (zig_ty_tag) { .Struct => ty.structFieldCount(mod), - .Union => ty.unionFields(mod).count(), + .Union => mod.typeToUnion(ty).?.field_names.len, else => unreachable, }) |field_i| { const field_ty = ty.structFieldType(field_i, mod); @@ -1581,7 +1581,7 @@ pub const CType = extern union { var is_packed = false; for (0..switch (zig_ty_tag) { .Struct => ty.structFieldCount(mod), - .Union => ty.unionFields(mod).count(), + .Union => mod.typeToUnion(ty).?.field_names.len, else => unreachable, }) |field_i| { const field_ty = ty.structFieldType(field_i, mod); @@ -1912,6 +1912,7 @@ pub const CType = extern union { kind: Kind, convert: Convert, ) !CType { + const ip = &mod.intern_pool; const arena = store.arena.allocator(); switch (convert.value) { .cty => |c| return c.copy(arena), @@ -1932,7 +1933,7 @@ pub const CType = extern union { const zig_ty_tag = ty.zigTypeTag(mod); const fields_len = switch (zig_ty_tag) { .Struct => ty.structFieldCount(mod), - .Union => ty.unionFields(mod).count(), + .Union => mod.typeToUnion(ty).?.field_names.len, else => unreachable, }; @@ -1956,9 +1957,9 @@ pub const CType = extern union { .name = try if (ty.isSimpleTuple(mod)) std.fmt.allocPrintZ(arena, "f{}", .{field_i}) 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), - .Union => ty.unionFields(mod).keys()[field_i], + .Union => mod.typeToUnion(ty).?.field_names.get(ip)[field_i], else => unreachable, })), .type = store.set.typeToIndex(field_ty, mod, switch (kind) { @@ -2015,7 +2016,6 @@ pub const CType = extern union { .function, .varargs_function, => { - const ip = &mod.intern_pool; const info = mod.typeToFunc(ty).?; assert(!info.is_generic); 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 { const mod = self.lookup.getModule(); + const ip = &mod.intern_pool; switch (self.convert.value) { .cty => |c| return c.eql(cty), .tag => |t| { @@ -2088,7 +2089,7 @@ pub const CType = extern union { var c_field_i: usize = 0; for (0..switch (zig_ty_tag) { .Struct => ty.structFieldCount(mod), - .Union => ty.unionFields(mod).count(), + .Union => mod.typeToUnion(ty).?.field_names.len, else => unreachable, }) |field_i| { const field_ty = ty.structFieldType(field_i, mod); @@ -2108,9 +2109,9 @@ pub const CType = extern union { if (ty.isSimpleTuple(mod)) std.fmt.bufPrintZ(&name_buf, "f{}", .{field_i}) catch unreachable else - mod.intern_pool.stringToSlice(switch (zig_ty_tag) { + ip.stringToSlice(switch (zig_ty_tag) { .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, }), mem.span(c_field.name), @@ -2149,7 +2150,6 @@ pub const CType = extern union { => { if (ty.zigTypeTag(mod) != .Fn) return false; - const ip = &mod.intern_pool; const info = mod.typeToFunc(ty).?; assert(!info.is_generic); const data = cty.cast(Payload.Function).?.data; @@ -2217,7 +2217,7 @@ pub const CType = extern union { const zig_ty_tag = ty.zigTypeTag(mod); for (0..switch (ty.zigTypeTag(mod)) { .Struct => ty.structFieldCount(mod), - .Union => ty.unionFields(mod).count(), + .Union => mod.typeToUnion(ty).?.field_names.len, else => unreachable, }) |field_i| { const field_ty = ty.structFieldType(field_i, mod); @@ -2235,7 +2235,7 @@ pub const CType = extern union { else mod.intern_pool.stringToSlice(switch (zig_ty_tag) { .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, })); autoHash(hasher, AlignAs.fieldAlign(ty, field_i, mod).@"align"); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index e36f411a01a6..bc1161475f55 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -2382,7 +2382,7 @@ pub const Object = struct { break :blk fwd_decl; }; - switch (mod.intern_pool.indexToKey(ty.toIntern())) { + switch (ip.indexToKey(ty.toIntern())) { .anon_struct_type => |tuple| { var di_fields: std.ArrayListUnmanaged(*llvm.DIType) = .{}; defer di_fields.deinit(gpa); @@ -2401,7 +2401,7 @@ pub const Object = struct { offset = field_offset + field_size; const field_name = if (tuple.names.len != 0) - mod.intern_pool.stringToSlice(tuple.names[i]) + ip.stringToSlice(tuple.names[i]) else try std.fmt.allocPrintZ(gpa, "{d}", .{i}); 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); 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( fwd_decl.toScope(), @@ -2546,8 +2546,8 @@ pub const Object = struct { break :blk fwd_decl; }; - const union_obj = mod.typeToUnion(ty).?; - if (!union_obj.haveFieldTypes() or !ty.hasRuntimeBitsIgnoreComptime(mod)) { + const union_type = ip.indexToKey(ty.toIntern()).union_type; + if (!union_type.haveFieldTypes(ip) or !ty.hasRuntimeBitsIgnoreComptime(mod)) { const union_di_ty = try o.makeEmptyNamespaceDIType(owner_decl_index); dib.replaceTemporary(fwd_decl, union_di_ty); // The recursive call to `lowerDebugType` via `makeEmptyNamespaceDIType` @@ -2556,10 +2556,11 @@ pub const Object = struct { 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) { - 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 full_di_ty = dib.createStructType( compile_unit_scope, @@ -2586,22 +2587,20 @@ pub const Object = struct { var di_fields: std.ArrayListUnmanaged(*llvm.DIType) = .{}; 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(); - while (it.next()) |kv| { - const field_name = kv.key_ptr.*; - const field = kv.value_ptr.*; + for (0..union_obj.field_names.len) |field_index| { + const field_ty = union_obj.field_types.get(ip)[field_index]; + if (!field_ty.toType().hasRuntimeBitsIgnoreComptime(mod)) continue; - if (!field.ty.hasRuntimeBitsIgnoreComptime(mod)) continue; - - const field_size = field.ty.abiSize(mod); - const field_align = field.normalAlignment(mod); + const field_size = field_ty.toType().abiSize(mod); + const field_align = mod.unionFieldNormalAlignment(union_obj, @intCast(field_index)); - const field_di_ty = try o.lowerDebugType(field.ty, .full); + const field_di_ty = try o.lowerDebugType(field_ty.toType(), .full); + const field_name = union_obj.field_names.get(ip)[field_index]; di_fields.appendAssumeCapacity(dib.createMemberType( fwd_decl.toScope(), - mod.intern_pool.stringToSlice(field_name), + ip.stringToSlice(field_name), null, // file 0, // line field_size * 8, // size in bits @@ -2659,7 +2658,7 @@ pub const Object = struct { layout.tag_align * 8, // align in bits tag_offset * 8, // offset in bits 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( @@ -3078,6 +3077,7 @@ pub const Object = struct { fn lowerTypeInner(o: *Object, t: Type) Allocator.Error!Builder.Type { const mod = o.module; const target = mod.getTarget(); + const ip = &mod.intern_pool; return switch (t.toIntern()) { .u0_type, .i0_type => unreachable, inline .u1_type, @@ -3172,7 +3172,7 @@ pub const Object = struct { .var_args_param_type, .none, => 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), .ptr_type => |ptr_type| type: { const ptr_ty = try o.builder.ptrType( @@ -3264,7 +3264,7 @@ pub const Object = struct { 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), )); 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()); if (gop.found_existing) return gop.value_ptr.*; - const union_obj = mod.unionPtr(union_type.index); - const layout = union_obj.getLayout(mod, union_type.hasTag()); + const union_obj = ip.loadUnionType(union_type); + 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))); gop.value_ptr.* = int_ty; return int_ty; } 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; return enum_tag_ty; } - const name = try o.builder.string(mod.intern_pool.stringToSlice( - try union_obj.getFullyQualifiedName(mod), + const name = try o.builder.string(ip.stringToSlice( + try mod.declPtr(union_obj.decl).getFullyQualifiedName(mod), )); const ty = try o.builder.opaqueType(name); 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 = try o.lowerType(aligned_field.ty); + const aligned_field_ty = union_obj.field_types.get(ip)[layout.most_aligned_field].toType(); + const aligned_field_llvm_ty = try o.lowerType(aligned_field_ty); const payload_ty = ty: { 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) layout.abi_size - layout.most_aligned_field_size else layout.payload_size - layout.most_aligned_field_size; break :ty try o.builder.structType(.@"packed", &.{ - aligned_field_ty, + aligned_field_llvm_ty, try o.builder.arrayType(padding_len, .i8), }); }; @@ -3402,7 +3402,7 @@ pub const Object = struct { ); 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 // alignment is greater. @@ -3430,7 +3430,7 @@ pub const Object = struct { .opaque_type => |opaque_type| { const gop = try o.type_map.getOrPut(o.gpa, t.toIntern()); 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), )); 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 { const mod = o.module; + const ip = &mod.intern_pool; const target = mod.getTarget(); 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) { .runtime_value => |rt| val = rt.val.toValue(), else => {}, @@ -3563,7 +3564,7 @@ pub const Object = struct { 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(); return switch (val_key) { .int_type, @@ -3749,7 +3750,7 @@ pub const Object = struct { fields[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) { .bytes => |bytes| try o.builder.stringConst(try o.builder.string(bytes)), .elems => |elems| { @@ -4024,11 +4025,10 @@ pub const Object = struct { if (layout.payload_size == 0) return o.lowerValue(un.tag); const union_obj = mod.typeToUnion(ty).?; - const field_index = ty.unionTagFieldIndex(un.tag.toValue(), o.module).?; - assert(union_obj.haveFieldTypes()); + const field_index = mod.unionTagFieldIndex(union_obj, un.tag.toValue()).?; - const field_ty = union_obj.fields.values()[field_index].ty; - if (union_obj.layout == .Packed) { + const field_ty = union_obj.field_types.get(ip)[field_index].toType(); + if (union_obj.getLayout(ip) == .Packed) { if (!field_ty.hasRuntimeBits(mod)) return o.builder.intConst(union_ty, 0); const small_int_val = try o.builder.castConst( 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 { const o = self.dg.object; const mod = o.module; + const ip = &mod.intern_pool; const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data; const union_ty = self.typeOfIndex(inst); @@ -9683,13 +9684,13 @@ pub const FuncGen = struct { const layout = union_ty.unionGetLayout(mod); 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 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 small_int_ty = try o.builder.intType(@intCast(field.ty.bitSize(mod))); - const small_int_val = if (field.ty.isPtrAtRuntime(mod)) + const small_int_ty = try o.builder.intType(@intCast(field_ty.bitSize(mod))); + const small_int_val = if (field_ty.isPtrAtRuntime(mod)) try self.wip.cast(.ptrtoint, non_int_val, small_int_ty, "") else 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_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 tag_val = try mod.enumValueFieldIndex(tag_ty, enum_field_index); 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 result_ptr = try self.buildAlloca(union_llvm_ty, alignment); const llvm_payload = try self.resolveInst(extra.init); - assert(union_obj.haveFieldTypes()); - const field = union_obj.fields.values()[extra.field_index]; - const field_llvm_ty = try o.lowerType(field.ty); - const field_size = field.ty.abiSize(mod); - const field_align = field.normalAlignment(mod); + const field_ty = union_obj.field_types.get(ip)[extra.field_index].toType(); + const field_llvm_ty = try o.lowerType(field_ty); + const field_size = field_ty.abiSize(mod); + const field_align = mod.unionFieldNormalAlignment(union_obj, extra.field_index); const llvm_usize = try o.lowerType(Type.usize); const usize_zero = try o.builder.intValue(llvm_usize, 0); const i32_zero = try o.builder.intValue(.i32, 0); const llvm_union_ty = t: { const payload_ty = p: { - if (!field.ty.hasRuntimeBitsIgnoreComptime(mod)) { + if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) { const padding_len = layout.payload_size; 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}); - 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_len: usize = 2; 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 // tag and the payload. const field_ptr_ty = try mod.ptrType(.{ - .child = field.ty.toIntern(), + .child = field_ty.toIntern(), .flags = .{ .alignment = InternPool.Alignment.fromNonzeroByteUnits(field_align) }, }); if (layout.tag_size == 0) { @@ -9786,9 +9786,9 @@ pub const FuncGen = struct { 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 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 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); } diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index 9f84781966e8..b44b09307b8b 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -619,9 +619,10 @@ pub const DeclGen = struct { fn lower(self: *@This(), ty: Type, arg_val: Value) !void { const dg = self.dg; const mod = dg.module; + const ip = &mod.intern_pool; var val = arg_val; - switch (mod.intern_pool.indexToKey(val.toIntern())) { + switch (ip.indexToKey(val.toIntern())) { .runtime_value => |rt| val = rt.val.toValue(), else => {}, } @@ -631,7 +632,7 @@ pub const DeclGen = struct { return try self.addUndef(size); } - switch (mod.intern_pool.indexToKey(val.toIntern())) { + switch (ip.indexToKey(val.toIntern())) { .int_type, .ptr_type, .array_type, @@ -770,7 +771,7 @@ pub const DeclGen = struct { try self.addConstBool(payload_val != null); 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| { const elem_ty = array_type.child.toType(); switch (aggregate.storage) { @@ -801,7 +802,7 @@ pub const DeclGen = struct { if (field.is_comptime or !field.ty.hasRuntimeBits(mod)) continue; 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(), .storage = .{ .u64 = bytes[i] }, } }), @@ -828,13 +829,13 @@ pub const DeclGen = struct { return try self.lower(ty.unionTagTypeSafety(mod).?, un.tag.toValue()); } - const union_ty = mod.typeToUnion(ty).?; - if (union_ty.layout == .Packed) { + const union_obj = mod.typeToUnion(ty).?; + if (union_obj.getLayout(ip) == .Packed) { return dg.todo("packed union constants", .{}); } 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 tag_first = layout.tag_align >= layout.payload_align; @@ -1162,16 +1163,17 @@ pub const DeclGen = struct { /// resulting struct will be *underaligned*. fn resolveUnionType(self: *DeclGen, ty: Type, maybe_active_field: ?usize) !CacheRef { const mod = self.module; + const ip = &mod.intern_pool; 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", .{}); } if (layout.payload_size == 0) { // 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){}; @@ -1182,13 +1184,13 @@ pub const DeclGen = struct { const u8_ty_ref = try self.intType(.unsigned, 8); // TODO: What if Int8Type is not enabled? 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_names.appendAssumeCapacity(try self.spv.resolveString("tag")); } 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_payload_ty_ref = try self.resolveType(active_field_ty, .indirect); @@ -1205,7 +1207,7 @@ pub const DeclGen = struct { } 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_names.appendAssumeCapacity(try self.spv.resolveString("tag")); } diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index b2ca85467c37..43eab54b5488 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -166,6 +166,7 @@ pub const DeclState = struct { const dbg_info_buffer = &self.dbg_info; const target = mod.getTarget(); const target_endian = target.cpu.arch.endian(); + const ip = &mod.intern_pool; switch (ty.zigTypeTag(mod)) { .NoReturn => unreachable, @@ -321,7 +322,7 @@ pub const DeclState = struct { // DW.AT.byte_size, DW.FORM.udata 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| { // DW.AT.name, DW.FORM.string try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(mod)}); @@ -357,7 +358,7 @@ pub const DeclState = struct { 0.., ) |field_name_ip, field, field_index| { 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 try dbg_info_buffer.ensureUnusedCapacity(field_name.len + 2); 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 dbg_info_buffer.append(0); - const ip = &mod.intern_pool; const enum_type = ip.indexToKey(ty.ip_index).enum_type; for (enum_type.names.get(ip), 0..) |field_name_index, field_i| { const field_name = ip.stringToSlice(field_name_index); @@ -414,8 +414,8 @@ pub const DeclState = struct { try dbg_info_buffer.append(0); }, .Union => { - const layout = ty.unionGetLayout(mod); 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 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 @@ -457,19 +457,17 @@ pub const DeclState = struct { try dbg_info_buffer.append(0); } - const fields = ty.unionFields(mod); - for (fields.keys()) |field_name| { - const field = fields.get(field_name).?; - if (!field.ty.hasRuntimeBits(mod)) continue; + for (union_obj.field_types.get(ip), union_obj.field_names.get(ip)) |field_ty, field_name| { + if (!field_ty.toType().hasRuntimeBits(mod)) continue; // DW.AT.member try dbg_info_buffer.append(@intFromEnum(AbbrevKind.struct_member)); // 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); // DW.AT.type, DW.FORM.ref4 const index = dbg_info_buffer.items.len; 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 try dbg_info_buffer.append(0); } @@ -486,7 +484,7 @@ pub const DeclState = struct { // DW.AT.type, DW.FORM.ref4 const index = dbg_info_buffer.items.len; 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 try leb128.writeULEB128(dbg_info_buffer.writer(), tag_offset); diff --git a/src/type.zig b/src/type.zig index b82d3723d0aa..8d4d9edf8638 100644 --- a/src/type.zig +++ b/src/type.zig @@ -349,8 +349,7 @@ pub const Type = struct { }, .union_type => |union_type| { - const union_obj = mod.unionPtr(union_type.index); - const decl = mod.declPtr(union_obj.owner_decl); + const decl = mod.declPtr(union_type.decl); try decl.renderFullyQualifiedName(mod, writer); }, .opaque_type => |opaque_type| { @@ -462,10 +461,11 @@ pub const Type = struct { ignore_comptime_only: bool, strat: AbiAlignmentAdvancedStrat, ) RuntimeBitsError!bool { + const ip = &mod.intern_pool; return switch (ty.toIntern()) { // False because it is a comptime-only type. .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, .ptr_type => |ptr_type| { // Pointers to zero-bit types still have a runtime address; however, pointers @@ -595,29 +595,36 @@ pub const Type = struct { }, .union_type => |union_type| { - const union_obj = mod.unionPtr(union_type.index); - switch (union_type.runtime_tag) { + switch (union_type.flagsPtr(ip).runtime_tag) { .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, // 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; } }, .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; } }, } switch (strat) { .sema => |sema| _ = try sema.resolveTypeFields(ty), - .eager => assert(union_obj.haveFieldTypes()), - .lazy => if (!union_obj.haveFieldTypes()) return error.NeedLazy, + .eager => assert(union_type.flagsPtr(ip).status.haveFieldTypes()), + .lazy => if (!union_type.flagsPtr(ip).status.haveFieldTypes()) + return error.NeedLazy, } - for (union_obj.fields.values()) |value| { - if (try value.ty.hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat)) + const union_obj = ip.loadUnionType(union_type); + 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; } else { return false; @@ -656,7 +663,8 @@ pub const Type = struct { /// readFrom/writeToMemory are supported only for types with a well- /// defined memory layout 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, .vector_type, => true, @@ -728,8 +736,8 @@ pub const Type = struct { }; return struct_obj.layout != .Auto; }, - .union_type => |union_type| switch (union_type.runtime_tag) { - .none, .safety => mod.unionPtr(union_type.index).layout != .Auto, + .union_type => |union_type| switch (union_type.flagsPtr(ip).runtime_tag) { + .none, .safety => union_type.flagsPtr(ip).layout != .Auto, .tagged => false, }, .enum_type => |enum_type| switch (enum_type.tag_mode) { @@ -867,6 +875,7 @@ pub const Type = struct { strat: AbiAlignmentAdvancedStrat, ) Module.CompileError!AbiAlignmentAdvanced { const target = mod.getTarget(); + const ip = &mod.intern_pool; const opt_sema = switch (strat) { .sema => |sema| sema, @@ -875,7 +884,7 @@ pub const Type = struct { switch (ty.toIntern()) { .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| { if (int_type.bits == 0) return AbiAlignmentAdvanced{ .scalar = 0 }; return AbiAlignmentAdvanced{ .scalar = intAbiAlignment(int_type.bits, target) }; @@ -1066,8 +1075,65 @@ pub const Type = struct { }, .union_type => |union_type| { - const union_obj = mod.unionPtr(union_type.index); - return abiAlignmentAdvancedUnion(ty, mod, strat, union_obj, union_type.hasTag()); + if (opt_sema) |sema| { + 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 }, .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`. pub fn lazyAbiSize(ty: Type, mod: *Module) !Value { switch (try ty.abiSizeAdvanced(mod, .lazy)) { @@ -1273,11 +1274,12 @@ pub const Type = struct { strat: AbiAlignmentAdvancedStrat, ) Module.CompileError!AbiSizeAdvanced { const target = mod.getTarget(); + const ip = &mod.intern_pool; switch (ty.toIntern()) { .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| { if (int_type.bits == 0) return AbiSizeAdvanced{ .scalar = 0 }; return AbiSizeAdvanced{ .scalar = intAbiSize(int_type.bits, target) }; @@ -1484,8 +1486,18 @@ pub const Type = struct { }, .union_type => |union_type| { - const union_obj = mod.unionPtr(union_type.index); - return abiSizeAdvancedUnion(ty, mod, strat, union_obj, union_type.hasTag()); + switch (strat) { + .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 .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( ty: Type, mod: *Module, @@ -1602,10 +1596,11 @@ pub const Type = struct { opt_sema: ?*Sema, ) Module.CompileError!u64 { const target = mod.getTarget(); + const ip = &mod.intern_pool; 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, .ptr_type => |ptr_type| switch (ptr_type.flags.size) { .Slice => return target.ptrBitWidth() * 2, @@ -1714,12 +1709,13 @@ pub const Type = struct { if (ty.containerLayout(mod) != .Packed) { return (try ty.abiSizeAdvanced(mod, strat)).scalar * 8; } - const union_obj = mod.unionPtr(union_type.index); - assert(union_obj.haveFieldTypes()); + const union_obj = ip.loadUnionType(union_type); + assert(union_obj.flagsPtr(ip).status.haveFieldTypes()); var size: u64 = 0; - for (union_obj.fields.values()) |field| { - size = @max(size, try bitSizeAdvanced(field.ty, mod, opt_sema)); + for (0..union_obj.field_types.len) |field_index| { + const field_ty = union_obj.field_types.get(ip)[field_index]; + size = @max(size, try bitSizeAdvanced(field_ty.toType(), mod, opt_sema)); } return size; }, @@ -1753,33 +1749,24 @@ pub const Type = struct { /// Returns true if the type's layout is already resolved and it is safe /// to use `abiSize`, `abiAlignment` and `bitSize` on it. pub fn layoutIsResolved(ty: Type, mod: *Module) bool { - switch (ty.zigTypeTag(mod)) { - .Struct => { - if (mod.typeToStruct(ty)) |struct_obj| { + const ip = &mod.intern_pool; + return switch (ip.indexToKey(ty.toIntern())) { + .struct_type => |struct_type| { + if (mod.structPtrUnwrap(struct_type.index)) |struct_obj| { return struct_obj.haveLayout(); + } else { + return true; } - return true; - }, - .Union => { - if (mod.typeToUnion(ty)) |union_obj| { - return union_obj.haveLayout(); - } - return true; }, - .Array => { - if (ty.arrayLenIncludingSentinel(mod) == 0) return true; - return ty.childType(mod).layoutIsResolved(mod); - }, - .Optional => { - const payload_ty = ty.optionalChild(mod); - return payload_ty.layoutIsResolved(mod); - }, - .ErrorUnion => { - const payload_ty = ty.errorUnionPayload(mod); - return payload_ty.layoutIsResolved(mod); + .union_type => |union_type| union_type.haveLayout(ip), + .array_type => |array_type| { + if ((array_type.len + @intFromBool(array_type.sentinel != .none)) == 0) return true; + return array_type.child.toType().layoutIsResolved(mod); }, - else => return true, - } + .opt_type => |child| child.toType().layoutIsResolved(mod), + .error_union_type => |k| k.payload_type.toType().layoutIsResolved(mod), + else => true, + }; } 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. /// Otherwise, returns `null`. pub fn unionTagType(ty: Type, mod: *Module) ?Type { - return switch (mod.intern_pool.indexToKey(ty.toIntern())) { - .union_type => |union_type| switch (union_type.runtime_tag) { + const ip = &mod.intern_pool; + return switch (ip.indexToKey(ty.toIntern())) { + .union_type => |union_type| switch (union_type.flagsPtr(ip).runtime_tag) { .tagged => { - const union_obj = mod.unionPtr(union_type.index); - assert(union_obj.haveFieldTypes()); - return union_obj.tag_ty; + assert(union_type.flagsPtr(ip).status.haveFieldTypes()); + return union_type.enum_tag_ty.toType(); }, else => null, }, @@ -1986,12 +1973,12 @@ pub const Type = struct { /// Same as `unionTagType` but includes safety tag. /// Codegen should use this version. 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| { - if (!union_type.hasTag()) return null; - const union_obj = mod.unionPtr(union_type.index); - assert(union_obj.haveFieldTypes()); - return union_obj.tag_ty; + if (!union_type.hasTag(ip)) return null; + assert(union_type.haveFieldTypes(ip)); + return union_type.enum_tag_ty.toType(); }, else => null, }; @@ -2001,52 +1988,46 @@ pub const Type = struct { /// not be stored at runtime. pub fn unionTagTypeHypothetical(ty: Type, mod: *Module) Type { const union_obj = mod.typeToUnion(ty).?; - assert(union_obj.haveFieldTypes()); - 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; + return union_obj.enum_tag_ty.toType(); } pub fn unionFieldType(ty: Type, enum_tag: Value, mod: *Module) Type { + const ip = &mod.intern_pool; const union_obj = mod.typeToUnion(ty).?; - const index = ty.unionTagFieldIndex(enum_tag, mod).?; - assert(union_obj.haveFieldTypes()); - return union_obj.fields.values()[index].ty; + const index = mod.unionTagFieldIndex(union_obj, enum_tag).?; + return union_obj.field_types.get(ip)[index].toType(); } - 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 index = union_obj.tag_ty.enumTagFieldIndex(enum_tag, mod) orelse return null; - const name = union_obj.tag_ty.enumFieldName(index, mod); - return union_obj.fields.getIndex(name); + return mod.unionTagFieldIndex(union_obj, enum_tag); } pub fn unionHasAllZeroBitFieldTypes(ty: Type, mod: *Module) bool { + const ip = &mod.intern_pool; 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 { - const union_type = mod.intern_pool.indexToKey(ty.toIntern()).union_type; - const union_obj = mod.unionPtr(union_type.index); - return union_obj.getLayout(mod, union_type.hasTag()); + pub fn unionGetLayout(ty: Type, mod: *Module) Module.UnionLayout { + const ip = &mod.intern_pool; + const union_type = ip.indexToKey(ty.toIntern()).union_type; + const union_obj = ip.loadUnionType(union_type); + return mod.getUnionLayout(union_obj); } 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| { const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse return .Auto; return struct_obj.layout; }, .anon_struct_type => .Auto, - .union_type => |union_type| { - const union_obj = mod.unionPtr(union_type.index); - return union_obj.layout; - }, + .union_type => |union_type| union_type.flagsPtr(ip).layout, else => unreachable, }; } @@ -2570,14 +2551,16 @@ pub const Type = struct { }, .union_type => |union_type| { - const union_obj = mod.unionPtr(union_type.index); - const tag_val = (try union_obj.tag_ty.onePossibleValue(mod)) orelse return null; - if (union_obj.fields.count() == 0) { + const union_obj = ip.loadUnionType(union_type); + const tag_val = (try union_obj.enum_tag_ty.toType().onePossibleValue(mod)) orelse + return null; + if (union_obj.field_names.len == 0) { const only = try mod.intern(.{ .empty_enum_value = ty.toIntern() }); return only.toValue(); } - const only_field = union_obj.fields.values()[0]; - const val_val = (try only_field.ty.onePossibleValue(mod)) orelse return null; + const only_field_ty = union_obj.field_types.get(ip)[0]; + const val_val = (try only_field_ty.toType().onePossibleValue(mod)) orelse + return null; const only = try mod.intern(.{ .un = .{ .ty = ty.toIntern(), .tag = tag_val.toIntern(), @@ -2657,10 +2640,11 @@ pub const Type = struct { /// TODO merge these implementations together with the "advanced" pattern seen /// elsewhere in this file. pub fn comptimeOnly(ty: Type, mod: *Module) bool { + const ip = &mod.intern_pool; return switch (ty.toIntern()) { .empty_struct_type => false, - else => switch (mod.intern_pool.indexToKey(ty.toIntern())) { + else => switch (ip.indexToKey(ty.toIntern())) { .int_type => false, .ptr_type => |ptr_type| { const child_ty = ptr_type.child.toType(); @@ -2704,6 +2688,7 @@ pub const Type = struct { .c_longlong, .c_ulonglong, .c_longdouble, + .anyopaque, .bool, .void, .anyerror, @@ -2722,7 +2707,6 @@ pub const Type = struct { .extern_options, => false, - .anyopaque, .type, .comptime_int, .comptime_float, @@ -2756,8 +2740,7 @@ pub const Type = struct { }, .union_type => |union_type| { - const union_obj = mod.unionPtr(union_type.index); - switch (union_obj.requires_comptime) { + switch (union_type.flagsPtr(ip).requires_comptime) { .wip, .unknown => { // Return false to avoid incorrect dependency loops. // 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), @@ -2847,7 +2830,7 @@ pub const Type = struct { return switch (mod.intern_pool.indexToKey(ty.toIntern())) { .opaque_type => |opaque_type| opaque_type.namespace.toOptional(), .struct_type => |struct_type| struct_type.namespace, - .union_type => |union_type| mod.unionPtr(union_type.index).namespace.toOptional(), + .union_type => |union_type| union_type.namespace.toOptional(), .enum_type => |enum_type| enum_type.namespace, else => .none, @@ -2935,7 +2918,7 @@ pub const Type = struct { /// Asserts the type is an enum or a union. pub fn intTagType(ty: Type, mod: *Module) Type { 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(), else => unreachable, }; @@ -3038,15 +3021,16 @@ pub const Type = struct { /// Supports structs and unions. 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| { const struct_obj = mod.structPtrUnwrap(struct_type.index).?; assert(struct_obj.haveFieldTypes()); return struct_obj.fields.values()[index].ty; }, .union_type => |union_type| { - const union_obj = mod.unionPtr(union_type.index); - return union_obj.fields.values()[index].ty; + const union_obj = ip.loadUnionType(union_type); + return union_obj.field_types.get(ip)[index].toType(); }, .anon_struct_type => |anon_struct| anon_struct.types[index].toType(), else => unreachable, @@ -3054,7 +3038,8 @@ pub const Type = struct { } 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| { const struct_obj = mod.structPtrUnwrap(struct_type.index).?; assert(struct_obj.layout != .Packed); @@ -3064,8 +3049,8 @@ pub const Type = struct { return anon_struct.types[index].toType().abiAlignment(mod); }, .union_type => |union_type| { - const union_obj = mod.unionPtr(union_type.index); - return union_obj.fields.values()[index].normalAlignment(mod); + const union_obj = ip.loadUnionType(union_type); + return mod.unionFieldNormalAlignment(union_obj, @intCast(index)); }, else => unreachable, } @@ -3198,7 +3183,8 @@ pub const Type = struct { /// Supports structs and unions. 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| { const struct_obj = mod.structPtrUnwrap(struct_type.index).?; assert(struct_obj.haveLayout()); @@ -3234,10 +3220,10 @@ pub const Type = struct { }, .union_type => |union_type| { - if (!union_type.hasTag()) + if (!union_type.hasTag(ip)) return 0; - const union_obj = mod.unionPtr(union_type.index); - const layout = union_obj.getLayout(mod, true); + const union_obj = ip.loadUnionType(union_type); + const layout = mod.getUnionLayout(union_obj); if (layout.tag_align >= layout.payload_align) { // {Tag, Payload} return std.mem.alignForward(u64, layout.tag_size, layout.payload_align); @@ -3262,8 +3248,7 @@ pub const Type = struct { return struct_obj.srcLoc(mod); }, .union_type => |union_type| { - const union_obj = mod.unionPtr(union_type.index); - return union_obj.srcLoc(mod); + return mod.declPtr(union_type.decl).srcLoc(mod); }, .opaque_type => |opaque_type| mod.opaqueSrcLoc(opaque_type), .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; return struct_obj.owner_decl; }, - .union_type => |union_type| { - const union_obj = mod.unionPtr(union_type.index); - return union_obj.owner_decl; - }, + .union_type => |union_type| union_type.decl, .opaque_type => |opaque_type| opaque_type.decl, .enum_type => |enum_type| enum_type.decl, else => null, diff --git a/src/value.zig b/src/value.zig index fdf061c680bd..7612a9f8c240 100644 --- a/src/value.zig +++ b/src/value.zig @@ -734,6 +734,7 @@ pub const Value = struct { buffer: []u8, bit_offset: usize, ) error{ ReinterpretDeclRef, OutOfMemory }!void { + const ip = &mod.intern_pool; const target = mod.getTarget(); const endian = target.cpu.arch.endian(); if (val.isUndef(mod)) { @@ -759,7 +760,7 @@ pub const Value = struct { const bits = ty.intInfo(mod).bits; 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), .big_int => |bigint| bigint.writePackedTwosComplement(buffer, bit_offset, bits, endian), else => unreachable, @@ -794,7 +795,7 @@ pub const Value = struct { .Packed => { var bits: u16 = 0; 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| { const field_bits = @as(u16, @intCast(field.ty.bitSize(mod))); const field_val = switch (storage) { @@ -807,16 +808,19 @@ pub const Value = struct { } }, }, - .Union => switch (ty.containerLayout(mod)) { - .Auto => unreachable, // Sema is supposed to have emitted a compile error already - .Extern => unreachable, // Handled in non-packed writeToMemory - .Packed => { - const field_index = ty.unionTagFieldIndex(val.unionTag(mod), mod); - const field_type = ty.unionFields(mod).values()[field_index.?].ty; - const field_val = try val.fieldValue(mod, field_index.?); - - return field_val.writeToPackedMemory(field_type, mod, buffer, bit_offset); - }, + .Union => { + const union_obj = mod.typeToUnion(ty).?; + switch (union_obj.getLayout(ip)) { + .Auto => unreachable, // Sema is supposed to have emitted a compile error already + .Extern => unreachable, // Handled in non-packed writeToMemory + .Packed => { + 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); + }, + } }, .Pointer => { assert(!ty.isSlice(mod)); // No well defined layout. diff --git a/test/behavior/union.zig b/test/behavior/union.zig index 3ea6f17a264a..ff5fd9edc42d 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -1347,31 +1347,6 @@ test "noreturn field in union" { 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" { if (builtin.zig_backend == .stage2_aarch64) 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, }; const U = union(E) { - a: usize, b: isize, + a: usize, }; var i: isize = -1; var u = @unionInit(U, "b", i); diff --git a/test/cases/compile_errors/access_inactive_union_field_comptime.zig b/test/cases/compile_errors/access_inactive_union_field_comptime.zig index 2098b19d1450..fec57b2b36e7 100644 --- a/test/cases/compile_errors/access_inactive_union_field_comptime.zig +++ b/test/cases/compile_errors/access_inactive_union_field_comptime.zig @@ -1,4 +1,4 @@ -const Enum = enum(u32) { a, b }; +const Enum = enum(u32) { b, a }; const TaggedUnion = union(Enum) { b: []const u8, a: []const u8, diff --git a/test/cases/compile_errors/dereference_anyopaque.zig b/test/cases/compile_errors/dereference_anyopaque.zig index 6dbbdfe1e201..bb22bc8e2cba 100644 --- a/test/cases/compile_errors/dereference_anyopaque.zig +++ b/test/cases/compile_errors/dereference_anyopaque.zig @@ -45,8 +45,7 @@ pub export fn entry() void { // backend=llvm // // :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: note: opaque type 'anyopaque' has undefined size +// :25:51: error: cannot load opaque type 'anyopaque' // :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: error: values of type 'fn(*anyopaque, []u8, u8, usize, usize) bool' must be comptime-known, but operand value is runtime-known diff --git a/test/cases/compile_errors/directly_embedding_opaque_type_in_struct_and_union.zig b/test/cases/compile_errors/directly_embedding_opaque_type_in_struct_and_union.zig index ace90bccfc1a..d9f27da4aa66 100644 --- a/test/cases/compile_errors/directly_embedding_opaque_type_in_struct_and_union.zig +++ b/test/cases/compile_errors/directly_embedding_opaque_type_in_struct_and_union.zig @@ -15,12 +15,12 @@ export fn b() void { _ = bar; } export fn c() void { - const baz = &@as(opaque {}, undefined); + const baz = &@as(O, undefined); const qux = .{baz.*}; _ = qux; } export fn d() void { - const baz = &@as(opaque {}, undefined); + const baz = &@as(O, undefined); const qux = .{ .a = baz.* }; _ = qux; } @@ -33,7 +33,5 @@ export fn d() void { // :1:11: note: opaque declared here // :7:10: error: opaque types have unknown size and therefore cannot be directly embedded in unions // :1:11: note: opaque declared here -// :19:18: error: opaque types have unknown size and therefore cannot be directly embedded in structs -// :18:22: note: opaque declared here -// :24:23: error: opaque types have unknown size and therefore cannot be directly embedded in structs -// :23:22: note: opaque declared here +// :19:22: error: cannot load opaque type 'tmp.O' +// :24:28: error: cannot load opaque type 'tmp.O' diff --git a/test/cases/compile_errors/non-const_variables_of_things_that_require_const_variables.zig b/test/cases/compile_errors/non-const_variables_of_things_that_require_const_variables.zig index 48b92460c475..7064ebb1b672 100644 --- a/test/cases/compile_errors/non-const_variables_of_things_that_require_const_variables.zig +++ b/test/cases/compile_errors/non-const_variables_of_things_that_require_const_variables.zig @@ -27,6 +27,10 @@ export fn entry7() void { _ = f; } const Opaque = opaque {}; +export fn entry8() void { + var e: Opaque = undefined; + _ = &e; +} // error // backend=stage2 @@ -39,7 +43,7 @@ const Opaque = opaque {}; // :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 // :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: note: opaque type 'tmp.Opaque' has undefined size +// :22:20: error: cannot load opaque type 'tmp.Opaque' // :26:9: error: variable of type 'type' must be const or comptime // :26:9: note: types are not available at runtime +// :31:12: error: non-extern variable with opaque type 'tmp.Opaque'