From 975b859377dee450418ae9ed572ec9d3c0b77312 Mon Sep 17 00:00:00 2001 From: mlugg Date: Sat, 17 Feb 2024 01:18:54 +0000 Subject: InternPool: create specialized functions for loading namespace types Namespace types (`struct`, `enum`, `union`, `opaque`) do not use structural equality - equivalence is based on their Decl index (and soon will change to AST node + captures). However, we previously stored all other information in the corresponding `InternPool.Key` anyway. For logical consistency, it makes sense to have the key only be the true key (that is, the Decl index) and to load all other data through another function. This introduces those functions, by the name of `loadStructType` etc. It's a big diff, but most of it is no-brainer changes. In future, it might be nice to eliminate a bunch of the loaded state in favour of accessor functions on the `LoadedXyzType` types (like how we have `LoadedUnionType.size()`), but that can be explored at a later date. --- src/codegen/llvm.zig | 54 +++++++++++++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 24 deletions(-) (limited to 'src/codegen/llvm.zig') diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index a7e2815e73..d056a384ee 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1997,7 +1997,7 @@ pub const Object = struct { return debug_enum_type; } - const enum_type = ip.indexToKey(ty.toIntern()).enum_type; + const enum_type = ip.loadEnumType(ty.toIntern()); const enumerators = try gpa.alloc(Builder.Metadata, enum_type.names.len); defer gpa.free(enumerators); @@ -2507,8 +2507,8 @@ pub const Object = struct { try o.debug_type_map.put(gpa, ty, debug_struct_type); return debug_struct_type; }, - .struct_type => |struct_type| { - if (!struct_type.haveFieldTypes(ip)) { + .struct_type => { + if (!ip.loadStructType(ty.toIntern()).haveFieldTypes(ip)) { // This can happen if a struct type makes it all the way to // flush() without ever being instantiated or referenced (even // via pointer). The only reason we are hearing about it now is @@ -2597,15 +2597,14 @@ pub const Object = struct { const name = try o.allocTypeName(ty); defer gpa.free(name); - const union_type = ip.indexToKey(ty.toIntern()).union_type; + const union_type = ip.loadUnionType(ty.toIntern()); if (!union_type.haveFieldTypes(ip) or !ty.hasRuntimeBitsIgnoreComptime(mod)) { const debug_union_type = try o.makeEmptyNamespaceDebugType(owner_decl_index); try o.debug_type_map.put(gpa, ty, debug_union_type); return debug_union_type; } - const union_obj = ip.loadUnionType(union_type); - const layout = mod.getUnionLayout(union_obj); + const layout = mod.getUnionLayout(union_type); const debug_fwd_ref = try o.builder.debugForwardReference(); @@ -2622,7 +2621,7 @@ pub const Object = struct { ty.abiSize(mod) * 8, ty.abiAlignment(mod).toByteUnits(0) * 8, try o.builder.debugTuple( - &.{try o.lowerDebugType(Type.fromInterned(union_obj.enum_tag_ty))}, + &.{try o.lowerDebugType(Type.fromInterned(union_type.enum_tag_ty))}, ), ); @@ -2636,21 +2635,23 @@ pub const Object = struct { var fields: std.ArrayListUnmanaged(Builder.Metadata) = .{}; defer fields.deinit(gpa); - try fields.ensureUnusedCapacity(gpa, union_obj.field_names.len); + try fields.ensureUnusedCapacity(gpa, union_type.loadTagType(ip).names.len); const debug_union_fwd_ref = if (layout.tag_size == 0) debug_fwd_ref else try o.builder.debugForwardReference(); - for (0..union_obj.field_names.len) |field_index| { - const field_ty = union_obj.field_types.get(ip)[field_index]; + const tag_type = union_type.loadTagType(); + + for (0..tag_type.names.len) |field_index| { + const field_ty = union_type.field_types.get(ip)[field_index]; if (!Type.fromInterned(field_ty).hasRuntimeBitsIgnoreComptime(mod)) continue; const field_size = Type.fromInterned(field_ty).abiSize(mod); - const field_align = mod.unionFieldNormalAlignment(union_obj, @intCast(field_index)); + const field_align = mod.unionFieldNormalAlignment(union_type, @intCast(field_index)); - const field_name = union_obj.field_names.get(ip)[field_index]; + const field_name = tag_type.names.get(ip)[field_index]; fields.appendAssumeCapacity(try o.builder.debugMemberType( try o.builder.metadataString(ip.stringToSlice(field_name)), .none, // File @@ -2706,7 +2707,7 @@ pub const Object = struct { .none, // File debug_fwd_ref, 0, // Line - try o.lowerDebugType(Type.fromInterned(union_obj.enum_tag_ty)), + try o.lowerDebugType(Type.fromInterned(union_type.enum_tag_ty)), layout.tag_size * 8, layout.tag_align.toByteUnits(0) * 8, tag_offset * 8, @@ -3321,9 +3322,11 @@ pub const Object = struct { return o.builder.structType(.normal, fields[0..fields_len]); }, .simple_type => unreachable, - .struct_type => |struct_type| { + .struct_type => { if (o.type_map.get(t.toIntern())) |value| return value; + const struct_type = ip.loadStructType(t.toIntern()); + if (struct_type.layout == .Packed) { const int_ty = try o.lowerType(Type.fromInterned(struct_type.backingIntType(ip).*)); try o.type_map.put(o.gpa, t.toIntern(), int_ty); @@ -3468,10 +3471,10 @@ pub const Object = struct { } return o.builder.structType(.normal, llvm_field_types.items); }, - .union_type => |union_type| { + .union_type => { if (o.type_map.get(t.toIntern())) |value| return value; - const union_obj = ip.loadUnionType(union_type); + const union_obj = ip.loadUnionType(t.toIntern()); const layout = mod.getUnionLayout(union_obj); if (union_obj.flagsPtr(ip).layout == .Packed) { @@ -3555,7 +3558,7 @@ pub const Object = struct { } return gop.value_ptr.*; }, - .enum_type => |enum_type| try o.lowerType(Type.fromInterned(enum_type.tag_ty)), + .enum_type => try o.lowerType(Type.fromInterned(ip.loadEnumType(t.toIntern()).tag_ty)), .func_type => |func_type| try o.lowerTypeFn(func_type), .error_set_type, .inferred_error_set_type => try o.errorIntType(), // values, not types @@ -4032,7 +4035,8 @@ pub const Object = struct { else struct_ty, vals); }, - .struct_type => |struct_type| { + .struct_type => { + const struct_type = ip.loadStructType(ty.toIntern()); assert(struct_type.haveLayout(ip)); const struct_ty = try o.lowerType(ty); if (struct_type.layout == .Packed) { @@ -4596,7 +4600,7 @@ pub const Object = struct { fn getEnumTagNameFunction(o: *Object, enum_ty: Type) !Builder.Function.Index { const zcu = o.module; const ip = &zcu.intern_pool; - const enum_type = ip.indexToKey(enum_ty.toIntern()).enum_type; + const enum_type = ip.loadEnumType(enum_ty.toIntern()); // TODO: detect when the type changes and re-emit this function. const gop = try o.decl_map.getOrPut(o.gpa, enum_type.decl); @@ -9620,7 +9624,7 @@ pub const FuncGen = struct { fn getIsNamedEnumValueFunction(self: *FuncGen, enum_ty: Type) !Builder.Function.Index { const o = self.dg.object; const zcu = o.module; - const enum_type = zcu.intern_pool.indexToKey(enum_ty.toIntern()).enum_type; + const enum_type = zcu.intern_pool.loadEnumType(enum_ty.toIntern()); // TODO: detect when the type changes and re-emit this function. const gop = try o.named_enum_map.getOrPut(o.gpa, enum_type.decl); @@ -10092,7 +10096,7 @@ pub const FuncGen = struct { const tag_int = blk: { const tag_ty = union_ty.unionTagTypeHypothetical(mod); - const union_field_name = union_obj.field_names.get(ip)[extra.field_index]; + const union_field_name = union_obj.loadTagType(ip).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); @@ -11154,7 +11158,8 @@ fn lowerSystemVFnRetTy(o: *Object, fn_info: InternPool.Key.FuncType) Allocator.E if (first_non_integer == null or classes[first_non_integer.?] == .none) { assert(first_non_integer orelse classes.len == types_index); switch (ip.indexToKey(return_type.toIntern())) { - .struct_type => |struct_type| { + .struct_type => { + const struct_type = ip.loadStructType(return_type.toIntern()); assert(struct_type.haveLayout(ip)); const size: u64 = struct_type.size(ip).*; assert((std.math.divCeil(u64, size, 8) catch unreachable) == types_index); @@ -11446,7 +11451,8 @@ const ParamTypeIterator = struct { return .byref; } switch (ip.indexToKey(ty.toIntern())) { - .struct_type => |struct_type| { + .struct_type => { + const struct_type = ip.loadStructType(ty.toIntern()); assert(struct_type.haveLayout(ip)); const size: u64 = struct_type.size(ip).*; assert((std.math.divCeil(u64, size, 8) catch unreachable) == types_index); @@ -11562,7 +11568,7 @@ fn isByRef(ty: Type, mod: *Module) bool { } return false; }, - .struct_type => |s| s, + .struct_type => ip.loadStructType(ty.toIntern()), else => unreachable, }; -- cgit v1.2.3 From d0c022f7347b5cda34751a986a535aee3b1f45dc Mon Sep 17 00:00:00 2001 From: mlugg Date: Tue, 5 Mar 2024 06:02:58 +0000 Subject: compiler: namespace type equivalence based on AST node + captures This implements the accepted proposal #18816. Namespace-owning types (struct, enum, union, opaque) are no longer unique whenever analysed; instead, their identity is determined based on their AST node and the set of values they capture. Reified types (`@Type`) are deduplicated based on the structure of the type created. For instance, if two structs are created by the same reification with identical fields, layout, etc, they will be the same type. This commit does not produce a working compiler; the next commit, adding captures for decl references, is necessary. It felt appropriate to split this up. Resolves: #18816 --- src/InternPool.zig | 1361 ++++++++++++++++++++++++++++++----------------- src/Module.zig | 138 ++--- src/Sema.zig | 1421 ++++++++++++++++++++++++++------------------------ src/codegen/llvm.zig | 7 +- src/type.zig | 44 +- 5 files changed, 1709 insertions(+), 1262 deletions(-) (limited to 'src/codegen/llvm.zig') diff --git a/src/InternPool.zig b/src/InternPool.zig index 86dd29c90a..36311100da 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -344,6 +344,7 @@ const KeyAdapter = struct { pub fn eql(ctx: @This(), a: Key, b_void: void, b_map_index: usize) bool { _ = b_void; + if (ctx.intern_pool.items.items(.tag)[b_map_index] == .removed) return false; return ctx.intern_pool.indexToKey(@as(Index, @enumFromInt(b_map_index))).eql(a, ctx.intern_pool); } @@ -551,14 +552,14 @@ pub const Key = union(enum) { /// This represents a struct that has been explicitly declared in source code, /// or was created with `@Type`. It is unique and based on a declaration. /// It may be a tuple, if declared like this: `struct {A, B, C}`. - struct_type: StructType, + struct_type: NamespaceType, /// This is an anonymous struct or tuple type which has no corresponding /// 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: Key.UnionType, - opaque_type: OpaqueType, - enum_type: EnumType, + union_type: NamespaceType, + opaque_type: NamespaceType, + enum_type: NamespaceType, func_type: FuncType, error_set_type: ErrorSetType, /// The payload is the function body, either a `func_decl` or `func_instance`. @@ -703,66 +704,41 @@ pub const Key = union(enum) { } }; - /// This is the hashmap key. To fetch other data associated with the struct, see `loadStructType`. - pub const StructType = struct { - /// The struct's owner Decl. `none` when the struct is `@TypeOf(.{})`. - decl: OptionalDeclIndex, - }; - - /// This is the hashmap key. To fetch other data associated with the opaque, see `loadOpaqueType`. - pub const OpaqueType = struct { - /// The opaque's owner Decl. - decl: DeclIndex, - }; - - /// This is the hashmap key. To fetch other data associated with the union, see `loadUnionType`. - pub const UnionType = struct { - /// The union's owner Decl. - decl: DeclIndex, - }; - - /// This is the hashmap key. To fetch other data associated with the enum, see `loadEnumType`. - pub const EnumType = struct { - /// The enum's owner Decl. - decl: DeclIndex, - }; - - pub const IncompleteEnumType = struct { - /// Same as corresponding `EnumType` field. - decl: DeclIndex, - /// Same as corresponding `EnumType` field. - namespace: OptionalNamespaceIndex, - /// The field names and field values are not known yet, but - /// the number of fields must be known ahead of time. - fields_len: u32, - /// This information is needed so that the size does not change - /// later when populating field values. - has_values: bool, - /// Same as corresponding `EnumType` field. - tag_mode: LoadedEnumType.TagMode, - /// This may be updated via `setTagType` later. - tag_ty: Index = .none, - zir_index: TrackedInst.Index.Optional, - captures: []const CaptureValue, - - pub fn toEnumType(self: @This()) LoadedEnumType { - if (true) @compileError("AHHHH"); - return .{ - .decl = self.decl, - .namespace = self.namespace, - .tag_ty = self.tag_ty, - .tag_mode = self.tag_mode, - .names = .{ .start = 0, .len = 0 }, - .values = .{ .start = 0, .len = 0 }, - .zir_index = self.zir_index, - }; - } - - /// Only the decl is used for hashing and equality, so we can construct - /// this minimal key for use with `map`. - pub fn toKey(self: @This()) Key { - return .{ .enum_type = .{ .decl = self.decl } }; - } + /// This is the hashmap key. To fetch other data associated with the type, see: + /// * `loadStructType` + /// * `loadUnionType` + /// * `loadEnumType` + /// * `loadOpaqueType` + pub const NamespaceType = union(enum) { + /// This type corresponds to an actual source declaration, e.g. `struct { ... }`. + /// It is hashed based on its ZIR instruction index and set of captures. + declared: struct { + /// A `struct_decl`, `union_decl`, `enum_decl`, or `opaque_decl` instruction. + zir_index: TrackedInst.Index, + /// The captured values of this type. These values must be fully resolved per the language spec. + captures: union(enum) { + owned: CaptureValue.Slice, + external: []const CaptureValue, + }, + }, + /// This type is an automatically-generated enum tag type for a union. + /// It is hashed based on the index of the union type it corresponds to. + generated_tag: struct { + /// The union for which this is a tag type. + union_type: Index, + }, + /// This type originates from a reification via `@Type`. + /// It is hased based on its ZIR instruction index and fields, attributes, etc. + /// To avoid making this key overly complex, the type-specific data is hased by Sema. + reified: struct { + /// A `reify` instruction. + zir_index: TrackedInst.Index, + /// A hash of this type's attributes, fields, etc, generated by Sema. + type_hash: u64, + }, + /// This type is `@TypeOf(.{})`. + /// TODO: can we change the language spec to not special-case this type? + empty_struct: void, }; pub const FuncType = struct { @@ -1113,12 +1089,37 @@ pub const Key = union(enum) { .payload => |y| Hash.hash(seed + 1, asBytes(&x.ty) ++ asBytes(&y)), }, - inline .opaque_type, + .variable => |variable| Hash.hash(seed, asBytes(&variable.decl)), + + .opaque_type, .enum_type, - .variable, .union_type, .struct_type, - => |x| Hash.hash(seed, asBytes(&x.decl)), + => |namespace_type| { + var hasher = Hash.init(seed); + std.hash.autoHash(&hasher, std.meta.activeTag(namespace_type)); + switch (namespace_type) { + .declared => |declared| { + std.hash.autoHash(&hasher, declared.zir_index); + const captures = switch (declared.captures) { + .owned => |cvs| cvs.get(ip), + .external => |cvs| cvs, + }; + for (captures) |cv| { + std.hash.autoHash(&hasher, cv); + } + }, + .generated_tag => |generated_tag| { + std.hash.autoHash(&hasher, generated_tag.union_type); + }, + .reified => |reified| { + std.hash.autoHash(&hasher, reified.zir_index); + std.hash.autoHash(&hasher, reified.type_hash); + }, + .empty_struct => {}, + } + return hasher.final(); + }, .int => |int| { var hasher = Hash.init(seed); @@ -1523,21 +1524,31 @@ pub const Key = union(enum) { } }, - .opaque_type => |a_info| { - const b_info = b.opaque_type; - return a_info.decl == b_info.decl; - }, - .enum_type => |a_info| { - 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; - }, - .struct_type => |a_info| { - const b_info = b.struct_type; - return a_info.decl == b_info.decl; + inline .opaque_type, .enum_type, .union_type, .struct_type => |a_info, a_tag_ct| { + const b_info = @field(b, @tagName(a_tag_ct)); + if (std.meta.activeTag(a_info) != b_info) return false; + switch (a_info) { + .declared => |a_d| { + const b_d = b_info.declared; + if (a_d.zir_index != b_d.zir_index) return false; + const a_captures = switch (a_d.captures) { + .owned => |s| s.get(ip), + .external => |cvs| cvs, + }; + const b_captures = switch (b_d.captures) { + .owned => |s| s.get(ip), + .external => |cvs| cvs, + }; + return std.mem.eql(u32, @ptrCast(a_captures), @ptrCast(b_captures)); + }, + .generated_tag => |a_gt| return a_gt.union_type == b_info.generated_tag.union_type, + .reified => |a_r| { + const b_r = b_info.reified; + return a_r.zir_index == b_r.zir_index and + a_r.type_hash == b_r.type_hash; + }, + .empty_struct => return true, + } }, .aggregate => |a_info| { const b_info = b.aggregate; @@ -1685,7 +1696,7 @@ pub const LoadedUnionType = struct { /// The Decl that corresponds to the union itself. decl: DeclIndex, /// Represents the declarations inside this union. - namespace: NamespaceIndex, + namespace: OptionalNamespaceIndex, /// The enum tag type. enum_tag_ty: Index, /// List of field types in declaration order. @@ -1695,8 +1706,8 @@ pub const LoadedUnionType = struct { /// `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: TrackedInst.Index.Optional, + /// Index of the union_decl or reify ZIR instruction. + zir_index: TrackedInst.Index, captures: CaptureValue.Slice, pub const RuntimeTag = enum(u2) { @@ -1845,6 +1856,9 @@ pub fn loadUnionType(ip: *const InternPool, index: Index) LoadedUnionType { .len = captures_len, }; extra_index += captures_len; + if (type_union.data.flags.is_reified) { + extra_index += 2; // PackedU64 + } const field_types: Index.Slice = .{ .start = extra_index, @@ -1880,7 +1894,8 @@ pub const LoadedStructType = struct { decl: OptionalDeclIndex, /// `none` when the struct has no declarations. namespace: OptionalNamespaceIndex, - /// Index of the `struct_decl` ZIR instruction. + /// Index of the `struct_decl` or `reify` ZIR instruction. + /// Only `none` when the struct is `@TypeOf(.{})`. zir_index: TrackedInst.Index.Optional, layout: std.builtin.Type.ContainerLayout, field_names: NullTerminatedString.Slice, @@ -2239,6 +2254,9 @@ pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType { .len = captures_len, }; extra_index += captures_len; + if (extra.data.flags.is_reified) { + extra_index += 2; // PackedU64 + } const field_types: Index.Slice = .{ .start = extra_index, .len = fields_len, @@ -2286,7 +2304,7 @@ pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType { .extra_index = item.data, .decl = extra.data.decl.toOptional(), .namespace = namespace, - .zir_index = extra.data.zir_index, + .zir_index = extra.data.zir_index.toOptional(), .layout = if (extra.data.flags.is_extern) .Extern else .Auto, .field_names = names, .field_types = field_types, @@ -2314,6 +2332,9 @@ pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType { .len = captures_len, }; extra_index += captures_len; + if (extra.data.flags.is_reified) { + extra_index += 2; // PackedU64 + } const field_types: Index.Slice = .{ .start = extra_index, .len = fields_len, @@ -2336,7 +2357,7 @@ pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType { .extra_index = item.data, .decl = extra.data.decl.toOptional(), .namespace = extra.data.namespace, - .zir_index = extra.data.zir_index, + .zir_index = extra.data.zir_index.toOptional(), .layout = .Packed, .field_names = field_names, .field_types = field_types, @@ -2372,6 +2393,7 @@ const LoadedEnumType = struct { names_map: MapIndex, /// This is guaranteed to not be `.none` if explicit values are provided. values_map: OptionalMapIndex, + /// This is `none` only if this is a generated tag type. zir_index: TrackedInst.Index.Optional, captures: CaptureValue.Slice, @@ -2425,15 +2447,23 @@ const LoadedEnumType = struct { pub fn loadEnumType(ip: *const InternPool, index: Index) LoadedEnumType { const item = ip.items.get(@intFromEnum(index)); - switch (item.tag) { + const tag_mode: LoadedEnumType.TagMode = switch (item.tag) { .type_enum_auto => { const extra = ip.extraDataTrail(EnumAuto, item.data); + var extra_index: u32 = @intCast(extra.end); + if (extra.data.zir_index == .none) { + extra_index += 1; // owner_union + } + const captures_len = if (extra.data.captures_len == std.math.maxInt(u32)) c: { + extra_index += 2; // type_hash: PackedU64 + break :c 0; + } else extra.data.captures_len; return .{ .decl = extra.data.decl, .namespace = extra.data.namespace, .tag_ty = extra.data.int_tag_type, .names = .{ - .start = @intCast(extra.end + extra.data.captures_len), + .start = extra_index + captures_len, .len = extra.data.fields_len, }, .values = .{ .start = 0, .len = 0 }, @@ -2442,41 +2472,45 @@ pub fn loadEnumType(ip: *const InternPool, index: Index) LoadedEnumType { .values_map = .none, .zir_index = extra.data.zir_index, .captures = .{ - .start = @intCast(extra.end), - .len = extra.data.captures_len, - }, - }; - }, - .type_enum_explicit, .type_enum_nonexhaustive => { - const extra = ip.extraDataTrail(EnumExplicit, item.data); - return .{ - .decl = extra.data.decl, - .namespace = extra.data.namespace, - .tag_ty = extra.data.int_tag_type, - .names = .{ - .start = @intCast(extra.end + extra.data.captures_len), - .len = extra.data.fields_len, - }, - .values = .{ - .start = @intCast(extra.end + extra.data.captures_len + extra.data.fields_len), - .len = if (extra.data.values_map != .none) extra.data.fields_len else 0, - }, - .tag_mode = switch (item.tag) { - .type_enum_explicit => .explicit, - .type_enum_nonexhaustive => .nonexhaustive, - else => unreachable, - }, - .names_map = extra.data.names_map, - .values_map = extra.data.values_map, - .zir_index = extra.data.zir_index, - .captures = .{ - .start = @intCast(extra.end), - .len = extra.data.captures_len, + .start = extra_index, + .len = captures_len, }, }; }, + .type_enum_explicit => .explicit, + .type_enum_nonexhaustive => .nonexhaustive, else => unreachable, - } + }; + const extra = ip.extraDataTrail(EnumExplicit, item.data); + var extra_index: u32 = @intCast(extra.end); + if (extra.data.zir_index == .none) { + extra_index += 1; // owner_union + } + const captures_len = if (extra.data.captures_len == std.math.maxInt(u32)) c: { + extra_index += 2; // type_hash: PackedU64 + break :c 0; + } else extra.data.captures_len; + return .{ + .decl = extra.data.decl, + .namespace = extra.data.namespace, + .tag_ty = extra.data.int_tag_type, + .names = .{ + .start = extra_index + captures_len, + .len = extra.data.fields_len, + }, + .values = .{ + .start = extra_index + captures_len + extra.data.fields_len, + .len = if (extra.data.values_map != .none) extra.data.fields_len else 0, + }, + .tag_mode = tag_mode, + .names_map = extra.data.names_map, + .values_map = extra.data.values_map, + .zir_index = extra.data.zir_index, + .captures = .{ + .start = extra_index, + .len = captures_len, + }, + }; } /// Note that this type doubles as the payload for `Tag.type_opaque`. @@ -2484,9 +2518,9 @@ pub const LoadedOpaqueType = struct { /// The opaque's owner Decl. decl: DeclIndex, /// Contains the declarations inside this opaque. - namespace: NamespaceIndex, - /// The index of the `opaque_decl` instruction. - zir_index: TrackedInst.Index.Optional, + namespace: OptionalNamespaceIndex, + /// Index of the `opaque_decl` or `reify` instruction. + zir_index: TrackedInst.Index, captures: CaptureValue.Slice, }; @@ -2494,13 +2528,17 @@ pub fn loadOpaqueType(ip: *const InternPool, index: Index) LoadedOpaqueType { assert(ip.items.items(.tag)[@intFromEnum(index)] == .type_opaque); const extra_index = ip.items.items(.data)[@intFromEnum(index)]; const extra = ip.extraDataTrail(Tag.TypeOpaque, extra_index); + const captures_len = if (extra.data.captures_len == std.math.maxInt(u32)) + 0 + else + extra.data.captures_len; return .{ .decl = extra.data.decl, .namespace = extra.data.namespace, .zir_index = extra.data.zir_index, .captures = .{ .start = extra.end, - .len = extra.data.captures_len, + .len = captures_len, }, }; } @@ -2693,6 +2731,7 @@ pub const Index = enum(u32) { }, }; + removed: void, type_int_signed: struct { data: u32 }, type_int_unsigned: struct { data: u32 }, type_array_big: struct { data: *Array }, @@ -3100,6 +3139,12 @@ comptime { } pub const Tag = enum(u8) { + /// This special tag represents a value which was removed from this pool via + /// `InternPool.remove`. The item remains allocated to preserve indices, but + /// lookups will consider it not equal to any other item, and all queries + /// assert not this tag. `data` is unused. + removed, + /// An integer type. /// data is number of bits type_int_signed, @@ -3367,6 +3412,7 @@ pub const Tag = enum(u8) { fn Payload(comptime tag: Tag) type { return switch (tag) { + .removed => unreachable, .type_int_signed => unreachable, .type_int_unsigned => unreachable, .type_array_big => Array, @@ -3544,8 +3590,9 @@ pub const Tag = enum(u8) { /// Trailing: /// 0. captures_len: u32 // if `any_captures` /// 1. capture: CaptureValue // for each `captures_len` - /// 2. field type: Index for each field; declaration order - /// 3. field align: Alignment for each field; declaration order + /// 2. type_hash: PackedU64 // if `is_reified` + /// 3. field type: Index for each field; declaration order + /// 4. field align: Alignment for each field; declaration order pub const TypeUnion = struct { flags: Flags, /// This could be provided through the tag type, but it is more convenient @@ -3557,10 +3604,10 @@ pub const Tag = enum(u8) { /// Only valid after .have_layout padding: u32, decl: DeclIndex, - namespace: NamespaceIndex, + namespace: OptionalNamespaceIndex, /// The enum that provides the list of field names and values. tag_ty: Index, - zir_index: TrackedInst.Index.Optional, + zir_index: TrackedInst.Index, pub const Flags = packed struct(u32) { any_captures: bool, @@ -3573,19 +3620,21 @@ pub const Tag = enum(u8) { assumed_runtime_bits: bool, assumed_pointer_aligned: bool, alignment: Alignment, - _: u13 = 0, + is_reified: bool, + _: u12 = 0, }; }; /// Trailing: /// 0. captures_len: u32 // if `any_captures` /// 1. capture: CaptureValue // for each `captures_len` - /// 2. type: Index for each fields_len - /// 3. name: NullTerminatedString for each fields_len - /// 4. init: Index for each fields_len // if tag is type_struct_packed_inits + /// 2. type_hash: PackedU64 // if `is_reified` + /// 3. type: Index for each fields_len + /// 4. name: NullTerminatedString for each fields_len + /// 5. init: Index for each fields_len // if tag is type_struct_packed_inits pub const TypeStructPacked = struct { decl: DeclIndex, - zir_index: TrackedInst.Index.Optional, + zir_index: TrackedInst.Index, fields_len: u32, namespace: OptionalNamespaceIndex, backing_int_ty: Index, @@ -3597,7 +3646,8 @@ pub const Tag = enum(u8) { /// Dependency loop detection when resolving field inits. field_inits_wip: bool, inits_resolved: bool, - _: u29 = 0, + is_reified: bool, + _: u28 = 0, }; }; @@ -3618,24 +3668,25 @@ pub const Tag = enum(u8) { /// Trailing: /// 0. captures_len: u32 // if `any_captures` /// 1. capture: CaptureValue // for each `captures_len` - /// 2. type: Index for each field in declared order - /// 3. if not is_tuple: + /// 2. type_hash: PackedU64 // if `is_reified` + /// 3. type: Index for each field in declared order + /// 4. if not is_tuple: /// names_map: MapIndex, /// name: NullTerminatedString // for each field in declared order - /// 4. if any_default_inits: + /// 5. if any_default_inits: /// init: Index // for each field in declared order - /// 5. if has_namespace: + /// 6. if has_namespace: /// namespace: NamespaceIndex - /// 6. if any_aligned_fields: + /// 7. if any_aligned_fields: /// align: Alignment // for each field in declared order - /// 7. if any_comptime_fields: + /// 8. if any_comptime_fields: /// field_is_comptime_bits: u32 // minimal number of u32s needed, LSB is field 0 - /// 8. if not is_extern: + /// 9. if not is_extern: /// field_index: RuntimeOrder // for each field in runtime order - /// 9. field_offset: u32 // for each field in declared order, undef until layout_resolved + /// 10. field_offset: u32 // for each field in declared order, undef until layout_resolved pub const TypeStruct = struct { decl: DeclIndex, - zir_index: TrackedInst.Index.Optional, + zir_index: TrackedInst.Index, fields_len: u32, flags: Flags, size: u32, @@ -3670,8 +3721,8 @@ pub const Tag = enum(u8) { // The types and all its fields have had their layout resolved. Even through pointer, // which `layout_resolved` does not ensure. fully_resolved: bool, - - _: u7 = 0, + is_reified: bool, + _: u6 = 0, }; }; @@ -3681,9 +3732,10 @@ pub const Tag = enum(u8) { /// The opaque's owner Decl. decl: DeclIndex, /// Contains the declarations inside this opaque. - namespace: NamespaceIndex, + namespace: OptionalNamespaceIndex, /// The index of the `opaque_decl` instruction. - zir_index: TrackedInst.Index.Optional, + zir_index: TrackedInst.Index, + /// `std.math.maxInt(u32)` indicates this type is reified. captures_len: u32, }; }; @@ -3992,12 +4044,15 @@ pub const Array = struct { }; /// Trailing: -/// 0. capture: CaptureValue // for each `captures_len` -/// 1. field name: NullTerminatedString for each fields_len; declaration order -/// 2. tag value: Index for each fields_len; declaration order +/// 0. owner_union: Index // if `zir_index == .none` +/// 1. capture: CaptureValue // for each `captures_len` +/// 2. type_hash: PackedU64 // if reified (`captures_len == std.math.maxInt(u32)`) +/// 3. field name: NullTerminatedString for each fields_len; declaration order +/// 4. tag value: Index for each fields_len; declaration order pub const EnumExplicit = struct { /// The Decl that corresponds to the enum itself. decl: DeclIndex, + /// `std.math.maxInt(u32)` indicates this type is reified. captures_len: u32, /// This may be `none` if there are no declarations. namespace: OptionalNamespaceIndex, @@ -4011,15 +4066,20 @@ pub const EnumExplicit = struct { /// If this is `none`, it means the trailing tag values are absent because /// they are auto-numbered. values_map: OptionalMapIndex, + /// `none` means this is a generated tag type. + /// There will be a trailing union type for which this is a tag. zir_index: TrackedInst.Index.Optional, }; /// Trailing: -/// 0. capture: CaptureValue // for each `captures_len` -/// 1. field name: NullTerminatedString for each fields_len; declaration order +/// 0. owner_union: Index // if `zir_index == .none` +/// 1. capture: CaptureValue // for each `captures_len` +/// 2. type_hash: PackedU64 // if reified (`captures_len == std.math.maxInt(u32)`) +/// 3. field name: NullTerminatedString for each fields_len; declaration order pub const EnumAuto = struct { /// The Decl that corresponds to the enum itself. decl: DeclIndex, + /// `std.math.maxInt(u32)` indicates this type is reified. captures_len: u32, /// This may be `none` if there are no declarations. namespace: OptionalNamespaceIndex, @@ -4029,6 +4089,8 @@ pub const EnumAuto = struct { fields_len: u32, /// Maps field names to declaration index. names_map: MapIndex, + /// `none` means this is a generated tag type. + /// There will be a trailing union type for which this is a tag. zir_index: TrackedInst.Index.Optional, }; @@ -4269,6 +4331,7 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key { const item = ip.items.get(@intFromEnum(index)); const data = item.data; return switch (item.tag) { + .removed => unreachable, .type_int_signed => .{ .int_type = .{ .signedness = .signed, @@ -4330,31 +4393,123 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key { .inferred_error_set_type = @enumFromInt(data), }, - .type_opaque => .{ .opaque_type = .{ - .decl = ip.extraData(Tag.TypeOpaque, data).decl, + .type_opaque => .{ .opaque_type = ns: { + const extra = ip.extraDataTrail(Tag.TypeOpaque, data); + if (extra.data.captures_len == std.math.maxInt(u32)) { + break :ns .{ .reified = .{ + .zir_index = extra.data.zir_index, + .type_hash = 0, + } }; + } + break :ns .{ .declared = .{ + .zir_index = extra.data.zir_index, + .captures = .{ .owned = .{ + .start = extra.end, + .len = extra.data.captures_len, + } }, + } }; } }, - .type_struct => .{ .struct_type = if (data == 0) .{ - .decl = .none, - } else .{ - .decl = ip.extraData(Tag.TypeStruct, data).decl.toOptional(), + .type_struct => .{ .struct_type = ns: { + if (data == 0) break :ns .empty_struct; + const extra = ip.extraDataTrail(Tag.TypeStruct, data); + if (extra.data.flags.is_reified) { + assert(!extra.data.flags.any_captures); + break :ns .{ .reified = .{ + .zir_index = extra.data.zir_index, + .type_hash = ip.extraData(PackedU64, extra.end).get(), + } }; + } + break :ns .{ .declared = .{ + .zir_index = extra.data.zir_index, + .captures = .{ .owned = if (extra.data.flags.any_captures) .{ + .start = extra.end + 1, + .len = ip.extra.items[extra.end], + } else .{ .start = 0, .len = 0 } }, + } }; } }, - .type_struct_packed, .type_struct_packed_inits => .{ .struct_type = .{ - .decl = ip.extraData(Tag.TypeStructPacked, data).decl.toOptional(), + .type_struct_packed, .type_struct_packed_inits => .{ .struct_type = ns: { + const extra = ip.extraDataTrail(Tag.TypeStructPacked, data); + if (extra.data.flags.is_reified) { + assert(!extra.data.flags.any_captures); + break :ns .{ .reified = .{ + .zir_index = extra.data.zir_index, + .type_hash = ip.extraData(PackedU64, extra.end).get(), + } }; + } + break :ns .{ .declared = .{ + .zir_index = extra.data.zir_index, + .captures = .{ .owned = if (extra.data.flags.any_captures) .{ + .start = extra.end + 1, + .len = ip.extra.items[extra.end], + } else .{ .start = 0, .len = 0 } }, + } }; } }, .type_struct_anon => .{ .anon_struct_type = extraTypeStructAnon(ip, data) }, .type_tuple_anon => .{ .anon_struct_type = extraTypeTupleAnon(ip, data) }, - .type_union => .{ .union_type = .{ - .decl = ip.extraData(Tag.TypeUnion, data).decl, + .type_union => .{ .union_type = ns: { + const extra = ip.extraDataTrail(Tag.TypeUnion, data); + if (extra.data.flags.is_reified) { + assert(!extra.data.flags.any_captures); + break :ns .{ .reified = .{ + .zir_index = extra.data.zir_index, + .type_hash = ip.extraData(PackedU64, extra.end).get(), + } }; + } + break :ns .{ .declared = .{ + .zir_index = extra.data.zir_index, + .captures = .{ .owned = if (extra.data.flags.any_captures) .{ + .start = extra.end + 1, + .len = ip.extra.items[extra.end], + } else .{ .start = 0, .len = 0 } }, + } }; } }, - .type_enum_auto => .{ .enum_type = .{ - .decl = ip.extraData(EnumAuto, data).decl, + .type_enum_auto => .{ .enum_type = ns: { + const extra = ip.extraDataTrail(EnumAuto, data); + const zir_index = extra.data.zir_index.unwrap() orelse { + assert(extra.data.captures_len == 0); + break :ns .{ .generated_tag = .{ + .union_type = @enumFromInt(ip.extra.items[extra.end]), + } }; + }; + if (extra.data.captures_len == std.math.maxInt(u32)) { + break :ns .{ .reified = .{ + .zir_index = zir_index, + .type_hash = ip.extraData(PackedU64, extra.end).get(), + } }; + } + break :ns .{ .declared = .{ + .zir_index = zir_index, + .captures = .{ .owned = .{ + .start = extra.end, + .len = extra.data.captures_len, + } }, + } }; } }, - .type_enum_explicit, .type_enum_nonexhaustive => .{ .enum_type = .{ - .decl = ip.extraData(EnumExplicit, data).decl, + .type_enum_explicit, .type_enum_nonexhaustive => .{ .enum_type = ns: { + const extra = ip.extraDataTrail(EnumExplicit, data); + const zir_index = extra.data.zir_index.unwrap() orelse { + assert(extra.data.captures_len == 0); + break :ns .{ .generated_tag = .{ + .union_type = @enumFromInt(ip.extra.items[extra.end]), + } }; + }; + if (extra.data.captures_len == std.math.maxInt(u32)) { + break :ns .{ .reified = .{ + .zir_index = zir_index, + .type_hash = ip.extraData(PackedU64, extra.end).get(), + } }; + } + break :ns .{ .declared = .{ + .zir_index = zir_index, + .captures = .{ .owned = .{ + .start = extra.end, + .len = extra.data.captures_len, + } }, + } }; } }, .type_function => .{ .func_type = ip.extraFuncType(data) }, @@ -4979,7 +5134,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { .union_type => unreachable, // use getUnionType() instead .opaque_type => unreachable, // use getOpaqueType() instead - .enum_type => unreachable, // use getEnum() or getIncompleteEnum() instead + .enum_type => unreachable, // use getEnumType() instead .func_type => unreachable, // use getFuncType() instead .extern_func => unreachable, // use getExternFunc() instead .func => unreachable, // use getFuncInstance() or getFuncDecl() instead @@ -5652,9 +5807,7 @@ pub const UnionTypeInit = struct { assumed_pointer_aligned: bool, alignment: Alignment, }, - decl: DeclIndex, - namespace: NamespaceIndex, - zir_index: TrackedInst.Index.Optional, + has_namespace: bool, fields_len: u32, enum_tag_ty: Index, /// May have length 0 which leaves the values unset until later. @@ -5663,23 +5816,50 @@ pub const UnionTypeInit = struct { /// The logic for `any_aligned_fields` is asserted to have been done before /// calling this function. field_aligns: []const Alignment, - captures: []const CaptureValue, + key: union(enum) { + declared: struct { + zir_index: TrackedInst.Index, + captures: []const CaptureValue, + }, + reified: struct { + zir_index: TrackedInst.Index, + type_hash: u64, + }, + }, }; -pub fn getUnionType(ip: *InternPool, gpa: Allocator, ini: UnionTypeInit) Allocator.Error!Index { - const prev_extra_len = ip.extra.items.len; +pub fn getUnionType(ip: *InternPool, gpa: Allocator, ini: UnionTypeInit) Allocator.Error!WipNamespaceType.Result { + const adapter: KeyAdapter = .{ .intern_pool = ip }; + const gop = try ip.map.getOrPutAdapted(gpa, Key{ .union_type = switch (ini.key) { + .declared => |d| .{ .declared = .{ + .zir_index = d.zir_index, + .captures = .{ .external = d.captures }, + } }, + .reified => |r| .{ .reified = .{ + .zir_index = r.zir_index, + .type_hash = r.type_hash, + } }, + } }, adapter); + if (gop.found_existing) return .{ .existing = @enumFromInt(gop.index) }; + errdefer _ = ip.map.pop(); + 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 + - @intFromBool(ini.captures.len != 0) + // captures_len - ini.captures.len + // captures + // TODO: fmt bug + // zig fmt: off + switch (ini.key) { + .declared => |d| @intFromBool(d.captures.len != 0) + d.captures.len, + .reified => 2, // type_hash: PackedU64 + } + + // zig fmt: on ini.fields_len + // field types align_elements_len); try ip.items.ensureUnusedCapacity(gpa, 1); - const union_type_extra_index = ip.addExtraAssumeCapacity(Tag.TypeUnion{ + const extra_index = ip.addExtraAssumeCapacity(Tag.TypeUnion{ .flags = .{ - .any_captures = ini.captures.len != 0, + .any_captures = ini.key == .declared and ini.key.declared.captures.len != 0, .runtime_tag = ini.flags.runtime_tag, .any_aligned_fields = ini.flags.any_aligned_fields, .layout = ini.flags.layout, @@ -5688,19 +5868,30 @@ pub fn getUnionType(ip: *InternPool, gpa: Allocator, ini: UnionTypeInit) Allocat .assumed_runtime_bits = ini.flags.assumed_runtime_bits, .assumed_pointer_aligned = ini.flags.assumed_pointer_aligned, .alignment = ini.flags.alignment, + .is_reified = ini.key == .reified, }, .fields_len = ini.fields_len, .size = std.math.maxInt(u32), .padding = std.math.maxInt(u32), - .decl = ini.decl, - .namespace = ini.namespace, + .decl = undefined, // set by `finish` + .namespace = .none, // set by `finish` .tag_ty = ini.enum_tag_ty, - .zir_index = ini.zir_index, + .zir_index = switch (ini.key) { + inline else => |x| x.zir_index, + }, + }); + + ip.items.appendAssumeCapacity(.{ + .tag = .type_union, + .data = extra_index, }); - if (ini.captures.len != 0) { - ip.extra.appendAssumeCapacity(@intCast(ini.captures.len)); - ip.extra.appendSliceAssumeCapacity(@ptrCast(ini.captures)); + switch (ini.key) { + .declared => |d| if (d.captures.len != 0) { + ip.extra.appendAssumeCapacity(@intCast(d.captures.len)); + ip.extra.appendSliceAssumeCapacity(@ptrCast(d.captures)); + }, + .reified => |r| _ = ip.addExtraAssumeCapacity(PackedU64.init(r.type_hash)), } // field types @@ -5725,27 +5916,41 @@ pub fn getUnionType(ip: *InternPool, gpa: Allocator, ini: UnionTypeInit) Allocat assert(ini.field_aligns.len == 0); } - const adapter: KeyAdapter = .{ .intern_pool = ip }; - const gop = try ip.map.getOrPutAdapted(gpa, Key{ - .union_type = .{ .decl = ini.decl }, - }, adapter); - if (gop.found_existing) { - ip.extra.items.len = prev_extra_len; - return @enumFromInt(gop.index); + return .{ .wip = .{ + .index = @enumFromInt(ip.items.len - 1), + .decl_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeUnion, "decl").?, + .namespace_extra_index = if (ini.has_namespace) + extra_index + std.meta.fieldIndex(Tag.TypeUnion, "namespace").? + else + null, + } }; +} + +pub const WipNamespaceType = struct { + index: Index, + decl_extra_index: u32, + namespace_extra_index: ?u32, + pub fn finish(wip: WipNamespaceType, ip: *InternPool, decl: DeclIndex, namespace: OptionalNamespaceIndex) Index { + ip.extra.items[wip.decl_extra_index] = @intFromEnum(decl); + if (wip.namespace_extra_index) |i| { + ip.extra.items[i] = @intFromEnum(namespace.unwrap().?); + } else { + assert(namespace == .none); + } + return wip.index; + } + pub fn cancel(wip: WipNamespaceType, ip: *InternPool) void { + ip.remove(wip.index); } - ip.items.appendAssumeCapacity(.{ - .tag = .type_union, - .data = union_type_extra_index, - }); - return @enumFromInt(ip.items.len - 1); -} + pub const Result = union(enum) { + wip: WipNamespaceType, + existing: Index, + }; +}; pub const StructTypeInit = struct { - decl: DeclIndex, - namespace: OptionalNamespaceIndex, layout: std.builtin.Type.ContainerLayout, - zir_index: TrackedInst.Index.Optional, fields_len: u32, known_non_opv: bool, requires_comptime: RequiresComptime, @@ -5754,61 +5959,101 @@ pub const StructTypeInit = struct { any_default_inits: bool, inits_resolved: bool, any_aligned_fields: bool, - captures: []const CaptureValue, + has_namespace: bool, + key: union(enum) { + declared: struct { + zir_index: TrackedInst.Index, + captures: []const CaptureValue, + }, + reified: struct { + zir_index: TrackedInst.Index, + type_hash: u64, + }, + }, }; pub fn getStructType( ip: *InternPool, gpa: Allocator, ini: StructTypeInit, -) Allocator.Error!Index { +) Allocator.Error!WipNamespaceType.Result { const adapter: KeyAdapter = .{ .intern_pool = ip }; - const key: Key = .{ - .struct_type = .{ .decl = ini.decl.toOptional() }, - }; + const key: Key = .{ .struct_type = switch (ini.key) { + .declared => |d| .{ .declared = .{ + .zir_index = d.zir_index, + .captures = .{ .external = d.captures }, + } }, + .reified => |r| .{ .reified = .{ + .zir_index = r.zir_index, + .type_hash = r.type_hash, + } }, + } }; const gop = try ip.map.getOrPutAdapted(gpa, key, adapter); - if (gop.found_existing) return @enumFromInt(gop.index); + if (gop.found_existing) return .{ .existing = @enumFromInt(gop.index) }; errdefer _ = ip.map.pop(); const names_map = try ip.addMap(gpa, ini.fields_len); errdefer _ = ip.maps.pop(); + const zir_index = switch (ini.key) { + inline else => |x| x.zir_index, + }; + const is_extern = switch (ini.layout) { .Auto => false, .Extern => true, .Packed => { try ip.extra.ensureUnusedCapacity(gpa, @typeInfo(Tag.TypeStructPacked).Struct.fields.len + - @intFromBool(ini.captures.len != 0) + // captures_len - ini.captures.len + // captures + // TODO: fmt bug + // zig fmt: off + switch (ini.key) { + .declared => |d| @intFromBool(d.captures.len != 0) + d.captures.len, + .reified => 2, // type_hash: PackedU64 + } + + // zig fmt: on ini.fields_len + // types ini.fields_len + // names ini.fields_len); // inits + const extra_index = ip.addExtraAssumeCapacity(Tag.TypeStructPacked{ + .decl = undefined, // set by `finish` + .zir_index = zir_index, + .fields_len = ini.fields_len, + .namespace = .none, + .backing_int_ty = .none, + .names_map = names_map, + .flags = .{ + .any_captures = ini.key == .declared and ini.key.declared.captures.len != 0, + .field_inits_wip = false, + .inits_resolved = ini.inits_resolved, + .is_reified = ini.key == .reified, + }, + }); try ip.items.append(gpa, .{ .tag = if (ini.any_default_inits) .type_struct_packed_inits else .type_struct_packed, - .data = ip.addExtraAssumeCapacity(Tag.TypeStructPacked{ - .decl = ini.decl, - .zir_index = ini.zir_index, - .fields_len = ini.fields_len, - .namespace = ini.namespace, - .backing_int_ty = .none, - .names_map = names_map, - .flags = .{ - .any_captures = ini.captures.len != 0, - .field_inits_wip = false, - .inits_resolved = ini.inits_resolved, - }, - }), + .data = extra_index, }); - if (ini.captures.len != 0) { - ip.extra.appendAssumeCapacity(@intCast(ini.captures.len)); - ip.extra.appendSliceAssumeCapacity(@ptrCast(ini.captures)); + switch (ini.key) { + .declared => |d| if (d.captures.len != 0) { + ip.extra.appendAssumeCapacity(@intCast(d.captures.len)); + ip.extra.appendSliceAssumeCapacity(@ptrCast(d.captures)); + }, + .reified => |r| { + _ = ip.addExtraAssumeCapacity(PackedU64.init(r.type_hash)); + }, } ip.extra.appendNTimesAssumeCapacity(@intFromEnum(Index.none), ini.fields_len); ip.extra.appendNTimesAssumeCapacity(@intFromEnum(OptionalNullTerminatedString.none), ini.fields_len); if (ini.any_default_inits) { ip.extra.appendNTimesAssumeCapacity(@intFromEnum(Index.none), ini.fields_len); } - return @enumFromInt(ip.items.len - 1); + return .{ .wip = .{ + .index = @enumFromInt(ip.items.len - 1), + .decl_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeStructPacked, "decl").?, + .namespace_extra_index = if (ini.has_namespace) + extra_index + std.meta.fieldIndex(Tag.TypeStructPacked, "namespace").? + else + null, + } }; }, }; @@ -5817,44 +6062,56 @@ pub fn getStructType( const comptime_elements_len = if (ini.any_comptime_fields) (ini.fields_len + 31) / 32 else 0; try ip.extra.ensureUnusedCapacity(gpa, @typeInfo(Tag.TypeStruct).Struct.fields.len + - @intFromBool(ini.captures.len != 0) + // captures_len - ini.captures.len + // captures + // TODO: fmt bug + // zig fmt: off + switch (ini.key) { + .declared => |d| @intFromBool(d.captures.len != 0) + d.captures.len, + .reified => 2, // type_hash: PackedU64 + } + + // zig fmt: on (ini.fields_len * 5) + // types, names, inits, runtime order, offsets align_elements_len + comptime_elements_len + 2); // names_map + namespace + const extra_index = ip.addExtraAssumeCapacity(Tag.TypeStruct{ + .decl = undefined, // set by `finish` + .zir_index = zir_index, + .fields_len = ini.fields_len, + .size = std.math.maxInt(u32), + .flags = .{ + .any_captures = ini.key == .declared and ini.key.declared.captures.len != 0, + .is_extern = is_extern, + .known_non_opv = ini.known_non_opv, + .requires_comptime = ini.requires_comptime, + .is_tuple = ini.is_tuple, + .assumed_runtime_bits = false, + .assumed_pointer_aligned = false, + .has_namespace = ini.has_namespace, + .any_comptime_fields = ini.any_comptime_fields, + .any_default_inits = ini.any_default_inits, + .any_aligned_fields = ini.any_aligned_fields, + .alignment = .none, + .alignment_wip = false, + .field_types_wip = false, + .layout_wip = false, + .layout_resolved = false, + .field_inits_wip = false, + .inits_resolved = ini.inits_resolved, + .fully_resolved = false, + .is_reified = ini.key == .reified, + }, + }); try ip.items.append(gpa, .{ .tag = .type_struct, - .data = ip.addExtraAssumeCapacity(Tag.TypeStruct{ - .decl = ini.decl, - .zir_index = ini.zir_index, - .fields_len = ini.fields_len, - .size = std.math.maxInt(u32), - .flags = .{ - .any_captures = ini.captures.len != 0, - .is_extern = is_extern, - .known_non_opv = ini.known_non_opv, - .requires_comptime = ini.requires_comptime, - .is_tuple = ini.is_tuple, - .assumed_runtime_bits = false, - .assumed_pointer_aligned = false, - .has_namespace = ini.namespace != .none, - .any_comptime_fields = ini.any_comptime_fields, - .any_default_inits = ini.any_default_inits, - .any_aligned_fields = ini.any_aligned_fields, - .alignment = .none, - .alignment_wip = false, - .field_types_wip = false, - .layout_wip = false, - .layout_resolved = false, - .field_inits_wip = false, - .inits_resolved = ini.inits_resolved, - .fully_resolved = false, - }, - }), + .data = extra_index, }); - if (ini.captures.len != 0) { - ip.extra.appendAssumeCapacity(@intCast(ini.captures.len)); - ip.extra.appendSliceAssumeCapacity(@ptrCast(ini.captures)); + switch (ini.key) { + .declared => |d| if (d.captures.len != 0) { + ip.extra.appendAssumeCapacity(@intCast(d.captures.len)); + ip.extra.appendSliceAssumeCapacity(@ptrCast(d.captures)); + }, + .reified => |r| { + _ = ip.addExtraAssumeCapacity(PackedU64.init(r.type_hash)); + }, } ip.extra.appendNTimesAssumeCapacity(@intFromEnum(Index.none), ini.fields_len); if (!ini.is_tuple) { @@ -5864,9 +6121,10 @@ pub fn getStructType( if (ini.any_default_inits) { ip.extra.appendNTimesAssumeCapacity(@intFromEnum(Index.none), ini.fields_len); } - if (ini.namespace.unwrap()) |namespace| { - ip.extra.appendAssumeCapacity(@intFromEnum(namespace)); - } + const namespace_extra_index: ?u32 = if (ini.has_namespace) i: { + ip.extra.appendAssumeCapacity(undefined); // set by `finish` + break :i @intCast(ip.extra.items.len - 1); + } else null; if (ini.any_aligned_fields) { ip.extra.appendNTimesAssumeCapacity(align_element, align_elements_len); } @@ -5877,7 +6135,11 @@ pub fn getStructType( ip.extra.appendNTimesAssumeCapacity(@intFromEnum(LoadedStructType.RuntimeOrder.unresolved), ini.fields_len); } ip.extra.appendNTimesAssumeCapacity(std.math.maxInt(u32), ini.fields_len); - return @enumFromInt(ip.items.len - 1); + return .{ .wip = .{ + .index = @enumFromInt(ip.items.len - 1), + .decl_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeStruct, "decl").?, + .namespace_extra_index = namespace_extra_index, + } }; } pub const AnonStructTypeInit = struct { @@ -6513,282 +6775,399 @@ fn finishFuncInstance( return func_index; } -/// Provides API for completing an enum type after calling `getIncompleteEnum`. -pub const IncompleteEnumType = struct { +pub const EnumTypeInit = struct { + has_namespace: bool, + has_values: bool, + tag_mode: LoadedEnumType.TagMode, + fields_len: u32, + key: union(enum) { + declared: struct { + zir_index: TrackedInst.Index, + captures: []const CaptureValue, + }, + reified: struct { + zir_index: TrackedInst.Index, + type_hash: u64, + }, + }, +}; + +pub const WipEnumType = struct { index: Index, tag_ty_index: u32, + decl_index: u32, + namespace_index: ?u32, names_map: MapIndex, names_start: u32, values_map: OptionalMapIndex, values_start: u32, + expected_fields_len: if (std.debug.runtime_safety) u32 else void, - pub fn setTagType(self: @This(), ip: *InternPool, tag_ty: Index) void { - assert(tag_ty == .noreturn_type or ip.isIntegerType(tag_ty)); - ip.extra.items[self.tag_ty_index] = @intFromEnum(tag_ty); - } - - /// Returns the already-existing field with the same name, if any. - pub fn addFieldName( - self: @This(), + pub fn prepare( + wip: WipEnumType, ip: *InternPool, - name: NullTerminatedString, - ) ?u32 { - return ip.addFieldName(self.names_map, self.names_start, name); + decl: DeclIndex, + namespace: OptionalNamespaceIndex, + tag_ty: Index, + ) void { + assert(ip.isIntegerType(tag_ty)); + ip.extra.items[wip.tag_ty_index] = @intFromEnum(tag_ty); + ip.extra.items[wip.decl_index] = @intFromEnum(decl); + if (wip.namespace_index) |i| { + ip.extra.items[i] = @intFromEnum(namespace.unwrap().?); + } else { + assert(namespace == .none); + } } - /// Returns the already-existing field with the same value, if any. - /// Make sure the type of the value has the integer tag type of the enum. - pub fn addFieldValue( - self: @This(), - ip: *InternPool, - value: Index, - ) ?u32 { - assert(ip.typeOf(value) == @as(Index, @enumFromInt(ip.extra.items[self.tag_ty_index]))); - const map = &ip.maps.items[@intFromEnum(self.values_map.unwrap().?)]; + pub const FieldConflict = struct { + kind: enum { name, value }, + prev_field_idx: u32, + }; + + /// Returns the already-existing field with the same name or value, if any. + /// If the enum is automatially numbered, `value` must be `.none`. + /// Otherwise, the type of `value` must be the integer tag type of the enum. + pub fn nextField(wip: WipEnumType, ip: *InternPool, name: NullTerminatedString, value: Index) ?FieldConflict { + if (ip.addFieldName(wip.names_map, wip.names_start, name)) |conflict| { + return .{ .kind = .name, .prev_field_idx = conflict }; + } + if (value == .none) { + assert(wip.values_map == .none); + return null; + } + assert(ip.typeOf(value) == @as(Index, @enumFromInt(ip.extra.items[wip.tag_ty_index]))); + const map = &ip.maps.items[@intFromEnum(wip.values_map.unwrap().?)]; const field_index = map.count(); - const indexes = ip.extra.items[self.values_start..][0..field_index]; + const indexes = ip.extra.items[wip.values_start..][0..field_index]; const adapter: Index.Adapter = .{ .indexes = @ptrCast(indexes) }; const gop = map.getOrPutAssumeCapacityAdapted(value, adapter); - if (gop.found_existing) return @intCast(gop.index); - ip.extra.items[self.values_start + field_index] = @intFromEnum(value); + if (gop.found_existing) { + return .{ .kind = .value, .prev_field_idx = @intCast(gop.index) }; + } + ip.extra.items[wip.values_start + field_index] = @intFromEnum(value); return null; } -}; -/// This is used to create an enum type in the `InternPool`, with the ability -/// to update the tag type, field names, and field values later. -pub fn getIncompleteEnum( - ip: *InternPool, - gpa: Allocator, - enum_type: Key.IncompleteEnumType, -) Allocator.Error!IncompleteEnumType { - switch (enum_type.tag_mode) { - .auto => return getIncompleteEnumAuto(ip, gpa, enum_type), - .explicit => return getIncompleteEnumExplicit(ip, gpa, enum_type, .type_enum_explicit), - .nonexhaustive => return getIncompleteEnumExplicit(ip, gpa, enum_type, .type_enum_nonexhaustive), + pub fn finish(wip: WipEnumType, ip: *InternPool) Index { + if (std.debug.runtime_safety) { + const names_map = &ip.maps.items[@intFromEnum(wip.names_map)]; + assert(names_map.count() == wip.expected_fields_len); + if (wip.values_map.unwrap()) |v| { + const values_map = &ip.maps.items[@intFromEnum(v)]; + assert(values_map.count() == wip.expected_fields_len); + } + } + return wip.index; } -} -fn getIncompleteEnumAuto( - ip: *InternPool, - gpa: Allocator, - enum_type: Key.IncompleteEnumType, -) Allocator.Error!IncompleteEnumType { - const int_tag_type = if (enum_type.tag_ty != .none) - enum_type.tag_ty - else - try ip.get(gpa, .{ .int_type = .{ - .bits = if (enum_type.fields_len == 0) 0 else std.math.log2_int_ceil(u32, enum_type.fields_len), - .signedness = .unsigned, - } }); - - // We must keep the map in sync with `items`. The hash and equality functions - // for enum types only look at the decl field, which is present even in - // an `IncompleteEnumType`. - const adapter: KeyAdapter = .{ .intern_pool = ip }; - const gop = try ip.map.getOrPutAdapted(gpa, enum_type.toKey(), adapter); - assert(!gop.found_existing); - - const names_map = try ip.addMap(gpa, enum_type.fields_len); - - const extra_fields_len: u32 = @typeInfo(EnumAuto).Struct.fields.len; - try ip.extra.ensureUnusedCapacity(gpa, extra_fields_len + enum_type.captures.len + enum_type.fields_len); - try ip.items.ensureUnusedCapacity(gpa, 1); - - const extra_index = ip.addExtraAssumeCapacity(EnumAuto{ - .decl = enum_type.decl, - .captures_len = @intCast(enum_type.captures.len), - .namespace = enum_type.namespace, - .int_tag_type = int_tag_type, - .names_map = names_map, - .fields_len = enum_type.fields_len, - .zir_index = enum_type.zir_index, - }); + pub fn cancel(wip: WipEnumType, ip: *InternPool) void { + ip.remove(wip.index); + } - ip.items.appendAssumeCapacity(.{ - .tag = .type_enum_auto, - .data = extra_index, - }); - ip.extra.appendSliceAssumeCapacity(@ptrCast(enum_type.captures)); - ip.extra.appendNTimesAssumeCapacity(@intFromEnum(Index.none), enum_type.fields_len); - return .{ - .index = @enumFromInt(ip.items.len - 1), - .tag_ty_index = extra_index + std.meta.fieldIndex(EnumAuto, "int_tag_type").?, - .names_map = names_map, - .names_start = extra_index + extra_fields_len, - .values_map = .none, - .values_start = undefined, + pub const Result = union(enum) { + wip: WipEnumType, + existing: Index, }; -} +}; -fn getIncompleteEnumExplicit( +pub fn getEnumType( ip: *InternPool, gpa: Allocator, - enum_type: Key.IncompleteEnumType, - tag: Tag, -) Allocator.Error!IncompleteEnumType { - // We must keep the map in sync with `items`. The hash and equality functions - // for enum types only look at the decl field, which is present even in - // an `IncompleteEnumType`. + ini: EnumTypeInit, +) Allocator.Error!WipEnumType.Result { const adapter: KeyAdapter = .{ .intern_pool = ip }; - const gop = try ip.map.getOrPutAdapted(gpa, enum_type.toKey(), adapter); - assert(!gop.found_existing); - - const names_map = try ip.addMap(gpa, enum_type.fields_len); - const values_map: OptionalMapIndex = if (!enum_type.has_values) .none else m: { - const values_map = try ip.addMap(gpa, enum_type.fields_len); - break :m values_map.toOptional(); - }; - - const reserved_len = enum_type.fields_len + - if (enum_type.has_values) enum_type.fields_len else 0; + const gop = try ip.map.getOrPutAdapted(gpa, Key{ .enum_type = switch (ini.key) { + .declared => |d| .{ .declared = .{ + .zir_index = d.zir_index, + .captures = .{ .external = d.captures }, + } }, + .reified => |r| .{ .reified = .{ + .zir_index = r.zir_index, + .type_hash = r.type_hash, + } }, + } }, adapter); + if (gop.found_existing) return .{ .existing = @enumFromInt(gop.index) }; + assert(gop.index == ip.items.len); + errdefer _ = ip.map.pop(); - const extra_fields_len: u32 = @typeInfo(EnumExplicit).Struct.fields.len; - try ip.extra.ensureUnusedCapacity(gpa, extra_fields_len + enum_type.captures.len + reserved_len); try ip.items.ensureUnusedCapacity(gpa, 1); - const extra_index = ip.addExtraAssumeCapacity(EnumExplicit{ - .decl = enum_type.decl, - .captures_len = @intCast(enum_type.captures.len), - .namespace = enum_type.namespace, - .int_tag_type = enum_type.tag_ty, - .fields_len = enum_type.fields_len, - .names_map = names_map, - .values_map = values_map, - .zir_index = enum_type.zir_index, - }); + const names_map = try ip.addMap(gpa, ini.fields_len); + errdefer _ = ip.maps.pop(); - ip.items.appendAssumeCapacity(.{ - .tag = tag, - .data = extra_index, - }); - ip.extra.appendSliceAssumeCapacity(@ptrCast(enum_type.captures)); - // This is both fields and values (if present). - ip.extra.appendNTimesAssumeCapacity(@intFromEnum(Index.none), reserved_len); - return .{ - .index = @enumFromInt(ip.items.len - 1), - .tag_ty_index = extra_index + std.meta.fieldIndex(EnumExplicit, "int_tag_type").?, - .names_map = names_map, - .names_start = extra_index + extra_fields_len, - .values_map = values_map, - .values_start = extra_index + extra_fields_len + enum_type.fields_len, - }; + switch (ini.tag_mode) { + .auto => { + assert(!ini.has_values); + try ip.extra.ensureUnusedCapacity(gpa, @typeInfo(EnumAuto).Struct.fields.len + + // TODO: fmt bug + // zig fmt: off + switch (ini.key) { + .declared => |d| d.captures.len, + .reified => 2, // type_hash: PackedU64 + } + + // zig fmt: on + ini.fields_len); // field types + + const extra_index = ip.addExtraAssumeCapacity(EnumAuto{ + .decl = undefined, // set by `prepare` + .captures_len = switch (ini.key) { + .declared => |d| @intCast(d.captures.len), + .reified => std.math.maxInt(u32), + }, + .namespace = .none, + .int_tag_type = .none, // set by `prepare` + .fields_len = ini.fields_len, + .names_map = names_map, + .zir_index = switch (ini.key) { + inline else => |x| x.zir_index, + }.toOptional(), + }); + ip.items.appendAssumeCapacity(.{ + .tag = .type_enum_auto, + .data = extra_index, + }); + switch (ini.key) { + .declared => |d| ip.extra.appendSliceAssumeCapacity(@ptrCast(d.captures)), + .reified => |r| _ = ip.addExtraAssumeCapacity(PackedU64.init(r.type_hash)), + } + const names_start = ip.extra.items.len; + ip.extra.appendNTimesAssumeCapacity(undefined, ini.fields_len); + return .{ .wip = .{ + .index = @enumFromInt(gop.index), + .tag_ty_index = extra_index + std.meta.fieldIndex(EnumAuto, "int_tag_type").?, + .decl_index = extra_index + std.meta.fieldIndex(EnumAuto, "decl").?, + .namespace_index = if (ini.has_namespace) extra_index + std.meta.fieldIndex(EnumAuto, "namespace").? else null, + .names_map = names_map, + .names_start = @intCast(names_start), + .values_map = .none, + .values_start = undefined, + .expected_fields_len = if (std.debug.runtime_safety) ini.fields_len else {}, + } }; + }, + .explicit, .nonexhaustive => { + const values_map: OptionalMapIndex = if (!ini.has_values) .none else m: { + const values_map = try ip.addMap(gpa, ini.fields_len); + break :m values_map.toOptional(); + }; + errdefer if (ini.has_values) { + _ = ip.map.pop(); + }; + + try ip.extra.ensureUnusedCapacity(gpa, @typeInfo(EnumExplicit).Struct.fields.len + + // TODO: fmt bug + // zig fmt: off + switch (ini.key) { + .declared => |d| d.captures.len, + .reified => 2, // type_hash: PackedU64 + } + + // zig fmt: on + ini.fields_len + // field types + ini.fields_len * @intFromBool(ini.has_values)); // field values + + const extra_index = ip.addExtraAssumeCapacity(EnumExplicit{ + .decl = undefined, // set by `prepare` + .captures_len = switch (ini.key) { + .declared => |d| @intCast(d.captures.len), + .reified => std.math.maxInt(u32), + }, + .namespace = .none, + .int_tag_type = .none, // set by `prepare` + .fields_len = ini.fields_len, + .names_map = names_map, + .values_map = values_map, + .zir_index = switch (ini.key) { + inline else => |x| x.zir_index, + }.toOptional(), + }); + ip.items.appendAssumeCapacity(.{ + .tag = switch (ini.tag_mode) { + .auto => unreachable, + .explicit => .type_enum_explicit, + .nonexhaustive => .type_enum_nonexhaustive, + }, + .data = extra_index, + }); + switch (ini.key) { + .declared => |d| ip.extra.appendSliceAssumeCapacity(@ptrCast(d.captures)), + .reified => |r| _ = ip.addExtraAssumeCapacity(PackedU64.init(r.type_hash)), + } + const names_start = ip.extra.items.len; + ip.extra.appendNTimesAssumeCapacity(undefined, ini.fields_len); + const values_start = ip.extra.items.len; + if (ini.has_values) { + ip.extra.appendNTimesAssumeCapacity(undefined, ini.fields_len); + } + return .{ .wip = .{ + .index = @enumFromInt(gop.index), + .tag_ty_index = extra_index + std.meta.fieldIndex(EnumAuto, "int_tag_type").?, + .decl_index = extra_index + std.meta.fieldIndex(EnumAuto, "decl").?, + .namespace_index = if (ini.has_namespace) extra_index + std.meta.fieldIndex(EnumAuto, "namespace").? else null, + .names_map = names_map, + .names_start = @intCast(names_start), + .values_map = values_map, + .values_start = @intCast(values_start), + .expected_fields_len = if (std.debug.runtime_safety) ini.fields_len else {}, + } }; + }, + } } -pub const GetEnumInit = struct { +const GeneratedTagEnumTypeInit = struct { decl: DeclIndex, - namespace: OptionalNamespaceIndex, + owner_union_ty: Index, tag_ty: Index, names: []const NullTerminatedString, values: []const Index, tag_mode: LoadedEnumType.TagMode, - zir_index: TrackedInst.Index.Optional, - captures: []const CaptureValue, }; -pub fn getEnum(ip: *InternPool, gpa: Allocator, ini: GetEnumInit) Allocator.Error!Index { - const adapter: KeyAdapter = .{ .intern_pool = ip }; - const gop = try ip.map.getOrPutAdapted(gpa, Key{ - .enum_type = .{ .decl = ini.decl }, - }, adapter); - if (gop.found_existing) return @enumFromInt(gop.index); - errdefer _ = ip.map.pop(); +/// Creates an enum type which was automatically-generated as the tag type of a +/// `union` with no explicit tag type. Since this is only called once per union +/// type, it asserts that no matching type yet exists. +pub fn getGeneratedTagEnumType(ip: *InternPool, gpa: Allocator, ini: GeneratedTagEnumTypeInit) Allocator.Error!Index { + assert(ip.isUnion(ini.owner_union_ty)); + assert(ip.isIntegerType(ini.tag_ty)); + for (ini.values) |val| assert(ip.typeOf(val) == ini.tag_ty); + + try ip.map.ensureUnusedCapacity(gpa, 1); try ip.items.ensureUnusedCapacity(gpa, 1); - assert(ini.tag_ty == .noreturn_type or ip.isIntegerType(ini.tag_ty)); - for (ini.values) |value| assert(ip.typeOf(value) == ini.tag_ty); + const names_map = try ip.addMap(gpa, ini.names.len); + errdefer _ = ip.maps.pop(); + ip.addStringsToMap(names_map, ini.names); + + const fields_len: u32 = @intCast(ini.names.len); switch (ini.tag_mode) { .auto => { - const names_map = try ip.addMap(gpa, ini.names.len); - addStringsToMap(ip, names_map, ini.names); - - const fields_len: u32 = @intCast(ini.names.len); try ip.extra.ensureUnusedCapacity(gpa, @typeInfo(EnumAuto).Struct.fields.len + - ini.captures.len + fields_len); + 1 + // owner_union + fields_len); // field names ip.items.appendAssumeCapacity(.{ .tag = .type_enum_auto, .data = ip.addExtraAssumeCapacity(EnumAuto{ .decl = ini.decl, - .captures_len = @intCast(ini.captures.len), - .namespace = ini.namespace, + .captures_len = 0, + .namespace = .none, .int_tag_type = ini.tag_ty, + .fields_len = fields_len, .names_map = names_map, + .zir_index = .none, + }), + }); + ip.extra.appendAssumeCapacity(@intFromEnum(ini.owner_union_ty)); + ip.extra.appendSliceAssumeCapacity(@ptrCast(ini.names)); + }, + .explicit, .nonexhaustive => { + try ip.extra.ensureUnusedCapacity(gpa, @typeInfo(EnumExplicit).Struct.fields.len + + 1 + // owner_union + fields_len + // field names + ini.values.len); // field values + + const values_map: OptionalMapIndex = if (ini.values.len != 0) m: { + const map = try ip.addMap(gpa, ini.values.len); + addIndexesToMap(ip, map, ini.values); + break :m map.toOptional(); + } else .none; + // We don't clean up the values map on error! + errdefer @compileError("error path leaks values_map"); + + ip.items.appendAssumeCapacity(.{ + .tag = switch (ini.tag_mode) { + .explicit => .type_enum_explicit, + .nonexhaustive => .type_enum_nonexhaustive, + .auto => unreachable, + }, + .data = ip.addExtraAssumeCapacity(EnumExplicit{ + .decl = ini.decl, + .captures_len = 0, + .namespace = .none, + .int_tag_type = ini.tag_ty, .fields_len = fields_len, - .zir_index = ini.zir_index, + .names_map = names_map, + .values_map = values_map, + .zir_index = .none, }), }); - ip.extra.appendSliceAssumeCapacity(@ptrCast(ini.captures)); + ip.extra.appendAssumeCapacity(@intFromEnum(ini.owner_union_ty)); ip.extra.appendSliceAssumeCapacity(@ptrCast(ini.names)); - return @enumFromInt(ip.items.len - 1); + ip.extra.appendSliceAssumeCapacity(@ptrCast(ini.values)); }, - .explicit => return finishGetEnum(ip, gpa, ini, .type_enum_explicit), - .nonexhaustive => return finishGetEnum(ip, gpa, ini, .type_enum_nonexhaustive), } -} - -fn finishGetEnum( - ip: *InternPool, - gpa: Allocator, - ini: GetEnumInit, - tag: Tag, -) Allocator.Error!Index { - const names_map = try ip.addMap(gpa, ini.names.len); - addStringsToMap(ip, names_map, ini.names); + // Same as above + errdefer @compileError("error path leaks values_map and extra data"); - const values_map: OptionalMapIndex = if (ini.values.len == 0) .none else m: { - const values_map = try ip.addMap(gpa, ini.values.len); - addIndexesToMap(ip, values_map, ini.values); - break :m values_map.toOptional(); - }; - const fields_len: u32 = @intCast(ini.names.len); - try ip.extra.ensureUnusedCapacity(gpa, @typeInfo(EnumExplicit).Struct.fields.len + - ini.captures.len + fields_len); - ip.items.appendAssumeCapacity(.{ - .tag = tag, - .data = ip.addExtraAssumeCapacity(EnumExplicit{ - .decl = ini.decl, - .captures_len = @intCast(ini.captures.len), - .namespace = ini.namespace, - .int_tag_type = ini.tag_ty, - .fields_len = fields_len, - .names_map = names_map, - .values_map = values_map, - .zir_index = ini.zir_index, - }), - }); - ip.extra.appendSliceAssumeCapacity(@ptrCast(ini.captures)); - ip.extra.appendSliceAssumeCapacity(@ptrCast(ini.names)); - ip.extra.appendSliceAssumeCapacity(@ptrCast(ini.values)); - return @enumFromInt(ip.items.len - 1); + // Capacity for this was ensured earlier + const adapter: KeyAdapter = .{ .intern_pool = ip }; + const gop = ip.map.getOrPutAssumeCapacityAdapted(Key{ .enum_type = .{ + .generated_tag = .{ .union_type = ini.owner_union_ty }, + } }, adapter); + assert(!gop.found_existing); + assert(gop.index == ip.items.len - 1); + return @enumFromInt(gop.index); } pub const OpaqueTypeIni = struct { - decl: DeclIndex, - namespace: NamespaceIndex, - zir_index: TrackedInst.Index.Optional, - captures: []const CaptureValue, + has_namespace: bool, + key: union(enum) { + declared: struct { + zir_index: TrackedInst.Index, + captures: []const CaptureValue, + }, + reified: struct { + zir_index: TrackedInst.Index, + // No type hash since reifid opaques have no data other than the `@Type` location + }, + }, }; -pub fn getOpaqueType(ip: *InternPool, gpa: Allocator, ini: OpaqueTypeIni) Allocator.Error!Index { +pub fn getOpaqueType(ip: *InternPool, gpa: Allocator, ini: OpaqueTypeIni) Allocator.Error!WipNamespaceType.Result { const adapter: KeyAdapter = .{ .intern_pool = ip }; - try ip.extra.ensureUnusedCapacity(gpa, @typeInfo(LoadedOpaqueType).Struct.fields.len + ini.captures.len); + const gop = try ip.map.getOrPutAdapted(gpa, Key{ .opaque_type = switch (ini.key) { + .declared => |d| .{ .declared = .{ + .zir_index = d.zir_index, + .captures = .{ .external = d.captures }, + } }, + .reified => |r| .{ .reified = .{ + .zir_index = r.zir_index, + .type_hash = 0, + } }, + } }, adapter); + if (gop.found_existing) return .{ .existing = @enumFromInt(gop.index) }; + errdefer _ = ip.map.pop(); try ip.items.ensureUnusedCapacity(gpa, 1); - const gop = try ip.map.getOrPutAdapted(gpa, Key{ - .opaque_type = .{ .decl = ini.decl }, - }, adapter); - if (gop.found_existing) return @enumFromInt(gop.index); + try ip.extra.ensureUnusedCapacity(gpa, @typeInfo(Tag.TypeOpaque).Struct.fields.len + switch (ini.key) { + .declared => |d| d.captures.len, + .reified => 0, + }); + const extra_index = ip.addExtraAssumeCapacity(Tag.TypeOpaque{ + .decl = undefined, // set by `finish` + .namespace = .none, + .zir_index = switch (ini.key) { + inline else => |x| x.zir_index, + }, + .captures_len = switch (ini.key) { + .declared => |d| @intCast(d.captures.len), + .reified => std.math.maxInt(u32), + }, + }); ip.items.appendAssumeCapacity(.{ .tag = .type_opaque, - .data = ip.addExtraAssumeCapacity(Tag.TypeOpaque{ - .decl = ini.decl, - .namespace = ini.namespace, - .zir_index = ini.zir_index, - .captures_len = @intCast(ini.captures.len), - }), + .data = extra_index, }); - ip.extra.appendSliceAssumeCapacity(@ptrCast(ini.captures)); - return @enumFromInt(gop.index); + switch (ini.key) { + .declared => |d| ip.extra.appendSliceAssumeCapacity(@ptrCast(d.captures)), + .reified => {}, + } + return .{ .wip = .{ + .index = @enumFromInt(gop.index), + .decl_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeOpaque, "decl").?, + .namespace_extra_index = if (ini.has_namespace) + extra_index + std.meta.fieldIndex(Tag.TypeOpaque, "namespace").? + else + null, + } }; } pub fn getIfExists(ip: *const InternPool, key: Key) ?Index { @@ -6837,8 +7216,34 @@ fn addMap(ip: *InternPool, gpa: Allocator, cap: usize) Allocator.Error!MapIndex /// This operation only happens under compile error conditions. /// Leak the index until the next garbage collection. -/// TODO: this is a bit problematic to implement, can we get away without it? -pub const remove = @compileError("InternPool.remove is not currently a supported operation; put a TODO there instead"); +/// Invalidates all references to this index. +pub fn remove(ip: *InternPool, index: Index) void { + if (@intFromEnum(index) < static_keys.len) { + // The item being removed replaced a special index via `InternPool.resolveBuiltinType`. + // Restore the original item at this index. + switch (static_keys[@intFromEnum(index)]) { + .simple_type => |s| { + ip.items.set(@intFromEnum(index), .{ + .tag = .simple_type, + .data = @intFromEnum(s), + }); + }, + else => unreachable, + } + return; + } + + if (@intFromEnum(index) == ip.items.len - 1) { + // Happy case - we can just drop the item without affecting any other indices. + ip.items.len -= 1; + _ = ip.map.pop(); + } else { + // We must preserve the item so that indices following it remain valid. + // Thus, we will rewrite the tag to `removed`, leaking the item until + // next GC but causing `KeyAdapter` to ignore it. + ip.items.set(@intFromEnum(index), .{ .tag = .removed, .data = undefined }); + } +} fn addInt(ip: *InternPool, gpa: Allocator, ty: Index, tag: Tag, limbs: []const Limb) !void { const limbs_len = @as(u32, @intCast(limbs.len)); @@ -7635,6 +8040,10 @@ fn dumpStatsFallible(ip: *const InternPool, arena: Allocator) anyerror!void { if (!gop.found_existing) gop.value_ptr.* = .{}; gop.value_ptr.count += 1; gop.value_ptr.bytes += 1 + 4 + @as(usize, switch (tag) { + // Note that in this case, we have technically leaked some extra data + // bytes which we do not account for here. + .removed => 0, + .type_int_signed => 0, .type_int_unsigned => 0, .type_array_small => @sizeOf(Vector), @@ -7852,6 +8261,8 @@ fn dumpAllFallible(ip: *const InternPool) anyerror!void { for (tags, datas, 0..) |tag, data, i| { try w.print("${d} = {s}(", .{ i, @tagName(tag) }); switch (tag) { + .removed => {}, + .simple_type => try w.print("{s}", .{@tagName(@as(SimpleType, @enumFromInt(data)))}), .simple_value => try w.print("{s}", .{@tagName(@as(SimpleValue, @enumFromInt(data)))}), @@ -8258,6 +8669,8 @@ pub fn typeOf(ip: *const InternPool, index: Index) Index { // This optimization on tags is needed so that indexToKey can call // typeOf without being recursive. _ => switch (ip.items.items(.tag)[@intFromEnum(index)]) { + .removed => unreachable, + .type_int_signed, .type_int_unsigned, .type_array_big, @@ -8575,6 +8988,8 @@ pub fn zigTypeTagOrPoison(ip: *const InternPool, index: Index) error{GenericPois .var_args_param_type => unreachable, // special tag _ => switch (ip.items.items(.tag)[@intFromEnum(index)]) { + .removed => unreachable, + .type_int_signed, .type_int_unsigned, => .Int, diff --git a/src/Module.zig b/src/Module.zig index eb3168e08b..4ad760063b 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -568,9 +568,9 @@ pub const Decl = struct { .empty_struct_type => .none, .none => .none, else => switch (ip.indexToKey(decl.val.toIntern())) { - .opaque_type => ip.loadOpaqueType(decl.val.toIntern()).namespace.toOptional(), + .opaque_type => ip.loadOpaqueType(decl.val.toIntern()).namespace, .struct_type => ip.loadStructType(decl.val.toIntern()).namespace, - .union_type => ip.loadUnionType(decl.val.toIntern()).namespace.toOptional(), + .union_type => ip.loadUnionType(decl.val.toIntern()).namespace, .enum_type => ip.loadEnumType(decl.val.toIntern()).namespace, else => .none, }, @@ -3302,6 +3302,70 @@ pub fn semaPkg(mod: *Module, pkg: *Package.Module) !void { return mod.semaFile(file); } +fn getFileRootStruct(zcu: *Zcu, decl_index: Decl.Index, namespace_index: Namespace.Index, file: *File) Allocator.Error!InternPool.Index { + const gpa = zcu.gpa; + const ip = &zcu.intern_pool; + const extended = file.zir.instructions.items(.data)[@intFromEnum(Zir.Inst.Index.main_struct_inst)].extended; + assert(extended.opcode == .struct_decl); + const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small); + assert(!small.has_captures_len); + assert(!small.has_backing_int); + assert(small.layout == .Auto); + var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).Struct.fields.len; + const fields_len = if (small.has_fields_len) blk: { + const fields_len = file.zir.extra[extra_index]; + extra_index += 1; + break :blk fields_len; + } else 0; + const decls_len = if (small.has_decls_len) blk: { + const decls_len = file.zir.extra[extra_index]; + extra_index += 1; + break :blk decls_len; + } else 0; + const decls = file.zir.bodySlice(extra_index, decls_len); + extra_index += decls_len; + + const tracked_inst = try ip.trackZir(gpa, file, .main_struct_inst); + const wip_ty = switch (try ip.getStructType(gpa, .{ + .layout = .Auto, + .fields_len = fields_len, + .known_non_opv = small.known_non_opv, + .requires_comptime = if (small.known_comptime_only) .yes else .unknown, + .is_tuple = small.is_tuple, + .any_comptime_fields = small.any_comptime_fields, + .any_default_inits = small.any_default_inits, + .inits_resolved = false, + .any_aligned_fields = small.any_aligned_fields, + .has_namespace = true, + .key = .{ .declared = .{ + .zir_index = tracked_inst, + .captures = &.{}, + } }, + })) { + .existing => unreachable, // we wouldn't be analysing the file root if this type existed + .wip => |wip| wip, + }; + errdefer wip_ty.cancel(ip); + + if (zcu.comp.debug_incremental) { + try ip.addDependency( + gpa, + InternPool.Depender.wrap(.{ .decl = decl_index }), + .{ .src_hash = tracked_inst }, + ); + } + + const decl = zcu.declPtr(decl_index); + decl.val = Value.fromInterned(wip_ty.index); + decl.has_tv = true; + decl.owns_tv = true; + decl.analysis = .complete; + + try zcu.scanNamespace(namespace_index, decls, decl); + + return wip_ty.finish(ip, decl_index, namespace_index.toOptional()); +} + /// Regardless of the file status, will create a `Decl` so that we /// can track dependencies and re-analyze when the file becomes outdated. pub fn semaFile(mod: *Module, file: *File) SemaError!void { @@ -3323,7 +3387,6 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void { .decl_index = undefined, .file_scope = file, }); - const new_namespace = mod.namespacePtr(new_namespace_index); errdefer mod.destroyNamespace(new_namespace_index); const new_decl_index = try mod.allocateNewDecl(new_namespace_index, 0); @@ -3331,7 +3394,7 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void { errdefer @panic("TODO error handling"); file.root_decl = new_decl_index.toOptional(); - new_namespace.decl_index = new_decl_index; + mod.namespacePtr(new_namespace_index).decl_index = new_decl_index; new_decl.name = try file.fullyQualifiedName(mod); new_decl.name_fully_qualified = true; @@ -3350,63 +3413,10 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void { } assert(file.zir_loaded); - var sema_arena = std.heap.ArenaAllocator.init(gpa); - defer sema_arena.deinit(); - const sema_arena_allocator = sema_arena.allocator(); - - var comptime_mutable_decls = std.ArrayList(Decl.Index).init(gpa); - defer comptime_mutable_decls.deinit(); - - var comptime_err_ret_trace = std.ArrayList(SrcLoc).init(gpa); - defer comptime_err_ret_trace.deinit(); + const struct_ty = try mod.getFileRootStruct(new_decl_index, new_namespace_index, file); + errdefer mod.intern_pool.remove(struct_ty); - var sema: Sema = .{ - .mod = mod, - .gpa = gpa, - .arena = sema_arena_allocator, - .code = file.zir, - .owner_decl = new_decl, - .owner_decl_index = new_decl_index, - .func_index = .none, - .func_is_naked = false, - .fn_ret_ty = Type.void, - .fn_ret_ty_ies = null, - .owner_func_index = .none, - .comptime_mutable_decls = &comptime_mutable_decls, - .comptime_err_ret_trace = &comptime_err_ret_trace, - }; - defer sema.deinit(); - - const struct_ty = sema.getStructType( - new_decl_index, - new_namespace_index, - null, - try mod.intern_pool.trackZir(gpa, file, .main_struct_inst), - ) catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - // The following errors are from resolving capture values, but the root - // struct of a file has no captures. - error.AnalysisFail, - error.NeededSourceLocation, - error.GenericPoison, - error.ComptimeReturn, - error.ComptimeBreak, - => unreachable, - }; - // TODO: figure out InternPool removals for incremental compilation - //errdefer ip.remove(struct_ty); - for (comptime_mutable_decls.items) |decl_index| { - const decl = mod.declPtr(decl_index); - _ = try decl.internValue(mod); - } - - new_decl.val = Value.fromInterned(struct_ty); - new_decl.has_tv = true; - new_decl.owns_tv = true; - new_decl.analysis = .complete; - - const comp = mod.comp; - switch (comp.cache_use) { + switch (mod.comp.cache_use) { .whole => |whole| if (whole.cache_manifest) |man| { const source = file.getSource(gpa) catch |err| { try reportRetryableFileError(mod, file, "unable to load source: {s}", .{@errorName(err)}); @@ -5940,14 +5950,6 @@ pub fn atomicPtrAlignment( return .none; } -pub fn opaqueSrcLoc(mod: *Module, opaque_type: InternPool.Key.OpaqueType) SrcLoc { - return mod.declPtr(opaque_type.decl).srcLoc(mod); -} - -pub fn opaqueFullyQualifiedName(mod: *Module, opaque_type: InternPool.Key.OpaqueType) !InternPool.NullTerminatedString { - return mod.declPtr(opaque_type.decl).fullyQualifiedName(mod); -} - pub fn declFileScope(mod: *Module, decl_index: Decl.Index) *File { return mod.declPtr(decl_index).getFileScope(mod); } diff --git a/src/Sema.zig b/src/Sema.zig index e362c899c6..d4a95027c3 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -2685,7 +2685,7 @@ fn getCaptures(sema: *Sema, parent_namespace: ?InternPool.NamespaceIndex, extra_ capture.* = switch (zir_capture.unwrap()) { .inst => |inst| InternPool.CaptureValue.wrap(capture: { const air_ref = try sema.resolveInst(inst.toRef()); - if (try sema.resolveValue(air_ref)) |val| { + if (try sema.resolveValueResolveLazy(air_ref)) |val| { break :capture .{ .@"comptime" = val.toIntern() }; } break :capture .{ .runtime = sema.typeOf(air_ref).toIntern() }; @@ -2697,23 +2697,20 @@ fn getCaptures(sema: *Sema, parent_namespace: ?InternPool.NamespaceIndex, extra_ return captures; } -pub fn getStructType( +fn zirStructDecl( sema: *Sema, - decl: InternPool.DeclIndex, - namespace: InternPool.NamespaceIndex, - /// The direct parent Namespace for resolving nested capture values. - parent_namespace: ?InternPool.NamespaceIndex, - tracked_inst: InternPool.TrackedInst.Index, -) !InternPool.Index { + block: *Block, + extended: Zir.Inst.Extended.InstData, + inst: Zir.Inst.Index, +) CompileError!Air.Inst.Ref { const mod = sema.mod; const gpa = sema.gpa; const ip = &mod.intern_pool; - const zir_index = tracked_inst.resolve(ip); - const extended = sema.code.instructions.items(.data)[@intFromEnum(zir_index)].extended; - assert(extended.opcode == .struct_decl); const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small); + const extra = sema.code.extraData(Zir.Inst.StructDecl, extended.operand); + const src = extra.data.src(); + var extra_index = extra.end; - var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).Struct.fields.len; const captures_len = if (small.has_captures_len) blk: { const captures_len = sema.code.extra[extra_index]; extra_index += 1; @@ -2730,7 +2727,7 @@ pub fn getStructType( break :blk decls_len; } else 0; - const captures = try sema.getCaptures(parent_namespace, extra_index, captures_len); + const captures = try sema.getCaptures(block.namespace, extra_index, captures_len); extra_index += captures_len; if (small.has_backing_int) { @@ -2743,50 +2740,38 @@ pub fn getStructType( } } - const decls = sema.code.bodySlice(extra_index, decls_len); - try mod.scanNamespace(namespace, decls, mod.declPtr(decl)); - extra_index += decls_len; - - const ty = try ip.getStructType(gpa, .{ - .decl = decl, - .namespace = namespace.toOptional(), - .zir_index = tracked_inst.toOptional(), + const wip_ty = switch (try ip.getStructType(gpa, .{ .layout = small.layout, - .known_non_opv = small.known_non_opv, - .is_tuple = small.is_tuple, .fields_len = fields_len, + .known_non_opv = small.known_non_opv, .requires_comptime = if (small.known_comptime_only) .yes else .unknown, - .any_default_inits = small.any_default_inits, + .is_tuple = small.is_tuple, .any_comptime_fields = small.any_comptime_fields, + .any_default_inits = small.any_default_inits, .inits_resolved = false, .any_aligned_fields = small.any_aligned_fields, - .captures = captures, - }); - - return ty; -} - -fn zirStructDecl( - sema: *Sema, - block: *Block, - extended: Zir.Inst.Extended.InstData, - inst: Zir.Inst.Index, -) CompileError!Air.Inst.Ref { - const mod = sema.mod; - const ip = &mod.intern_pool; - const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small); - const src = sema.code.extraData(Zir.Inst.StructDecl, extended.operand).data.src(); - - // Because these three things each reference each other, `undefined` - // placeholders are used before being set after the struct type gains an - // InternPool index. + .has_namespace = true or decls_len > 0, // TODO: see below + .key = .{ .declared = .{ + .zir_index = try ip.trackZir(gpa, block.getFileScope(mod), inst), + .captures = captures, + } }, + })) { + .existing => |ty| return Air.internedToRef(ty), + .wip => |wip| wip: { + if (sema.builtin_type_target_index == .none) break :wip wip; + var new = wip; + new.index = sema.builtin_type_target_index; + ip.resolveBuiltinType(new.index, wip.index); + break :wip new; + }, + }; + errdefer wip_ty.cancel(ip); const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ - .ty = Type.noreturn, - .val = Value.@"unreachable", + .ty = Type.type, + .val = Value.fromInterned(wip_ty.index), }, small.name_strategy, "struct", inst); - const new_decl = mod.declPtr(new_decl_index); - new_decl.owns_tv = true; + mod.declPtr(new_decl_index).owns_tv = true; errdefer mod.abortAnonDecl(new_decl_index); if (sema.mod.comp.debug_incremental) { @@ -2797,31 +2782,21 @@ fn zirStructDecl( ); } - const new_namespace_index = try mod.createNamespace(.{ + // TODO: if AstGen tells us `@This` was not used in the fields, we can elide the namespace. + const new_namespace_index: InternPool.OptionalNamespaceIndex = if (true or decls_len > 0) (try mod.createNamespace(.{ .parent = block.namespace.toOptional(), .decl_index = new_decl_index, .file_scope = block.getFileScope(mod), - }); + })).toOptional() else .none; errdefer mod.destroyNamespace(new_namespace_index); - const struct_ty = ty: { - const tracked_inst = try ip.trackZir(mod.gpa, block.getFileScope(mod), inst); - const ty = try sema.getStructType(new_decl_index, new_namespace_index, block.namespace, tracked_inst); - if (sema.builtin_type_target_index != .none) { - ip.resolveBuiltinType(sema.builtin_type_target_index, ty); - break :ty sema.builtin_type_target_index; - } - break :ty ty; - }; - // TODO: figure out InternPool removals for incremental compilation - //errdefer ip.remove(struct_ty); - - new_decl.ty = Type.type; - new_decl.val = Value.fromInterned(struct_ty); + if (new_namespace_index.unwrap()) |ns| { + const decls = sema.code.bodySlice(extra_index, decls_len); + try mod.scanNamespace(ns, decls, mod.declPtr(new_decl_index)); + } - const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); try mod.finalizeAnonDecl(new_decl_index); - return decl_val; + return Air.internedToRef(wip_ty.finish(ip, new_decl_index, new_namespace_index)); } fn createAnonymousDeclTypeNamed( @@ -2931,6 +2906,7 @@ fn zirEnumDecl( const mod = sema.mod; const gpa = sema.gpa; + const ip = &mod.intern_pool; const small: Zir.Inst.EnumDecl.Small = @bitCast(extended.small); const extra = sema.code.extraData(Zir.Inst.EnumDecl, extended.operand); var extra_index: usize = extra.end; @@ -2968,39 +2944,10 @@ fn zirEnumDecl( break :blk decls_len; } else 0; - // Because these three things each reference each other, `undefined` - // placeholders are used before being set after the enum type gains an - // InternPool index. - - var done = false; - const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ - .ty = Type.noreturn, - .val = Value.@"unreachable", - }, small.name_strategy, "enum", inst); - const new_decl = mod.declPtr(new_decl_index); - new_decl.owns_tv = true; - errdefer if (!done) mod.abortAnonDecl(new_decl_index); - - if (sema.mod.comp.debug_incremental) { - try mod.intern_pool.addDependency( - sema.gpa, - InternPool.Depender.wrap(.{ .decl = new_decl_index }), - .{ .src_hash = try mod.intern_pool.trackZir(sema.gpa, block.getFileScope(mod), inst) }, - ); - } - const captures = try sema.getCaptures(block.namespace, extra_index, captures_len); extra_index += captures_len; - const new_namespace_index = try mod.createNamespace(.{ - .parent = block.namespace.toOptional(), - .decl_index = new_decl_index, - .file_scope = block.getFileScope(mod), - }); - errdefer if (!done) mod.destroyNamespace(new_namespace_index); - const decls = sema.code.bodySlice(extra_index, decls_len); - try mod.scanNamespace(new_namespace_index, decls, new_decl); extra_index += decls_len; const body = sema.code.bodySlice(extra_index, body_len); @@ -3014,36 +2961,59 @@ fn zirEnumDecl( if (bag != 0) break true; } else false; - const incomplete_enum = incomplete_enum: { - var incomplete_enum = try mod.intern_pool.getIncompleteEnum(gpa, .{ - .decl = new_decl_index, - .namespace = new_namespace_index.toOptional(), - .fields_len = fields_len, - .has_values = any_values, - .tag_mode = if (small.nonexhaustive) - .nonexhaustive - else if (tag_type_ref == .none) - .auto - else - .explicit, - .zir_index = (try mod.intern_pool.trackZir(sema.gpa, block.getFileScope(mod), inst)).toOptional(), + const wip_ty = switch (try ip.getEnumType(gpa, .{ + .has_namespace = true or decls_len > 0, // TODO: see below + .has_values = any_values, + .tag_mode = if (small.nonexhaustive) + .nonexhaustive + else if (tag_type_ref == .none) + .auto + else + .explicit, + .fields_len = fields_len, + .key = .{ .declared = .{ + .zir_index = try mod.intern_pool.trackZir(sema.gpa, block.getFileScope(mod), inst), .captures = captures, - }); - if (sema.builtin_type_target_index != .none) { - mod.intern_pool.resolveBuiltinType(sema.builtin_type_target_index, incomplete_enum.index); - incomplete_enum.index = sema.builtin_type_target_index; - } - break :incomplete_enum incomplete_enum; + } }, + })) { + .wip => |wip| wip: { + if (sema.builtin_type_target_index == .none) break :wip wip; + var new = wip; + new.index = sema.builtin_type_target_index; + ip.resolveBuiltinType(new.index, wip.index); + break :wip new; + }, + .existing => |ty| return Air.internedToRef(ty), }; - // TODO: figure out InternPool removals for incremental compilation - //errdefer if (!done) mod.intern_pool.remove(incomplete_enum.index); + errdefer wip_ty.cancel(ip); - new_decl.ty = Type.type; - new_decl.val = Value.fromInterned(incomplete_enum.index); + const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ + .ty = Type.type, + .val = Value.fromInterned(wip_ty.index), + }, small.name_strategy, "enum", inst); + const new_decl = mod.declPtr(new_decl_index); + new_decl.owns_tv = true; + errdefer mod.abortAnonDecl(new_decl_index); - const decl_val = try sema.analyzeDeclVal(block, src, new_decl_index); - try mod.finalizeAnonDecl(new_decl_index); - done = true; + if (sema.mod.comp.debug_incremental) { + try mod.intern_pool.addDependency( + sema.gpa, + InternPool.Depender.wrap(.{ .decl = new_decl_index }), + .{ .src_hash = try mod.intern_pool.trackZir(sema.gpa, block.getFileScope(mod), inst) }, + ); + } + + // TODO: if AstGen tells us `@This` was not used in the fields, we can elide the namespace. + const new_namespace_index: InternPool.OptionalNamespaceIndex = if (true or decls_len > 0) (try mod.createNamespace(.{ + .parent = block.namespace.toOptional(), + .decl_index = new_decl_index, + .file_scope = block.getFileScope(mod), + })).toOptional() else .none; + errdefer if (new_namespace_index.unwrap()) |ns| mod.destroyNamespace(ns); + + if (new_namespace_index.unwrap()) |ns| { + try mod.scanNamespace(ns, decls, new_decl); + } const int_tag_ty = ty: { // We create a block for the field type instructions because they @@ -3072,7 +3042,7 @@ fn zirEnumDecl( .parent = null, .sema = sema, .src_decl = new_decl_index, - .namespace = new_namespace_index, + .namespace = new_namespace_index.unwrap() orelse block.namespace, .instructions = .{}, .inlining = null, .is_comptime = true, @@ -3088,7 +3058,6 @@ fn zirEnumDecl( if (ty.zigTypeTag(mod) != .Int and ty.zigTypeTag(mod) != .ComptimeInt) { return sema.fail(block, tag_ty_src, "expected integer tag type, found '{}'", .{ty.fmt(sema.mod)}); } - incomplete_enum.setTagType(&mod.intern_pool, ty.toIntern()); break :ty ty; } else if (fields_len == 0) { break :ty try mod.intType(.unsigned, 0); @@ -3098,6 +3067,8 @@ fn zirEnumDecl( } }; + wip_ty.prepare(ip, new_decl_index, new_namespace_index, int_tag_ty.toIntern()); + if (small.nonexhaustive and int_tag_ty.toIntern() != .comptime_int_type) { if (fields_len > 1 and std.math.log2_int(u64, fields_len) == int_tag_ty.bitSize(mod)) { return sema.fail(block, src, "non-exhaustive enum specifies every value", .{}); @@ -3121,7 +3092,6 @@ fn zirEnumDecl( extra_index += 2; // field name, doc comment const field_name = try mod.intern_pool.getOrPutString(gpa, field_name_zir); - assert(incomplete_enum.addFieldName(&mod.intern_pool, field_name) == null); const tag_overflow = if (has_tag_value) overflow: { const tag_val_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); @@ -3142,12 +3112,13 @@ fn zirEnumDecl( }; if (!(try sema.intFitsInType(last_tag_val.?, int_tag_ty, null))) break :overflow true; last_tag_val = try mod.getCoerced(last_tag_val.?, int_tag_ty); - if (incomplete_enum.addFieldValue(&mod.intern_pool, last_tag_val.?.toIntern())) |other_index| { + if (wip_ty.nextField(&mod.intern_pool, field_name, last_tag_val.?.toIntern())) |conflict| { + assert(conflict.kind == .value); // AstGen validated names are unique const value_src = mod.fieldSrcLoc(new_decl_index, .{ .index = field_i, .range = .value, }).lazy; - const other_field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = other_index }).lazy; + const other_field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = conflict.prev_field_idx }).lazy; const msg = msg: { const msg = try sema.errMsg(block, value_src, "enum tag value {} already taken", .{last_tag_val.?.fmtValue(int_tag_ty, sema.mod)}); errdefer msg.destroy(gpa); @@ -3164,9 +3135,10 @@ fn zirEnumDecl( else try mod.intValue(int_tag_ty, 0); if (overflow != null) break :overflow true; - if (incomplete_enum.addFieldValue(&mod.intern_pool, last_tag_val.?.toIntern())) |other_index| { + if (wip_ty.nextField(&mod.intern_pool, field_name, last_tag_val.?.toIntern())) |conflict| { + assert(conflict.kind == .value); // AstGen validated names are unique const field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = field_i }).lazy; - const other_field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = other_index }).lazy; + const other_field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = conflict.prev_field_idx }).lazy; const msg = msg: { const msg = try sema.errMsg(block, field_src, "enum tag value {} already taken", .{last_tag_val.?.fmtValue(int_tag_ty, sema.mod)}); errdefer msg.destroy(gpa); @@ -3177,6 +3149,7 @@ fn zirEnumDecl( } break :overflow false; } else overflow: { + assert(wip_ty.nextField(&mod.intern_pool, field_name, .none) == null); last_tag_val = try mod.intValue(Type.comptime_int, field_i); if (!try sema.intFitsInType(last_tag_val.?, int_tag_ty, null)) break :overflow true; last_tag_val = try mod.getCoerced(last_tag_val.?, int_tag_ty); @@ -3194,7 +3167,9 @@ fn zirEnumDecl( return sema.failWithOwnedErrorMsg(block, msg); } } - return decl_val; + + try mod.finalizeAnonDecl(new_decl_index); + return Air.internedToRef(wip_ty.finish(ip)); } fn zirUnionDecl( @@ -3208,6 +3183,7 @@ fn zirUnionDecl( const mod = sema.mod; const gpa = sema.gpa; + const ip = &mod.intern_pool; const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small); const extra = sema.code.extraData(Zir.Inst.UnionDecl, extended.operand); var extra_index: usize = extra.end; @@ -3233,16 +3209,53 @@ fn zirUnionDecl( break :blk decls_len; } else 0; - // Because these three things each reference each other, `undefined` - // placeholders are used before being set after the union type gains an - // InternPool index. + const captures = try sema.getCaptures(block.namespace, extra_index, captures_len); + extra_index += captures_len; + + const wip_ty = switch (try ip.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, + .assumed_pointer_aligned = false, + .alignment = .none, + }, + .has_namespace = true or decls_len != 0, // TODO: see below + .fields_len = fields_len, + .enum_tag_ty = .none, // set later + .field_types = &.{}, // set later + .field_aligns = &.{}, // set later + .key = .{ .declared = .{ + .zir_index = try ip.trackZir(gpa, block.getFileScope(mod), inst), + .captures = captures, + } }, + })) { + .wip => |wip| wip: { + if (sema.builtin_type_target_index == .none) break :wip wip; + var new = wip; + new.index = sema.builtin_type_target_index; + ip.resolveBuiltinType(new.index, wip.index); + break :wip new; + }, + .existing => |ty| return Air.internedToRef(ty), + }; + errdefer wip_ty.cancel(ip); const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ - .ty = Type.noreturn, - .val = Value.@"unreachable", + .ty = Type.type, + .val = Value.fromInterned(wip_ty.index), }, small.name_strategy, "union", inst); - const new_decl = mod.declPtr(new_decl_index); - new_decl.owns_tv = true; + mod.declPtr(new_decl_index).owns_tv = true; errdefer mod.abortAnonDecl(new_decl_index); if (sema.mod.comp.debug_incremental) { @@ -3253,62 +3266,22 @@ fn zirUnionDecl( ); } - const captures = try sema.getCaptures(block.namespace, extra_index, captures_len); - extra_index += captures_len; - - const new_namespace_index = try mod.createNamespace(.{ + // TODO: if AstGen tells us `@This` was not used in the fields, we can elide the namespace. + const new_namespace_index: InternPool.OptionalNamespaceIndex = if (true or decls_len > 0) (try mod.createNamespace(.{ .parent = block.namespace.toOptional(), .decl_index = new_decl_index, .file_scope = block.getFileScope(mod), - }); - errdefer mod.destroyNamespace(new_namespace_index); - - const union_ty = ty: { - 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, - .assumed_pointer_aligned = false, - .alignment = .none, - }, - .decl = new_decl_index, - .namespace = new_namespace_index, - .zir_index = (try mod.intern_pool.trackZir(gpa, block.getFileScope(mod), inst)).toOptional(), - .fields_len = fields_len, - .enum_tag_ty = .none, - .field_types = &.{}, - .field_aligns = &.{}, - .captures = captures, - }); - if (sema.builtin_type_target_index != .none) { - mod.intern_pool.resolveBuiltinType(sema.builtin_type_target_index, ty); - break :ty sema.builtin_type_target_index; - } - break :ty ty; - }; - // TODO: figure out InternPool removals for incremental compilation - //errdefer mod.intern_pool.remove(union_ty); + })).toOptional() else .none; + errdefer if (new_namespace_index.unwrap()) |ns| mod.destroyNamespace(ns); - new_decl.ty = Type.type; - new_decl.val = Value.fromInterned(union_ty); - - const decls = sema.code.bodySlice(extra_index, decls_len); - try mod.scanNamespace(new_namespace_index, decls, new_decl); + if (new_namespace_index.unwrap()) |ns| { + const decls = sema.code.bodySlice(extra_index, decls_len); + try mod.scanNamespace(ns, decls, mod.declPtr(new_decl_index)); + } - const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); try mod.finalizeAnonDecl(new_decl_index); - return decl_val; + + return Air.internedToRef(wip_ty.finish(ip, new_decl_index, new_namespace_index)); } fn zirOpaqueDecl( @@ -3321,6 +3294,9 @@ fn zirOpaqueDecl( defer tracy.end(); const mod = sema.mod; + const gpa = sema.gpa; + const ip = &mod.intern_pool; + const small: Zir.Inst.OpaqueDecl.Small = @bitCast(extended.small); const extra = sema.code.extraData(Zir.Inst.OpaqueDecl, extended.operand); var extra_index: usize = extra.end; @@ -3339,54 +3315,51 @@ fn zirOpaqueDecl( break :blk decls_len; } else 0; - // Because these three things each reference each other, `undefined` - // placeholders are used in two places before being set after the opaque - // type gains an InternPool index. + const captures = try sema.getCaptures(block.namespace, extra_index, captures_len); + extra_index += captures_len; + + const wip_ty = switch (try ip.getOpaqueType(gpa, .{ + .has_namespace = decls_len != 0, + .key = .{ .declared = .{ + .zir_index = try ip.trackZir(gpa, block.getFileScope(mod), inst), + .captures = captures, + } }, + })) { + .wip => |wip| wip, + .existing => |ty| return Air.internedToRef(ty), + }; + errdefer wip_ty.cancel(ip); const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ - .ty = Type.noreturn, - .val = Value.@"unreachable", + .ty = Type.type, + .val = Value.fromInterned(wip_ty.index), }, small.name_strategy, "opaque", inst); - const new_decl = mod.declPtr(new_decl_index); - new_decl.owns_tv = true; + mod.declPtr(new_decl_index).owns_tv = true; errdefer mod.abortAnonDecl(new_decl_index); if (sema.mod.comp.debug_incremental) { - try mod.intern_pool.addDependency( - sema.gpa, + try ip.addDependency( + gpa, InternPool.Depender.wrap(.{ .decl = new_decl_index }), - .{ .src_hash = try mod.intern_pool.trackZir(sema.gpa, block.getFileScope(mod), inst) }, + .{ .src_hash = try ip.trackZir(gpa, block.getFileScope(mod), inst) }, ); } - const captures = try sema.getCaptures(block.namespace, extra_index, captures_len); - extra_index += captures_len; - - const new_namespace_index = try mod.createNamespace(.{ + const new_namespace_index: InternPool.OptionalNamespaceIndex = if (decls_len > 0) (try mod.createNamespace(.{ .parent = block.namespace.toOptional(), .decl_index = new_decl_index, .file_scope = block.getFileScope(mod), - }); - errdefer mod.destroyNamespace(new_namespace_index); - - const opaque_ty = try mod.intern_pool.getOpaqueType(sema.gpa, .{ - .decl = new_decl_index, - .namespace = new_namespace_index, - .zir_index = (try mod.intern_pool.trackZir(sema.gpa, block.getFileScope(mod), inst)).toOptional(), - .captures = captures, - }); - // TODO: figure out InternPool removals for incremental compilation - //errdefer mod.intern_pool.remove(opaque_ty); - - new_decl.ty = Type.type; - new_decl.val = Value.fromInterned(opaque_ty); + })).toOptional() else .none; + errdefer if (new_namespace_index.unwrap()) |ns| mod.destroyNamespace(ns); - const decls = sema.code.bodySlice(extra_index, decls_len); - try mod.scanNamespace(new_namespace_index, decls, new_decl); + if (new_namespace_index.unwrap()) |ns| { + const decls = sema.code.bodySlice(extra_index, decls_len); + try mod.scanNamespace(ns, decls, mod.declPtr(new_decl_index)); + } - const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); try mod.finalizeAnonDecl(new_decl_index); - return decl_val; + + return Air.internedToRef(wip_ty.finish(ip, new_decl_index, new_namespace_index)); } fn zirErrorSetDecl( @@ -21322,103 +21295,11 @@ fn zirReify( try ip.getOrPutString(gpa, "is_exhaustive"), ).?); - // Decls if (decls_val.sliceLen(mod) > 0) { return sema.fail(block, src, "reified enums must have no decls", .{}); } - const int_tag_ty = tag_type_val.toType(); - if (int_tag_ty.zigTypeTag(mod) != .Int) { - return sema.fail(block, src, "Type.Enum.tag_type must be an integer type", .{}); - } - - // Because these things each reference each other, `undefined` - // placeholders are used before being set after the enum type gains - // an InternPool index. - - const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ - .ty = Type.noreturn, - .val = Value.@"unreachable", - }, name_strategy, "enum", 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); - } - - // Define our empty enum decl - const fields_len: u32 = @intCast(try sema.usizeCast(block, src, fields_val.sliceLen(mod))); - const incomplete_enum = try ip.getIncompleteEnum(gpa, .{ - .decl = new_decl_index, - .namespace = .none, - .fields_len = fields_len, - .has_values = true, - .tag_mode = if (!is_exhaustive_val.toBool()) - .nonexhaustive - else - .explicit, - .tag_ty = int_tag_ty.toIntern(), - .zir_index = .none, - .captures = &.{}, - }); - // TODO: figure out InternPool removals for incremental compilation - //errdefer ip.remove(incomplete_enum.index); - - new_decl.ty = Type.type; - new_decl.val = Value.fromInterned(incomplete_enum.index); - - for (0..fields_len) |field_i| { - const elem_val = try fields_val.elemValue(mod, field_i); - const elem_struct_type = ip.loadStructType(ip.typeOf(elem_val.toIntern())); - const name_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, "name"), - ).?); - const value_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, "value"), - ).?); - - const field_name = try name_val.toIpString(Type.slice_const_u8, mod); - - if (!try sema.intFitsInType(value_val, int_tag_ty, null)) { - // TODO: better source location - return sema.fail(block, src, "field '{}' with enumeration value '{}' is too large for backing int type '{}'", .{ - field_name.fmt(ip), - value_val.fmtValue(Type.comptime_int, mod), - int_tag_ty.fmt(mod), - }); - } - - if (incomplete_enum.addFieldName(ip, field_name)) |other_index| { - const msg = msg: { - const msg = try sema.errMsg(block, src, "duplicate enum field '{}'", .{ - field_name.fmt(ip), - }); - errdefer msg.destroy(gpa); - _ = other_index; // TODO: this note is incorrect - try sema.errNote(block, src, msg, "other field here", .{}); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); - } - - if (incomplete_enum.addFieldValue(ip, (try mod.getCoerced(value_val, int_tag_ty)).toIntern())) |other| { - const msg = msg: { - const msg = try sema.errMsg(block, src, "enum tag value {} already taken", .{value_val.fmtValue(Type.comptime_int, mod)}); - errdefer msg.destroy(gpa); - _ = other; // TODO: this note is incorrect - try sema.errNote(block, src, msg, "other enum tag value here", .{}); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); - } - } - - const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); - try mod.finalizeAnonDecl(new_decl_index); - return decl_val; + return sema.reifyEnum(block, inst, src, tag_type_val.toType(), is_exhaustive_val.toBool(), fields_val, name_strategy); }, .Opaque => { const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); @@ -21432,43 +21313,27 @@ fn zirReify( return sema.fail(block, src, "reified opaque must have no decls", .{}); } - // Because these three things each reference each other, - // `undefined` placeholders are used in two places before being set - // after the opaque type gains an InternPool index. + const wip_ty = switch (try ip.getOpaqueType(gpa, .{ + .has_namespace = false, + .key = .{ .reified = .{ + .zir_index = try ip.trackZir(gpa, block.getFileScope(mod), inst), + } }, + })) { + .existing => |ty| return Air.internedToRef(ty), + .wip => |wip| wip, + }; + errdefer wip_ty.cancel(ip); const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ - .ty = Type.noreturn, - .val = Value.@"unreachable", + .ty = Type.type, + .val = Value.fromInterned(wip_ty.index), }, name_strategy, "opaque", 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(), - .decl_index = new_decl_index, - .file_scope = block.getFileScope(mod), - }); - errdefer mod.destroyNamespace(new_namespace_index); - - const opaque_ty = try ip.getOpaqueType(gpa, .{ - .decl = new_decl_index, - .namespace = new_namespace_index, - .zir_index = .none, - .captures = &.{}, - }); - // TODO: figure out InternPool removals for incremental compilation - //errdefer ip.remove(opaque_ty); - - new_decl.ty = Type.type; - new_decl.val = Value.fromInterned(opaque_ty); + mod.declPtr(new_decl_index).owns_tv = true; + errdefer mod.abortAnonDecl(new_decl_index); - const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); try mod.finalizeAnonDecl(new_decl_index); - return decl_val; + + return Air.internedToRef(wip_ty.finish(ip, new_decl_index, .none)); }, .Union => { const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); @@ -21489,214 +21354,12 @@ fn zirReify( try ip.getOrPutString(gpa, "decls"), ).?); - // Decls if (decls_val.sliceLen(mod) > 0) { return sema.fail(block, src, "reified unions must have no decls", .{}); } const layout = mod.toEnum(std.builtin.Type.ContainerLayout, layout_val); - const fields_len: u32 = @intCast(try sema.usizeCast(block, src, fields_val.sliceLen(mod))); - - // Tag type - 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| { - enum_tag_ty = payload_val.toType().toIntern(); - - const enum_type = switch (ip.indexToKey(enum_tag_ty)) { - .enum_type => ip.loadEnumType(enum_tag_ty), - else => return sema.fail(block, src, "Type.Union.tag_type must be an enum type", .{}), - }; - - explicit_tags_seen = try sema.arena.alloc(bool, enum_type.names.len); - @memset(explicit_tags_seen, false); - } else { - enum_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, fields_len); - } - - // Fields - 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); - const elem_struct_type = ip.loadStructType(ip.typeOf(elem_val.toIntern())); - const name_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, "name"), - ).?); - const type_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, "type"), - ).?); - const alignment_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, "alignment"), - ).?); - - const field_name = try name_val.toIpString(Type.slice_const_u8, mod); - - if (enum_field_names.len != 0) { - enum_field_names[i] = field_name; - } - - if (enum_tag_ty != .none) { - const tag_info = ip.loadEnumType(enum_tag_ty); - const enum_index = tag_info.nameIndex(ip, field_name) orelse { - return sema.fail(block, src, "no field named '{}' in enum '{}'", .{ - field_name.fmt(ip), Type.fromInterned(enum_tag_ty).fmt(mod), - }); - }; - assert(explicit_tags_seen.len == tag_info.names.len); - // No check for duplicate because the check already happened in order - // to create the enum type in the first place. - assert(!explicit_tags_seen[enum_index]); - explicit_tags_seen[enum_index] = true; - } - - 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(); - const alignment_val_int = (try alignment_val.getUnsignedIntAdvanced(mod, sema)).?; - if (alignment_val_int > 0 and !math.isPowerOfTwo(alignment_val_int)) { - // TODO: better source location - return sema.fail(block, src, "alignment value '{d}' is not a power of two or zero", .{ - alignment_val_int, - }); - } - const field_align = Alignment.fromByteUnits(alignment_val_int); - 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: { - const msg = try sema.errMsg(block, src, "opaque types have unknown size and therefore cannot be directly embedded in unions", .{}); - errdefer msg.destroy(gpa); - - try sema.addDeclaredHereNote(msg, field_ty); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); - } - 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); - - const src_decl = mod.declPtr(block.src_decl); - try sema.explainWhyTypeIsNotExtern(msg, src_decl.toSrcLoc(src, mod), field_ty, .union_field); - - try sema.addDeclaredHereNote(msg, field_ty); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); - } else if (layout == .Packed and !try sema.validatePackedType(field_ty)) { - 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); - - const src_decl = mod.declPtr(block.src_decl); - try sema.explainWhyTypeIsNotPacked(msg, src_decl.toSrcLoc(src, mod), field_ty); - - try sema.addDeclaredHereNote(msg, field_ty); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); - } - } - - if (enum_tag_ty != .none) { - const tag_info = ip.loadEnumType(enum_tag_ty); - 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); - - assert(explicit_tags_seen.len == tag_info.names.len); - for (tag_info.names.get(ip), 0..) |field_name, field_index| { - if (explicit_tags_seen[field_index]) continue; - try sema.addFieldErrNote(Type.fromInterned(enum_tag_ty), field_index, msg, "field '{}' missing, declared here", .{ - field_name.fmt(ip), - }); - } - try sema.addDeclaredHereNote(msg, Type.fromInterned(enum_tag_ty)); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); - } - } else { - 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(), - .decl_index = new_decl_index, - .file_scope = block.getFileScope(mod), - }); - 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 = .none, - .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, - .assumed_pointer_aligned = false, - .alignment = .none, - }, - .field_types = union_fields.items(.type), - .field_aligns = if (any_aligned_fields) union_fields.items(.alignment) else &.{}, - .captures = &.{}, - }); - - new_decl.ty = Type.type; - new_decl.val = Value.fromInterned(union_ty); - - const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); - try mod.finalizeAnonDecl(new_decl_index); - return decl_val; + return sema.reifyUnion(block, inst, src, layout, tag_type_val, fields_val, name_strategy); }, .Fn => { const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); @@ -21795,127 +21458,491 @@ fn zirReify( } } -fn reifyStruct( +fn reifyEnum( sema: *Sema, block: *Block, inst: Zir.Inst.Index, src: LazySrcLoc, - layout: std.builtin.Type.ContainerLayout, - backing_int_val: Value, + tag_ty: Type, + is_exhaustive: bool, fields_val: Value, name_strategy: Zir.Inst.NameStrategy, - is_tuple: bool, ) CompileError!Air.Inst.Ref { const mod = sema.mod; const gpa = sema.gpa; const ip = &mod.intern_pool; - if (is_tuple) switch (layout) { - .Extern => return sema.fail(block, src, "extern tuples are not supported", .{}), - .Packed => return sema.fail(block, src, "packed tuples are not supported", .{}), - .Auto => {}, + // This logic must stay in sync with the structure of `std.builtin.Type.Enum` - search for `fieldValue`. + + const fields_len: u32 = @intCast(fields_val.sliceLen(mod)); + + // The validation work here is non-trivial, and it's possible the type already exists. + // So in this first pass, let's just construct a hash to optimize for this case. If the + // inputs turn out to be invalid, we can cancel the WIP type later. + + // For deduplication purposes, we must create a hash including all details of this type. + // TODO: use a longer hash! + var hasher = std.hash.Wyhash.init(0); + std.hash.autoHash(&hasher, tag_ty.toIntern()); + std.hash.autoHash(&hasher, is_exhaustive); + std.hash.autoHash(&hasher, fields_len); + + for (0..fields_len) |field_idx| { + const field_info = try fields_val.elemValue(mod, field_idx); + + const field_name_val = try field_info.fieldValue(mod, 0); + const field_value_val = try sema.resolveLazyValue(try field_info.fieldValue(mod, 1)); + + const field_name = try field_name_val.toIpString(Type.slice_const_u8, mod); + + std.hash.autoHash(&hasher, .{ + field_name, + field_value_val.toIntern(), + }); + } + + const wip_ty = switch (try ip.getEnumType(gpa, .{ + .has_namespace = false, + .has_values = true, + .tag_mode = if (is_exhaustive) .explicit else .nonexhaustive, + .fields_len = fields_len, + .key = .{ .reified = .{ + .zir_index = try ip.trackZir(gpa, block.getFileScope(mod), inst), + .type_hash = hasher.final(), + } }, + })) { + .wip => |wip| wip, + .existing => |ty| return Air.internedToRef(ty), }; + errdefer wip_ty.cancel(ip); + + if (tag_ty.zigTypeTag(mod) != .Int) { + return sema.fail(block, src, "Type.Enum.tag_type must be an integer type", .{}); + } + + const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ + .ty = Type.type, + .val = Value.fromInterned(wip_ty.index), + }, name_strategy, "enum", inst); + mod.declPtr(new_decl_index).owns_tv = true; + errdefer mod.abortAnonDecl(new_decl_index); + + wip_ty.prepare(ip, new_decl_index, .none, tag_ty.toIntern()); + + for (0..fields_len) |field_idx| { + const field_info = try fields_val.elemValue(mod, field_idx); - const fields_len: u32 = @intCast(try sema.usizeCast(block, src, fields_val.sliceLen(mod))); + const field_name_val = try field_info.fieldValue(mod, 0); + const field_value_val = try sema.resolveLazyValue(try field_info.fieldValue(mod, 1)); - // Because these three things each reference each other, `undefined` - // placeholders are used before being set after the struct type gains an - // InternPool index. + const field_name = try field_name_val.toIpString(Type.slice_const_u8, mod); + + if (!try sema.intFitsInType(field_value_val, tag_ty, null)) { + // TODO: better source location + return sema.fail(block, src, "field '{}' with enumeration value '{}' is too large for backing int type '{}'", .{ + field_name.fmt(ip), + field_value_val.fmtValue(Type.comptime_int, mod), + tag_ty.fmt(mod), + }); + } + + const coerced_field_val = try mod.getCoerced(field_value_val, tag_ty); + if (wip_ty.nextField(ip, field_name, coerced_field_val.toIntern())) |conflict| { + return sema.failWithOwnedErrorMsg(block, switch (conflict.kind) { + .name => msg: { + const msg = try sema.errMsg(block, src, "duplicate enum field '{}'", .{field_name.fmt(ip)}); + errdefer msg.destroy(gpa); + _ = conflict.prev_field_idx; // TODO: this note is incorrect + try sema.errNote(block, src, msg, "other field here", .{}); + break :msg msg; + }, + .value => msg: { + const msg = try sema.errMsg(block, src, "enum tag value {} already taken", .{field_value_val.fmtValue(Type.comptime_int, mod)}); + errdefer msg.destroy(gpa); + _ = conflict.prev_field_idx; // TODO: this note is incorrect + try sema.errNote(block, src, msg, "other enum tag value here", .{}); + break :msg msg; + }, + }); + } + } + + if (!is_exhaustive and fields_len > 1 and std.math.log2_int(u64, fields_len) == tag_ty.bitSize(mod)) { + return sema.fail(block, src, "non-exhaustive enum specified every value", .{}); + } + + try mod.finalizeAnonDecl(new_decl_index); + return Air.internedToRef(wip_ty.finish(ip)); +} + +fn reifyUnion( + sema: *Sema, + block: *Block, + inst: Zir.Inst.Index, + src: LazySrcLoc, + layout: std.builtin.Type.ContainerLayout, + opt_tag_type_val: Value, + fields_val: Value, + name_strategy: Zir.Inst.NameStrategy, +) CompileError!Air.Inst.Ref { + const mod = sema.mod; + const gpa = sema.gpa; + const ip = &mod.intern_pool; + + // This logic must stay in sync with the structure of `std.builtin.Type.Union` - search for `fieldValue`. + + const fields_len: u32 = @intCast(fields_val.sliceLen(mod)); + + // The validation work here is non-trivial, and it's possible the type already exists. + // So in this first pass, let's just construct a hash to optimize for this case. If the + // inputs turn out to be invalid, we can cancel the WIP type later. + + // For deduplication purposes, we must create a hash including all details of this type. + // TODO: use a longer hash! + var hasher = std.hash.Wyhash.init(0); + std.hash.autoHash(&hasher, layout); + std.hash.autoHash(&hasher, opt_tag_type_val.toIntern()); + std.hash.autoHash(&hasher, fields_len); + + var any_aligns = false; + + for (0..fields_len) |field_idx| { + const field_info = try fields_val.elemValue(mod, field_idx); + + const field_name_val = try field_info.fieldValue(mod, 0); + const field_type_val = try field_info.fieldValue(mod, 1); + const field_align_val = try sema.resolveLazyValue(try field_info.fieldValue(mod, 2)); + + const field_name = try field_name_val.toIpString(Type.slice_const_u8, mod); + + std.hash.autoHash(&hasher, .{ + field_name, + field_type_val.toIntern(), + field_align_val.toIntern(), + }); + + if (field_align_val.toUnsignedInt(mod) != 0) { + any_aligns = true; + } + } + + const wip_ty = switch (try ip.getUnionType(gpa, .{ + .flags = .{ + .layout = layout, + .status = .none, + .runtime_tag = if (opt_tag_type_val.optionalValue(mod) != null) + .tagged + else if (layout != .Auto) + .none + else switch (block.wantSafety()) { + true => .safety, + false => .none, + }, + .any_aligned_fields = any_aligns, + .requires_comptime = .unknown, + .assumed_runtime_bits = false, + .assumed_pointer_aligned = false, + .alignment = .none, + }, + .has_namespace = false, + .fields_len = fields_len, + .enum_tag_ty = .none, // set later because not yet validated + .field_types = &.{}, // set later + .field_aligns = &.{}, // set later + .key = .{ .reified = .{ + .zir_index = try ip.trackZir(gpa, block.getFileScope(mod), inst), + .type_hash = hasher.final(), + } }, + })) { + .wip => |wip| wip, + .existing => |ty| return Air.internedToRef(ty), + }; + errdefer wip_ty.cancel(ip); const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ - .ty = Type.noreturn, - .val = Value.@"unreachable", - }, name_strategy, "struct", 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); + .ty = Type.type, + .val = Value.fromInterned(wip_ty.index), + }, name_strategy, "union", inst); + mod.declPtr(new_decl_index).owns_tv = true; + errdefer mod.abortAnonDecl(new_decl_index); + + const field_types = try sema.arena.alloc(InternPool.Index, fields_len); + const field_aligns = if (any_aligns) try sema.arena.alloc(InternPool.Alignment, fields_len) else undefined; + + const enum_tag_ty, const has_explicit_tag = if (opt_tag_type_val.optionalValue(mod)) |tag_type_val| tag_ty: { + switch (ip.indexToKey(tag_type_val.toIntern())) { + .enum_type => {}, + else => return sema.fail(block, src, "Type.Union.tag_type must be an enum type", .{}), + } + const enum_tag_ty = tag_type_val.toType(); + + // We simply track which fields of the tag type have been seen. + const tag_ty_fields_len = enum_tag_ty.enumFieldCount(mod); + var seen_tags = try std.DynamicBitSetUnmanaged.initEmpty(sema.arena, tag_ty_fields_len); + + for (field_types, 0..) |*field_ty, field_idx| { + const field_info = try fields_val.elemValue(mod, field_idx); + + const field_name_val = try field_info.fieldValue(mod, 0); + const field_type_val = try field_info.fieldValue(mod, 1); + + const field_name = try field_name_val.toIpString(Type.slice_const_u8, mod); + + const enum_index = enum_tag_ty.enumFieldIndex(field_name, mod) orelse { + // TODO: better source location + return sema.fail(block, src, "no field named '{}' in enum '{}'", .{ + field_name.fmt(ip), enum_tag_ty.fmt(mod), + }); + }; + if (seen_tags.isSet(enum_index)) { + // TODO: better source location + return sema.fail(block, src, "duplicate union field {}", .{field_name.fmt(ip)}); + } + seen_tags.set(enum_index); + + field_ty.* = field_type_val.toIntern(); + if (any_aligns) { + const byte_align = try (try field_info.fieldValue(mod, 2)).toUnsignedIntAdvanced(sema); + if (byte_align > 0 and !math.isPowerOfTwo(byte_align)) { + // TODO: better source location + return sema.fail(block, src, "alignment value '{d}' is not a power of two or zero", .{byte_align}); + } + field_aligns[field_idx] = Alignment.fromByteUnits(byte_align); + } + } + + if (tag_ty_fields_len > fields_len) return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(block, src, "enum fields missing in union", .{}); + errdefer msg.destroy(gpa); + var it = seen_tags.iterator(.{ .kind = .unset }); + while (it.next()) |enum_index| { + const field_name = enum_tag_ty.enumFieldName(enum_index, mod); + try sema.addFieldErrNote(enum_tag_ty, enum_index, msg, "field '{}' missing, declared here", .{ + field_name.fmt(ip), + }); + } + try sema.addDeclaredHereNote(msg, enum_tag_ty); + break :msg msg; + }); + + break :tag_ty .{ enum_tag_ty.toIntern(), true }; + } else tag_ty: { + // We must track field names and set up the tag type ourselves. + var field_names: std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void) = .{}; + try field_names.ensureTotalCapacity(sema.arena, fields_len); + + for (field_types, 0..) |*field_ty, field_idx| { + const field_info = try fields_val.elemValue(mod, field_idx); + + const field_name_val = try field_info.fieldValue(mod, 0); + const field_type_val = try field_info.fieldValue(mod, 1); + + const field_name = try field_name_val.toIpString(Type.slice_const_u8, mod); + const gop = field_names.getOrPutAssumeCapacity(field_name); + if (gop.found_existing) { + // TODO: better source location + return sema.fail(block, src, "duplicate union field {}", .{field_name.fmt(ip)}); + } + + field_ty.* = field_type_val.toIntern(); + if (any_aligns) { + const byte_align = try (try field_info.fieldValue(mod, 2)).toUnsignedIntAdvanced(sema); + if (byte_align > 0 and !math.isPowerOfTwo(byte_align)) { + // TODO: better source location + return sema.fail(block, src, "alignment value '{d}' is not a power of two or zero", .{byte_align}); + } + field_aligns[field_idx] = Alignment.fromByteUnits(byte_align); + } + } + + const enum_tag_ty = try sema.generateUnionTagTypeSimple(block, field_names.keys(), mod.declPtr(new_decl_index)); + break :tag_ty .{ enum_tag_ty, false }; + }; + errdefer if (!has_explicit_tag) ip.remove(enum_tag_ty); // remove generated tag type on error + + for (field_types) |field_ty_ip| { + const field_ty = Type.fromInterned(field_ty_ip); + if (field_ty.zigTypeTag(mod) == .Opaque) { + return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(block, src, "opaque types have unknown size and therefore cannot be directly embedded in unions", .{}); + errdefer msg.destroy(gpa); + + try sema.addDeclaredHereNote(msg, field_ty); + break :msg msg; + }); + } + if (layout == .Extern and !try sema.validateExternType(field_ty, .union_field)) { + return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(block, src, "extern unions cannot contain fields of type '{}'", .{field_ty.fmt(mod)}); + errdefer msg.destroy(gpa); + + const src_decl = mod.declPtr(block.src_decl); + try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), field_ty, .union_field); + + try sema.addDeclaredHereNote(msg, field_ty); + break :msg msg; + }); + } else if (layout == .Packed and !try sema.validatePackedType(field_ty)) { + return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(block, src, "packed unions cannot contain fields of type '{}'", .{field_ty.fmt(mod)}); + errdefer msg.destroy(gpa); + + const src_decl = mod.declPtr(block.src_decl); + try sema.explainWhyTypeIsNotPacked(msg, src.toSrcLoc(src_decl, mod), field_ty); + + try sema.addDeclaredHereNote(msg, field_ty); + break :msg msg; + }); + } } - const ty = try ip.getStructType(gpa, .{ - .decl = new_decl_index, - .namespace = .none, - .zir_index = .none, + const loaded_union = ip.loadUnionType(wip_ty.index); + loaded_union.setFieldTypes(ip, field_types); + if (any_aligns) { + loaded_union.setFieldAligns(ip, field_aligns); + } + loaded_union.tagTypePtr(ip).* = enum_tag_ty; + loaded_union.flagsPtr(ip).status = .have_field_types; + + try mod.finalizeAnonDecl(new_decl_index); + return Air.internedToRef(wip_ty.finish(ip, new_decl_index, .none)); +} + +fn reifyStruct( + sema: *Sema, + block: *Block, + inst: Zir.Inst.Index, + src: LazySrcLoc, + layout: std.builtin.Type.ContainerLayout, + opt_backing_int_val: Value, + fields_val: Value, + name_strategy: Zir.Inst.NameStrategy, + is_tuple: bool, +) CompileError!Air.Inst.Ref { + const mod = sema.mod; + const gpa = sema.gpa; + const ip = &mod.intern_pool; + + // This logic must stay in sync with the structure of `std.builtin.Type.Struct` - search for `fieldValue`. + + const fields_len: u32 = @intCast(fields_val.sliceLen(mod)); + + // The validation work here is non-trivial, and it's possible the type already exists. + // So in this first pass, let's just construct a hash to optimize for this case. If the + // inputs turn out to be invalid, we can cancel the WIP type later. + + // For deduplication purposes, we must create a hash including all details of this type. + // TODO: use a longer hash! + var hasher = std.hash.Wyhash.init(0); + std.hash.autoHash(&hasher, layout); + std.hash.autoHash(&hasher, opt_backing_int_val.toIntern()); + std.hash.autoHash(&hasher, is_tuple); + std.hash.autoHash(&hasher, fields_len); + + var any_comptime_fields = false; + var any_default_inits = false; + var any_aligned_fields = false; + + for (0..fields_len) |field_idx| { + const field_info = try fields_val.elemValue(mod, field_idx); + + const field_name_val = try field_info.fieldValue(mod, 0); + const field_type_val = try field_info.fieldValue(mod, 1); + const field_default_value_val = try field_info.fieldValue(mod, 2); + const field_is_comptime_val = try field_info.fieldValue(mod, 3); + const field_alignment_val = try sema.resolveLazyValue(try field_info.fieldValue(mod, 4)); + + const field_name = try field_name_val.toIpString(Type.slice_const_u8, mod); + const field_is_comptime = field_is_comptime_val.toBool(); + const field_default_value: InternPool.Index = if (field_default_value_val.optionalValue(mod)) |ptr_val| d: { + const ptr_ty = try mod.singleConstPtrType(field_type_val.toType()); + // We need to do this deref here, so we won't check for this error case later on. + const val = try sema.pointerDeref(block, src, ptr_val, ptr_ty) orelse return sema.failWithNeededComptime( + block, + src, + .{ .needed_comptime_reason = "struct field default value must be comptime-known" }, + ); + // Resolve the value so that lazy values do not create distinct types. + break :d (try sema.resolveLazyValue(val)).toIntern(); + } else .none; + + std.hash.autoHash(&hasher, .{ + field_name, + field_type_val.toIntern(), + field_default_value, + field_is_comptime, + field_alignment_val.toIntern(), + }); + + if (field_is_comptime) any_comptime_fields = true; + if (field_default_value != .none) any_default_inits = true; + switch (try field_alignment_val.orderAgainstZeroAdvanced(mod, sema)) { + .eq => {}, + .gt => any_aligned_fields = true, + .lt => unreachable, + } + } + + const wip_ty = switch (try ip.getStructType(gpa, .{ .layout = layout, - .known_non_opv = false, .fields_len = fields_len, + .known_non_opv = false, .requires_comptime = .unknown, .is_tuple = is_tuple, - // So that we don't have to scan ahead, we allocate space in the struct - // type for alignments, comptime fields, and default inits. This might - // result in wasted space, however, this is a permitted encoding of - // struct types. - .any_comptime_fields = true, - .any_default_inits = true, + .any_comptime_fields = any_comptime_fields, + .any_default_inits = any_default_inits, + .any_aligned_fields = any_aligned_fields, .inits_resolved = true, - .any_aligned_fields = true, - .captures = &.{}, - }); - // TODO: figure out InternPool removals for incremental compilation - //errdefer ip.remove(ty); - const struct_type = ip.loadStructType(ty); + .has_namespace = false, + .key = .{ .reified = .{ + .zir_index = try ip.trackZir(gpa, block.getFileScope(mod), inst), + .type_hash = hasher.final(), + } }, + })) { + .wip => |wip| wip, + .existing => |ty| return Air.internedToRef(ty), + }; + errdefer wip_ty.cancel(ip); - new_decl.ty = Type.type; - new_decl.val = Value.fromInterned(ty); - - // Fields - for (0..fields_len) |i| { - const elem_val = try fields_val.elemValue(mod, i); - const elem_struct_type = ip.loadStructType(ip.typeOf(elem_val.toIntern())); - const name_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, "name"), - ).?); - const type_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, "type"), - ).?); - const default_value_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, "default_value"), - ).?); - const is_comptime_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, "is_comptime"), - ).?); - const alignment_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, "alignment"), - ).?); - - if (!try sema.intFitsInType(alignment_val, Type.u32, null)) { - return sema.fail(block, src, "alignment must fit in 'u32'", .{}); - } - const abi_align = (try alignment_val.getUnsignedIntAdvanced(mod, sema)).?; - - if (layout == .Packed) { - if (abi_align != 0) return sema.fail(block, src, "alignment in a packed struct field must be set to 0", .{}); - if (is_comptime_val.toBool()) return sema.fail(block, src, "packed struct fields cannot be marked comptime", .{}); - } else { - if (abi_align > 0 and !math.isPowerOfTwo(abi_align)) return sema.fail(block, src, "alignment value '{d}' is not a power of two or zero", .{abi_align}); - struct_type.field_aligns.get(ip)[i] = Alignment.fromByteUnits(abi_align); - } - if (layout == .Extern and is_comptime_val.toBool()) { - return sema.fail(block, src, "extern struct fields cannot be marked comptime", .{}); - } + if (is_tuple) switch (layout) { + .Extern => return sema.fail(block, src, "extern tuples are not supported", .{}), + .Packed => return sema.fail(block, src, "packed tuples are not supported", .{}), + .Auto => {}, + }; + + const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ + .ty = Type.type, + .val = Value.fromInterned(wip_ty.index), + }, name_strategy, "struct", inst); + mod.declPtr(new_decl_index).owns_tv = true; + errdefer mod.abortAnonDecl(new_decl_index); + + const struct_type = ip.loadStructType(wip_ty.index); + + for (0..fields_len) |field_idx| { + const field_info = try fields_val.elemValue(mod, field_idx); - const field_name = try name_val.toIpString(Type.slice_const_u8, mod); + const field_name_val = try field_info.fieldValue(mod, 0); + const field_type_val = try field_info.fieldValue(mod, 1); + const field_default_value_val = try field_info.fieldValue(mod, 2); + const field_is_comptime_val = try field_info.fieldValue(mod, 3); + const field_alignment_val = try field_info.fieldValue(mod, 4); + const field_ty = field_type_val.toType(); + const field_name = try field_name_val.toIpString(Type.slice_const_u8, mod); if (is_tuple) { - const field_index = field_name.toUnsigned(ip) orelse return sema.fail( + const field_name_index = field_name.toUnsigned(ip) orelse return sema.fail( block, src, "tuple cannot have non-numeric field '{}'", .{field_name.fmt(ip)}, ); - - if (field_index >= fields_len) { + if (field_name_index != field_idx) { return sema.fail( block, src, - "tuple field {} exceeds tuple field count", - .{field_index}, + "tuple field name '{}' does not match field index {}", + .{ field_name_index, field_idx }, ); } } else if (struct_type.addFieldName(ip, field_name)) |prev_index| { @@ -21923,45 +21950,72 @@ fn reifyStruct( return sema.fail(block, src, "duplicate struct field name {}", .{field_name.fmt(ip)}); } - const field_ty = type_val.toType(); - const default_val = if (default_value_val.optionalValue(mod)) |opt_val| - (try sema.pointerDeref(block, src, opt_val, try mod.singleConstPtrType(field_ty)) orelse - return sema.failWithNeededComptime(block, src, .{ - .needed_comptime_reason = "struct field default value must be comptime-known", - })).toIntern() - else - .none; - if (is_comptime_val.toBool() and default_val == .none) { + if (any_aligned_fields) { + if (!try sema.intFitsInType(field_alignment_val, Type.u32, null)) { + return sema.fail(block, src, "alignment must fit in 'u32'", .{}); + } + + const byte_align = try field_alignment_val.toUnsignedIntAdvanced(sema); + if (byte_align == 0) { + if (layout != .Packed) { + struct_type.field_aligns.get(ip)[field_idx] = .none; + } + } else { + if (layout == .Packed) return sema.fail(block, src, "alignment in a packed struct field must be set to 0", .{}); + if (!math.isPowerOfTwo(byte_align)) return sema.fail(block, src, "alignment value '{d}' is not a power of two or zero", .{byte_align}); + struct_type.field_aligns.get(ip)[field_idx] = Alignment.fromNonzeroByteUnits(byte_align); + } + } + + const field_is_comptime = field_is_comptime_val.toBool(); + if (field_is_comptime) { + assert(any_comptime_fields); + switch (layout) { + .Extern => return sema.fail(block, src, "extern struct fields cannot be marked comptime", .{}), + .Packed => return sema.fail(block, src, "packed struct fields cannot be marked comptime", .{}), + .Auto => struct_type.setFieldComptime(ip, field_idx), + } + } + + const field_default: InternPool.Index = d: { + if (!any_default_inits) break :d .none; + const ptr_val = field_default_value_val.optionalValue(mod) orelse break :d .none; + const ptr_ty = try mod.singleConstPtrType(field_ty); + // Asserted comptime-dereferencable above. + const val = (try sema.pointerDeref(block, src, ptr_val, ptr_ty)).?; + // We already resolved this for deduplication, so we may as well do it now. + break :d (try sema.resolveLazyValue(val)).toIntern(); + }; + + if (field_is_comptime and field_default == .none) { return sema.fail(block, src, "comptime field without default initialization value", .{}); } - struct_type.field_types.get(ip)[i] = field_ty.toIntern(); - struct_type.field_inits.get(ip)[i] = default_val; - if (is_comptime_val.toBool()) - struct_type.setFieldComptime(ip, i); + struct_type.field_types.get(ip)[field_idx] = field_type_val.toIntern(); + if (field_default != .none) { + struct_type.field_inits.get(ip)[field_idx] = field_default; + } if (field_ty.zigTypeTag(mod) == .Opaque) { - const msg = msg: { + return sema.failWithOwnedErrorMsg(block, msg: { const msg = try sema.errMsg(block, src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{}); errdefer msg.destroy(gpa); try sema.addDeclaredHereNote(msg, field_ty); break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); + }); } if (field_ty.zigTypeTag(mod) == .NoReturn) { - const msg = msg: { + return sema.failWithOwnedErrorMsg(block, msg: { const msg = try sema.errMsg(block, src, "struct fields cannot be 'noreturn'", .{}); errdefer msg.destroy(gpa); try sema.addDeclaredHereNote(msg, field_ty); break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); + }); } if (layout == .Extern and !try sema.validateExternType(field_ty, .struct_field)) { - const msg = msg: { + return sema.failWithOwnedErrorMsg(block, msg: { const msg = try sema.errMsg(block, src, "extern structs cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)}); errdefer msg.destroy(gpa); @@ -21970,10 +22024,9 @@ fn reifyStruct( try sema.addDeclaredHereNote(msg, field_ty); break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); + }); } else if (layout == .Packed and !try sema.validatePackedType(field_ty)) { - const msg = msg: { + return sema.failWithOwnedErrorMsg(block, msg: { const msg = try sema.errMsg(block, src, "packed structs cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)}); errdefer msg.destroy(gpa); @@ -21982,32 +22035,27 @@ fn reifyStruct( try sema.addDeclaredHereNote(msg, field_ty); break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); + }); } } if (layout == .Packed) { - for (0..struct_type.field_types.len) |index| { - const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[index]); + var fields_bit_sum: u64 = 0; + for (0..struct_type.field_types.len) |field_idx| { + const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_idx]); sema.resolveTypeLayout(field_ty) catch |err| switch (err) { error.AnalysisFail => { const msg = sema.err orelse return err; - try sema.addFieldErrNote(Type.fromInterned(ty), index, msg, "while checking this field", .{}); + try sema.errNote(block, src, msg, "while checking a field of this struct", .{}); return err; }, else => return err, }; - } - - var fields_bit_sum: u64 = 0; - for (0..struct_type.field_types.len) |i| { - const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]); fields_bit_sum += field_ty.bitSize(mod); } - if (backing_int_val.optionalValue(mod)) |backing_int_ty_val| { - const backing_int_ty = backing_int_ty_val.toType(); + if (opt_backing_int_val.optionalValue(mod)) |backing_int_val| { + const backing_int_ty = backing_int_val.toType(); try sema.checkBackingIntType(block, src, backing_int_ty, fields_bit_sum); struct_type.backingIntType(ip).* = backing_int_ty.toIntern(); } else { @@ -22016,9 +22064,8 @@ fn reifyStruct( } } - const decl_val = sema.analyzeDeclVal(block, src, new_decl_index); try mod.finalizeAnonDecl(new_decl_index); - return decl_val; + return Air.internedToRef(wip_ty.finish(ip, new_decl_index, .none)); } fn resolveVaListRef(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) CompileError!Air.Inst.Ref { @@ -36963,8 +37010,8 @@ fn semaUnionFields(mod: *Module, arena: Allocator, union_type: InternPool.Loaded const gpa = mod.gpa; const ip = &mod.intern_pool; const decl_index = union_type.decl; - const zir = mod.namespacePtr(union_type.namespace).file_scope.zir; - const zir_index = union_type.zir_index.unwrap().?.resolve(ip); + const zir = mod.namespacePtr(union_type.namespace.unwrap().?).file_scope.zir; + const zir_index = union_type.zir_index.resolve(ip); const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended; assert(extended.opcode == .union_decl); const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small); @@ -37037,7 +37084,7 @@ fn semaUnionFields(mod: *Module, arena: Allocator, union_type: InternPool.Loaded .parent = null, .sema = &sema, .src_decl = decl_index, - .namespace = union_type.namespace, + .namespace = union_type.namespace.unwrap().?, .instructions = .{}, .inlining = null, .is_comptime = true, @@ -37357,7 +37404,7 @@ fn semaUnionFields(mod: *Module, arena: Allocator, union_type: InternPool.Loaded 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 { - const enum_ty = try sema.generateUnionTagTypeSimple(&block_scope, enum_field_names, union_type.decl.toOptional()); + const enum_ty = try sema.generateUnionTagTypeSimple(&block_scope, enum_field_names, mod.declPtr(union_type.decl)); union_type.tagTypePtr(ip).* = enum_ty; } } @@ -37374,7 +37421,7 @@ fn generateUnionTagTypeNumbered( block: *Block, enum_field_names: []const InternPool.NullTerminatedString, enum_field_vals: []const InternPool.Index, - decl: *Module.Decl, + union_owner_decl: *Module.Decl, ) !InternPool.Index { const mod = sema.mod; const gpa = sema.gpa; @@ -37383,7 +37430,7 @@ fn generateUnionTagTypeNumbered( const src_decl = mod.declPtr(block.src_decl); const new_decl_index = try mod.allocateNewDecl(block.namespace, src_decl.src_node); errdefer mod.destroyDecl(new_decl_index); - const fqn = try decl.fullyQualifiedName(mod); + const fqn = try union_owner_decl.fullyQualifiedName(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, @@ -37395,9 +37442,9 @@ fn generateUnionTagTypeNumbered( new_decl.owns_tv = true; new_decl.name_fully_qualified = true; - const enum_ty = try ip.getEnum(gpa, .{ + const enum_ty = try ip.getGeneratedTagEnumType(gpa, .{ .decl = new_decl_index, - .namespace = .none, + .owner_union_ty = union_owner_decl.val.toIntern(), .tag_ty = if (enum_field_vals.len == 0) (try mod.intType(.unsigned, 0)).toIntern() else @@ -37405,8 +37452,6 @@ fn generateUnionTagTypeNumbered( .names = enum_field_names, .values = enum_field_vals, .tag_mode = .explicit, - .zir_index = .none, - .captures = &.{}, }); new_decl.ty = Type.type; @@ -37420,20 +37465,14 @@ fn generateUnionTagTypeSimple( sema: *Sema, block: *Block, enum_field_names: []const InternPool.NullTerminatedString, - maybe_decl_index: InternPool.OptionalDeclIndex, + union_owner_decl: *Module.Decl, ) !InternPool.Index { const mod = sema.mod; const ip = &mod.intern_pool; const gpa = sema.gpa; const new_decl_index = new_decl_index: { - 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).fullyQualifiedName(mod); + const fqn = try union_owner_decl.fullyQualifiedName(mod); const src_decl = mod.declPtr(block.src_decl); const new_decl_index = try mod.allocateNewDecl(block.namespace, src_decl.src_node); errdefer mod.destroyDecl(new_decl_index); @@ -37447,9 +37486,9 @@ fn generateUnionTagTypeSimple( }; errdefer mod.abortAnonDecl(new_decl_index); - const enum_ty = try ip.getEnum(gpa, .{ + const enum_ty = try ip.getGeneratedTagEnumType(gpa, .{ .decl = new_decl_index, - .namespace = .none, + .owner_union_ty = union_owner_decl.val.toIntern(), .tag_ty = if (enum_field_names.len == 0) (try mod.intType(.unsigned, 0)).toIntern() else @@ -37457,8 +37496,6 @@ fn generateUnionTagTypeSimple( .names = enum_field_names, .values = &.{}, .tag_mode = .auto, - .zir_index = .none, - .captures = &.{}, }); const new_decl = mod.declPtr(new_decl_index); @@ -37643,6 +37680,8 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { => unreachable, _ => switch (ip.items.items(.tag)[@intFromEnum(ty.toIntern())]) { + .removed => unreachable, + .type_int_signed, // i0 handled above .type_int_unsigned, // u0 handled above .type_pointer, diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index d056a384ee..d0f8f99037 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -3548,12 +3548,11 @@ pub const Object = struct { ); return ty; }, - .opaque_type => |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(ip.stringToSlice( - try mod.opaqueFullyQualifiedName(opaque_type), - )); + const decl = mod.declPtr(ip.loadOpaqueType(t.toIntern()).decl); + const name = try o.builder.string(ip.stringToSlice(try decl.getFullyQualifiedName(mod))); gop.value_ptr.* = try o.builder.opaqueType(name); } return gop.value_ptr.*; diff --git a/src/type.zig b/src/type.zig index d19ad6f02e..8b2c6f2a1e 100644 --- a/src/type.zig +++ b/src/type.zig @@ -355,16 +355,16 @@ pub const Type = struct { try writer.writeAll("}"); }, - .union_type => |union_type| { - const decl = mod.declPtr(union_type.decl); + .union_type => { + const decl = mod.declPtr(ip.loadUnionType(ty.toIntern()).decl); try decl.renderFullyQualifiedName(mod, writer); }, - .opaque_type => |opaque_type| { - const decl = mod.declPtr(opaque_type.decl); + .opaque_type => { + const decl = mod.declPtr(ip.loadOpaqueType(ty.toIntern()).decl); try decl.renderFullyQualifiedName(mod, writer); }, - .enum_type => |enum_type| { - const decl = mod.declPtr(enum_type.decl); + .enum_type => { + const decl = mod.declPtr(ip.loadEnumType(ty.toIntern()).decl); try decl.renderFullyQualifiedName(mod, writer); }, .func_type => |fn_info| { @@ -2845,9 +2845,9 @@ pub const Type = struct { pub fn getNamespaceIndex(ty: Type, mod: *Module) InternPool.OptionalNamespaceIndex { const ip = &mod.intern_pool; return switch (ip.indexToKey(ty.toIntern())) { - .opaque_type => ip.loadOpaqueType(ty.toIntern()).namespace.toOptional(), + .opaque_type => ip.loadOpaqueType(ty.toIntern()).namespace, .struct_type => ip.loadStructType(ty.toIntern()).namespace, - .union_type => ip.loadUnionType(ty.toIntern()).namespace.toOptional(), + .union_type => ip.loadUnionType(ty.toIntern()).namespace, .enum_type => ip.loadEnumType(ty.toIntern()).namespace, else => .none, @@ -3180,17 +3180,8 @@ pub const Type = struct { } pub fn declSrcLocOrNull(ty: Type, mod: *Module) ?Module.SrcLoc { - return switch (mod.intern_pool.indexToKey(ty.toIntern())) { - .struct_type => |struct_type| { - return mod.declPtr(struct_type.decl.unwrap() orelse return null).srcLoc(mod); - }, - .union_type => |union_type| { - 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), - else => null, - }; + const decl = ty.getOwnerDeclOrNull(mod) orelse return null; + return mod.declPtr(decl).srcLoc(mod); } pub fn getOwnerDecl(ty: Type, mod: *Module) InternPool.DeclIndex { @@ -3198,11 +3189,12 @@ pub const Type = struct { } pub fn getOwnerDeclOrNull(ty: Type, mod: *Module) ?InternPool.DeclIndex { - return switch (mod.intern_pool.indexToKey(ty.toIntern())) { - .struct_type => |struct_type| struct_type.decl.unwrap(), - .union_type => |union_type| union_type.decl, - .opaque_type => |opaque_type| opaque_type.decl, - .enum_type => |enum_type| enum_type.decl, + const ip = &mod.intern_pool; + return switch (ip.indexToKey(ty.toIntern())) { + .struct_type => ip.loadStructType(ty.toIntern()).decl.unwrap(), + .union_type => ip.loadUnionType(ty.toIntern()).decl, + .opaque_type => ip.loadOpaqueType(ty.toIntern()).decl, + .enum_type => ip.loadEnumType(ty.toIntern()).decl, else => null, }; } @@ -3287,9 +3279,9 @@ pub const Type = struct { const ip = &zcu.intern_pool; return switch (ip.indexToKey(ty.toIntern())) { .struct_type => ip.loadStructType(ty.toIntern()).zir_index.unwrap(), - .union_type => ip.loadUnionType(ty.toIntern()).zir_index.unwrap(), + .union_type => ip.loadUnionType(ty.toIntern()).zir_index, .enum_type => ip.loadEnumType(ty.toIntern()).zir_index.unwrap(), - .opaque_type => ip.loadOpaqueType(ty.toIntern()).zir_index.unwrap(), + .opaque_type => ip.loadOpaqueType(ty.toIntern()).zir_index, else => null, }; } -- cgit v1.2.3 From e043fe474ffe07d1503c6cb70e01983b8d07a1ba Mon Sep 17 00:00:00 2001 From: mlugg Date: Tue, 5 Mar 2024 08:15:28 +0000 Subject: Fix incorrectly resolved merge conflicts To be honest, I can't be bothered to figure out which commits these changes should be in. --- src/Sema.zig | 10 +-- src/codegen/c.zig | 211 ++++++++++++++++++++++++++------------------------- src/codegen/llvm.zig | 4 +- 3 files changed, 115 insertions(+), 110 deletions(-) (limited to 'src/codegen/llvm.zig') diff --git a/src/Sema.zig b/src/Sema.zig index dba0983739..c3e0d5991e 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -2674,7 +2674,7 @@ fn analyzeAsInt( fn getCaptures(sema: *Sema, block: *Block, extra_index: usize, captures_len: u32) ![]InternPool.CaptureValue { const zcu = sema.mod; const ip = &zcu.intern_pool; - const parent_captures: InternPool.CaptureValue.Slice = zcu.namespacePtr(block.namespace).ty.getCaptures(zcu); + const parent_captures: InternPool.CaptureValue.Slice = zcu.namespacePtr(block.namespace).getType(zcu).getCaptures(zcu); const captures = try sema.arena.alloc(InternPool.CaptureValue, captures_len); @@ -2796,7 +2796,7 @@ fn zirStructDecl( .decl_index = new_decl_index, .file_scope = block.getFileScope(mod), })).toOptional() else .none; - errdefer mod.destroyNamespace(new_namespace_index); + errdefer if (new_namespace_index.unwrap()) |ns| mod.destroyNamespace(ns); if (new_namespace_index.unwrap()) |ns| { const decls = sema.code.bodySlice(extra_index, decls_len); @@ -17268,7 +17268,7 @@ fn zirThis( fn zirClosureGet(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { const mod = sema.mod; const ip = &mod.intern_pool; - const captures = mod.namespacePtr(block.namespace).ty.getCaptures(mod); + const captures = mod.namespacePtr(block.namespace).getType(mod).getCaptures(mod); const src_node: i32 = @bitCast(extended.operand); const src = LazySrcLoc.nodeOffset(src_node); @@ -21787,7 +21787,7 @@ fn reifyUnion( errdefer msg.destroy(gpa); const src_decl = mod.declPtr(block.src_decl); - try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), field_ty, .union_field); + try sema.explainWhyTypeIsNotExtern(msg, src_decl.toSrcLoc(src, mod), field_ty, .union_field); try sema.addDeclaredHereNote(msg, field_ty); break :msg msg; @@ -21798,7 +21798,7 @@ fn reifyUnion( errdefer msg.destroy(gpa); const src_decl = mod.declPtr(block.src_decl); - try sema.explainWhyTypeIsNotPacked(msg, src.toSrcLoc(src_decl, mod), field_ty); + try sema.explainWhyTypeIsNotPacked(msg, src_decl.toSrcLoc(src, mod), field_ty); try sema.addDeclaredHereNote(msg, field_ty); break :msg msg; diff --git a/src/codegen/c.zig b/src/codegen/c.zig index f656ab3e50..abbf7501fc 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1376,70 +1376,24 @@ pub const DeclGen = struct { } try writer.writeByte('}'); }, - .struct_type => |struct_type| switch (struct_type.layout) { - .Auto, .Extern => { - if (!location.isInitializer()) { - try writer.writeByte('('); - try dg.renderType(writer, ty); - try writer.writeByte(')'); - } - - try writer.writeByte('{'); - var empty = true; - for (0..struct_type.field_types.len) |field_index| { - const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_index]); - if (struct_type.fieldIsComptime(ip, field_index)) continue; - if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue; - - if (!empty) try writer.writeByte(','); - 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_index] }, - } }), - .elems => |elems| elems[field_index], - .repeated_elem => |elem| elem, - }; - try dg.renderValue(writer, field_ty, Value.fromInterned(field_val), initializer_type); - - empty = false; - } - try writer.writeByte('}'); - }, - .Packed => { - const int_info = ty.intInfo(mod); - - const bits = Type.smallestUnsignedBits(int_info.bits - 1); - const bit_offset_ty = try mod.intType(.unsigned, bits); - - var bit_offset: u64 = 0; - var eff_num_fields: usize = 0; - - for (0..struct_type.field_types.len) |field_index| { - const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_index]); - if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue; - eff_num_fields += 1; - } - - if (eff_num_fields == 0) { - try writer.writeByte('('); - try dg.renderValue(writer, ty, Value.undef, initializer_type); - try writer.writeByte(')'); - } else if (ty.bitSize(mod) > 64) { - // zig_or_u128(zig_or_u128(zig_shl_u128(a, a_off), zig_shl_u128(b, b_off)), zig_shl_u128(c, c_off)) - var num_or = eff_num_fields - 1; - while (num_or > 0) : (num_or -= 1) { - try writer.writeAll("zig_or_"); - try dg.renderTypeForBuiltinFnName(writer, ty); + .struct_type => { + const struct_type = ip.loadStructType(ty.toIntern()); + switch (struct_type.layout) { + .Auto, .Extern => { + if (!location.isInitializer()) { try writer.writeByte('('); + try dg.renderType(writer, ty); + try writer.writeByte(')'); } - var eff_index: usize = 0; - var needs_closing_paren = false; + try writer.writeByte('{'); + var empty = true; for (0..struct_type.field_types.len) |field_index| { const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_index]); + if (struct_type.fieldIsComptime(ip, field_index)) continue; if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue; + if (!empty) try writer.writeByte(','); const field_val = switch (ip.indexToKey(val.ip_index).aggregate.storage) { .bytes => |bytes| try ip.get(mod.gpa, .{ .int = .{ .ty = field_ty.toIntern(), @@ -1448,62 +1402,113 @@ pub const DeclGen = struct { .elems => |elems| elems[field_index], .repeated_elem => |elem| elem, }; - const cast_context = IntCastContext{ .value = .{ .value = Value.fromInterned(field_val) } }; - if (bit_offset != 0) { - try writer.writeAll("zig_shl_"); - try dg.renderTypeForBuiltinFnName(writer, ty); - try writer.writeByte('('); - try dg.renderIntCast(writer, ty, cast_context, field_ty, .FunctionArgument); - try writer.writeAll(", "); - const bit_offset_val = try mod.intValue(bit_offset_ty, bit_offset); - try dg.renderValue(writer, bit_offset_ty, bit_offset_val, .FunctionArgument); - try writer.writeByte(')'); - } else { - try dg.renderIntCast(writer, ty, cast_context, field_ty, .FunctionArgument); - } - - if (needs_closing_paren) try writer.writeByte(')'); - if (eff_index != eff_num_fields - 1) try writer.writeAll(", "); + try dg.renderValue(writer, field_ty, Value.fromInterned(field_val), initializer_type); - bit_offset += field_ty.bitSize(mod); - needs_closing_paren = true; - eff_index += 1; + empty = false; } - } else { - try writer.writeByte('('); - // a << a_off | b << b_off | c << c_off - var empty = true; + try writer.writeByte('}'); + }, + .Packed => { + const int_info = ty.intInfo(mod); + + const bits = Type.smallestUnsignedBits(int_info.bits - 1); + const bit_offset_ty = try mod.intType(.unsigned, bits); + + var bit_offset: u64 = 0; + var eff_num_fields: usize = 0; + for (0..struct_type.field_types.len) |field_index| { const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_index]); - if (struct_type.fieldIsComptime(ip, field_index)) continue; if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue; + eff_num_fields += 1; + } - if (!empty) try writer.writeByte(','); - 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_index] }, - } }), - .elems => |elems| elems[field_index], - .repeated_elem => |elem| elem, - }; - try dg.renderValue(writer, field_ty, Value.fromInterned(field_val), initializer_type); + if (eff_num_fields == 0) { + try writer.writeByte('('); + try dg.renderValue(writer, ty, Value.undef, initializer_type); + try writer.writeByte(')'); + } else if (ty.bitSize(mod) > 64) { + // zig_or_u128(zig_or_u128(zig_shl_u128(a, a_off), zig_shl_u128(b, b_off)), zig_shl_u128(c, c_off)) + var num_or = eff_num_fields - 1; + while (num_or > 0) : (num_or -= 1) { + try writer.writeAll("zig_or_"); + try dg.renderTypeForBuiltinFnName(writer, ty); + try writer.writeByte('('); + } - if (bit_offset != 0) { - try dg.renderValue(writer, field_ty, Value.fromInterned(field_val), .Other); - try writer.writeAll(" << "); - const bit_offset_val = try mod.intValue(bit_offset_ty, bit_offset); - try dg.renderValue(writer, bit_offset_ty, bit_offset_val, .FunctionArgument); - } else { - try dg.renderValue(writer, field_ty, Value.fromInterned(field_val), .Other); + var eff_index: usize = 0; + var needs_closing_paren = false; + for (0..struct_type.field_types.len) |field_index| { + const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_index]); + if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue; + + 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_index] }, + } }), + .elems => |elems| elems[field_index], + .repeated_elem => |elem| elem, + }; + const cast_context = IntCastContext{ .value = .{ .value = Value.fromInterned(field_val) } }; + if (bit_offset != 0) { + try writer.writeAll("zig_shl_"); + try dg.renderTypeForBuiltinFnName(writer, ty); + try writer.writeByte('('); + try dg.renderIntCast(writer, ty, cast_context, field_ty, .FunctionArgument); + try writer.writeAll(", "); + const bit_offset_val = try mod.intValue(bit_offset_ty, bit_offset); + try dg.renderValue(writer, bit_offset_ty, bit_offset_val, .FunctionArgument); + try writer.writeByte(')'); + } else { + try dg.renderIntCast(writer, ty, cast_context, field_ty, .FunctionArgument); + } + + if (needs_closing_paren) try writer.writeByte(')'); + if (eff_index != eff_num_fields - 1) try writer.writeAll(", "); + + bit_offset += field_ty.bitSize(mod); + needs_closing_paren = true; + eff_index += 1; } + } else { + try writer.writeByte('('); + // a << a_off | b << b_off | c << c_off + var empty = true; + for (0..struct_type.field_types.len) |field_index| { + const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_index]); + if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue; - bit_offset += field_ty.bitSize(mod); - empty = false; + if (!empty) try writer.writeAll(" | "); + try writer.writeByte('('); + try dg.renderType(writer, ty); + try writer.writeByte(')'); + + 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_index] }, + } }), + .elems => |elems| elems[field_index], + .repeated_elem => |elem| elem, + }; + + if (bit_offset != 0) { + try dg.renderValue(writer, field_ty, Value.fromInterned(field_val), .Other); + try writer.writeAll(" << "); + const bit_offset_val = try mod.intValue(bit_offset_ty, bit_offset); + try dg.renderValue(writer, bit_offset_ty, bit_offset_val, .FunctionArgument); + } else { + try dg.renderValue(writer, field_ty, Value.fromInterned(field_val), .Other); + } + + bit_offset += field_ty.bitSize(mod); + empty = false; + } + try writer.writeByte(')'); } - try writer.writeByte('}'); - } - }, + }, + } }, else => unreachable, }, diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index d0f8f99037..8f69dbc7e9 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -2642,7 +2642,7 @@ pub const Object = struct { else try o.builder.debugForwardReference(); - const tag_type = union_type.loadTagType(); + const tag_type = union_type.loadTagType(ip); for (0..tag_type.names.len) |field_index| { const field_ty = union_type.field_types.get(ip)[field_index]; @@ -3552,7 +3552,7 @@ pub const Object = struct { const gop = try o.type_map.getOrPut(o.gpa, t.toIntern()); if (!gop.found_existing) { const decl = mod.declPtr(ip.loadOpaqueType(t.toIntern()).decl); - const name = try o.builder.string(ip.stringToSlice(try decl.getFullyQualifiedName(mod))); + const name = try o.builder.string(ip.stringToSlice(try decl.fullyQualifiedName(mod))); gop.value_ptr.* = try o.builder.opaqueType(name); } return gop.value_ptr.*; -- cgit v1.2.3