diff --git a/src/AstGen.zig b/src/AstGen.zig index edd6099127..998e08ba04 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -10694,8 +10694,8 @@ fn identAsString(astgen: *AstGen, ident_token: Ast.TokenIndex) !u32 { const string_bytes = &astgen.string_bytes; const str_index = @intCast(u32, string_bytes.items.len); try astgen.appendIdentStr(ident_token, string_bytes); - const key = string_bytes.items[str_index..]; - const gop = try astgen.string_table.getOrPutContextAdapted(gpa, @as([]const u8, key), StringIndexAdapter{ + const key: []const u8 = string_bytes.items[str_index..]; + const gop = try astgen.string_table.getOrPutContextAdapted(gpa, key, StringIndexAdapter{ .bytes = string_bytes, }, StringIndexContext{ .bytes = string_bytes, diff --git a/src/InternPool.zig b/src/InternPool.zig index 4c7b7016ea..6ff68a7583 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -40,6 +40,14 @@ unions_free_list: std.ArrayListUnmanaged(Module.Union.Index) = .{}, /// to provide lookup. maps: std.ArrayListUnmanaged(std.AutoArrayHashMapUnmanaged(void, void)) = .{}, +/// Used for finding the index inside `string_bytes`. +string_table: std.HashMapUnmanaged( + u32, + void, + std.hash_map.StringIndexContext, + std.hash_map.default_max_load_percentage, +) = .{}, + const std = @import("std"); const Allocator = std.mem.Allocator; const assert = std.debug.assert; @@ -68,6 +76,11 @@ const KeyAdapter = struct { pub const OptionalMapIndex = enum(u32) { none = std.math.maxInt(u32), _, + + pub fn unwrap(oi: OptionalMapIndex) ?MapIndex { + if (oi == .none) return null; + return @intToEnum(MapIndex, @enumToInt(oi)); + } }; /// An index into `maps`. @@ -83,6 +96,10 @@ pub const MapIndex = enum(u32) { pub const NullTerminatedString = enum(u32) { _, + pub fn toOptional(self: NullTerminatedString) OptionalNullTerminatedString { + return @intToEnum(OptionalNullTerminatedString, @enumToInt(self)); + } + const Adapter = struct { strings: []const NullTerminatedString, @@ -102,6 +119,11 @@ pub const NullTerminatedString = enum(u32) { pub const OptionalNullTerminatedString = enum(u32) { none = std.math.maxInt(u32), _, + + pub fn unwrap(oi: OptionalNullTerminatedString) ?NullTerminatedString { + if (oi == .none) return null; + return @intToEnum(NullTerminatedString, @enumToInt(oi)); + } }; pub const Key = union(enum) { @@ -242,13 +264,75 @@ pub const Key = union(enum) { /// Entries are in declaration order, same as `fields`. /// If this is empty, it means the enum tags are auto-numbered. values: []const Index, - /// true if zig inferred this tag type, false if user specified it - tag_ty_inferred: bool, + tag_mode: TagMode, /// This is ignored by `get` but will always be provided by `indexToKey`. names_map: OptionalMapIndex = .none, /// This is ignored by `get` but will be provided by `indexToKey` when /// a value map exists. values_map: OptionalMapIndex = .none, + + pub const TagMode = enum { + /// The integer tag type was auto-numbered by zig. + auto, + /// The integer tag type was provided by the enum declaration, and the enum + /// is exhaustive. + explicit, + /// The integer tag type was provided by the enum declaration, and the enum + /// is non-exhaustive. + nonexhaustive, + }; + + /// Look up field index based on field name. + pub fn nameIndex(self: EnumType, ip: InternPool, name: NullTerminatedString) ?usize { + const map = &ip.maps.items[@enumToInt(self.names_map.unwrap().?)]; + const adapter: NullTerminatedString.Adapter = .{ .strings = self.names }; + return map.getIndexAdapted(name, adapter); + } + + /// Look up field index based on tag value. + /// Asserts that `values_map` is not `none`. + /// This function returns `null` when `tag_val` does not have the + /// integer tag type of the enum. + pub fn tagValueIndex(self: EnumType, ip: InternPool, tag_val: Index) ?usize { + assert(tag_val != .none); + const map = &ip.maps.items[@enumToInt(self.values_map.unwrap().?)]; + const adapter: Index.Adapter = .{ .indexes = self.values }; + return map.getIndexAdapted(tag_val, adapter); + } + }; + + pub const IncompleteEnumType = struct { + /// Same as corresponding `EnumType` field. + decl: Module.Decl.Index, + /// Same as corresponding `EnumType` field. + namespace: Module.Namespace.OptionalIndex, + /// 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: EnumType.TagMode, + /// This may be updated via `setTagType` later. + tag_ty: Index = .none, + + pub fn toEnumType(self: @This()) EnumType { + return .{ + .decl = self.decl, + .namespace = self.namespace, + .tag_ty = self.tag_ty, + .tag_mode = self.tag_mode, + .names = &.{}, + .values = &.{}, + }; + } + + /// 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 = self.toEnumType() }; + } }; pub const Int = struct { @@ -946,12 +1030,18 @@ pub const Tag = enum(u8) { /// An error union type. /// data is payload to ErrorUnion. type_error_union, - /// An enum type with an explicitly provided integer tag type. - /// data is payload index to `EnumExplicit`. - type_enum_explicit, /// An enum type with auto-numbered tag values. + /// The enum is exhaustive. /// data is payload index to `EnumAuto`. type_enum_auto, + /// An enum type with an explicitly provided integer tag type. + /// The enum is exhaustive. + /// data is payload index to `EnumExplicit`. + type_enum_explicit, + /// An enum type with an explicitly provided integer tag type. + /// The enum is non-exhaustive. + /// data is payload index to `EnumExplicit`. + type_enum_nonexhaustive, /// A type that can be represented with only an enum tag. /// data is SimpleType enum value. simple_type, @@ -1302,9 +1392,11 @@ pub fn deinit(ip: *InternPool, gpa: Allocator) void { ip.unions_free_list.deinit(gpa); ip.allocated_unions.deinit(gpa); - for (ip.maps) |*map| map.deinit(gpa); + for (ip.maps.items) |*map| map.deinit(gpa); ip.maps.deinit(gpa); + ip.string_table.deinit(gpa); + ip.* = undefined; } @@ -1421,33 +1513,13 @@ pub fn indexToKey(ip: InternPool, index: Index) Key { .tag_ty = ip.getEnumIntTagType(enum_auto.data.fields_len), .names = names, .values = &.{}, - .tag_ty_inferred = true, + .tag_mode = .auto, .names_map = enum_auto.data.names_map.toOptional(), .values_map = .none, } }; }, - .type_enum_explicit => { - const enum_explicit = ip.extraDataTrail(EnumExplicit, data); - const names = @ptrCast( - []const NullTerminatedString, - ip.extra.items[enum_explicit.end..][0..enum_explicit.data.fields_len], - ); - const values = if (enum_explicit.data.values_map != .none) @ptrCast( - []const Index, - ip.extra.items[enum_explicit.end + names.len ..][0..enum_explicit.data.fields_len], - ) else &[0]Index{}; - - return .{ .enum_type = .{ - .decl = enum_explicit.data.decl, - .namespace = enum_explicit.data.namespace, - .tag_ty = enum_explicit.data.int_tag_type, - .names = names, - .values = values, - .tag_ty_inferred = false, - .names_map = enum_explicit.data.names_map.toOptional(), - .values_map = enum_explicit.data.values_map, - } }; - }, + .type_enum_explicit => indexToKeyEnum(ip, data, .explicit), + .type_enum_nonexhaustive => indexToKeyEnum(ip, data, .nonexhaustive), .opt_null => .{ .opt = .{ .ty = @intToEnum(Index, data), @@ -1531,6 +1603,29 @@ fn getEnumIntTagType(ip: InternPool, fields_len: u32) Index { } }); } +fn indexToKeyEnum(ip: InternPool, data: u32, tag_mode: Key.EnumType.TagMode) Key { + const enum_explicit = ip.extraDataTrail(EnumExplicit, data); + const names = @ptrCast( + []const NullTerminatedString, + ip.extra.items[enum_explicit.end..][0..enum_explicit.data.fields_len], + ); + const values = if (enum_explicit.data.values_map != .none) @ptrCast( + []const Index, + ip.extra.items[enum_explicit.end + names.len ..][0..enum_explicit.data.fields_len], + ) else &[0]Index{}; + + return .{ .enum_type = .{ + .decl = enum_explicit.data.decl, + .namespace = enum_explicit.data.namespace, + .tag_ty = enum_explicit.data.int_tag_type, + .names = names, + .values = values, + .tag_mode = tag_mode, + .names_map = enum_explicit.data.names_map.toOptional(), + .values_map = enum_explicit.data.values_map, + } }; +} + fn indexToKeyBigInt(ip: InternPool, limb_index: u32, positive: bool) Key { const int_info = ip.limbData(Int, limb_index); return .{ .int = .{ @@ -1696,47 +1791,29 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { assert(enum_type.names_map == .none); assert(enum_type.values_map == .none); - const names_map = try ip.addMap(gpa); - try addStringsToMap(ip, gpa, names_map, enum_type.names); + switch (enum_type.tag_mode) { + .auto => { + const names_map = try ip.addMap(gpa); + try addStringsToMap(ip, gpa, names_map, enum_type.names); - const fields_len = @intCast(u32, enum_type.names.len); - - if (enum_type.tag_ty_inferred) { - try ip.extra.ensureUnusedCapacity(gpa, @typeInfo(EnumAuto).Struct.fields.len + - fields_len); - ip.items.appendAssumeCapacity(.{ - .tag = .type_enum_auto, - .data = ip.addExtraAssumeCapacity(EnumAuto{ - .decl = enum_type.decl, - .namespace = enum_type.namespace, - .names_map = names_map, - .fields_len = fields_len, - }), - }); - ip.extra.appendSliceAssumeCapacity(@ptrCast([]const u32, enum_type.names)); - return @intToEnum(Index, ip.items.len - 1); + const fields_len = @intCast(u32, enum_type.names.len); + try ip.extra.ensureUnusedCapacity(gpa, @typeInfo(EnumAuto).Struct.fields.len + + fields_len); + ip.items.appendAssumeCapacity(.{ + .tag = .type_enum_auto, + .data = ip.addExtraAssumeCapacity(EnumAuto{ + .decl = enum_type.decl, + .namespace = enum_type.namespace, + .names_map = names_map, + .fields_len = fields_len, + }), + }); + ip.extra.appendSliceAssumeCapacity(@ptrCast([]const u32, enum_type.names)); + return @intToEnum(Index, ip.items.len - 1); + }, + .explicit => return finishGetEnum(ip, gpa, enum_type, .type_enum_explicit), + .nonexhaustive => return finishGetEnum(ip, gpa, enum_type, .type_enum_nonexhaustive), } - - const values_map: OptionalMapIndex = if (enum_type.values.len == 0) .none else m: { - const values_map = try ip.addMap(gpa); - try addIndexesToMap(ip, gpa, values_map, enum_type.values); - break :m values_map.toOptional(); - }; - try ip.extra.ensureUnusedCapacity(gpa, @typeInfo(EnumExplicit).Struct.fields.len + - fields_len); - ip.items.appendAssumeCapacity(.{ - .tag = .type_enum_auto, - .data = ip.addExtraAssumeCapacity(EnumExplicit{ - .decl = enum_type.decl, - .namespace = enum_type.namespace, - .int_tag_type = enum_type.tag_ty, - .fields_len = fields_len, - .names_map = names_map, - .values_map = values_map, - }), - }); - ip.extra.appendSliceAssumeCapacity(@ptrCast([]const u32, enum_type.names)); - ip.extra.appendSliceAssumeCapacity(@ptrCast([]const u32, enum_type.values)); }, .extern_func => @panic("TODO"), @@ -1934,8 +2011,206 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { return @intToEnum(Index, ip.items.len - 1); } -pub fn getAssumeExists(ip: InternPool, key: Key) Index { - const adapter: KeyAdapter = .{ .intern_pool = &ip }; +/// Provides API for completing an enum type after calling `getIncompleteEnum`. +pub const IncompleteEnumType = struct { + index: Index, + tag_ty_index: u32, + names_map: MapIndex, + names_start: u32, + values_map: OptionalMapIndex, + values_start: u32, + + pub fn setTagType(self: @This(), ip: *InternPool, tag_ty: Index) void { + assert(tag_ty != .none); + ip.extra.items[self.tag_ty_index] = @enumToInt(tag_ty); + } + + /// Returns the already-existing field with the same name, if any. + pub fn addFieldName( + self: @This(), + ip: *InternPool, + gpa: Allocator, + name: NullTerminatedString, + ) Allocator.Error!?u32 { + const map = &ip.maps.items[@enumToInt(self.names_map)]; + const field_index = map.count(); + const strings = ip.extra.items[self.names_start..][0..field_index]; + const adapter: NullTerminatedString.Adapter = .{ + .strings = @ptrCast([]const NullTerminatedString, strings), + }; + const gop = try map.getOrPutAdapted(gpa, name, adapter); + if (gop.found_existing) return @intCast(u32, gop.index); + ip.extra.items[self.names_start + field_index] = @enumToInt(name); + return null; + } + + /// 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, + gpa: Allocator, + value: Index, + ) Allocator.Error!?u32 { + const map = &ip.maps.items[@enumToInt(self.values_map.unwrap().?)]; + const field_index = map.count(); + const indexes = ip.extra.items[self.values_start..][0..field_index]; + const adapter: Index.Adapter = .{ + .indexes = @ptrCast([]const Index, indexes), + }; + const gop = try map.getOrPutAdapted(gpa, value, adapter); + if (gop.found_existing) return @intCast(u32, gop.index); + ip.extra.items[self.values_start + field_index] = @enumToInt(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!InternPool.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 getIncompleteEnumAuto( + ip: *InternPool, + gpa: Allocator, + enum_type: Key.IncompleteEnumType, +) Allocator.Error!InternPool.IncompleteEnumType { + // Although the integer tag type will not be stored in the `EnumAuto` struct, + // `InternPool` logic depends on it being present so that `typeOf` can be infallible. + // Ensure it is present here: + _ = 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); + + const extra_fields_len: u32 = @typeInfo(EnumAuto).Struct.fields.len; + try ip.extra.ensureUnusedCapacity(gpa, extra_fields_len + enum_type.fields_len); + + const extra_index = ip.addExtraAssumeCapacity(EnumAuto{ + .decl = enum_type.decl, + .namespace = enum_type.namespace, + .names_map = names_map, + .fields_len = enum_type.fields_len, + }); + + ip.items.appendAssumeCapacity(.{ + .tag = .type_enum_auto, + .data = extra_index, + }); + ip.extra.appendNTimesAssumeCapacity(@enumToInt(Index.none), enum_type.fields_len); + return .{ + .index = @intToEnum(Index, ip.items.len - 1), + .tag_ty_index = undefined, + .names_map = names_map, + .names_start = extra_index + extra_fields_len, + .values_map = .none, + .values_start = undefined, + }; +} + +pub fn getIncompleteEnumExplicit( + ip: *InternPool, + gpa: Allocator, + enum_type: Key.IncompleteEnumType, + tag: Tag, +) Allocator.Error!InternPool.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`. + 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); + const values_map: OptionalMapIndex = if (!enum_type.has_values) .none else m: { + const values_map = try ip.addMap(gpa); + break :m values_map.toOptional(); + }; + + const reserved_len = enum_type.fields_len + + if (enum_type.has_values) enum_type.fields_len else 0; + + const extra_fields_len: u32 = @typeInfo(EnumExplicit).Struct.fields.len; + try ip.extra.ensureUnusedCapacity(gpa, extra_fields_len + reserved_len); + + const extra_index = ip.addExtraAssumeCapacity(EnumExplicit{ + .decl = enum_type.decl, + .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, + }); + + ip.items.appendAssumeCapacity(.{ + .tag = tag, + .data = extra_index, + }); + // This is both fields and values (if present). + ip.extra.appendNTimesAssumeCapacity(@enumToInt(Index.none), reserved_len); + return .{ + .index = @intToEnum(Index, 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, + }; +} + +pub fn finishGetEnum( + ip: *InternPool, + gpa: Allocator, + enum_type: Key.EnumType, + tag: Tag, +) Allocator.Error!Index { + const names_map = try ip.addMap(gpa); + try addStringsToMap(ip, gpa, names_map, enum_type.names); + + const values_map: OptionalMapIndex = if (enum_type.values.len == 0) .none else m: { + const values_map = try ip.addMap(gpa); + try addIndexesToMap(ip, gpa, values_map, enum_type.values); + break :m values_map.toOptional(); + }; + const fields_len = @intCast(u32, enum_type.names.len); + try ip.extra.ensureUnusedCapacity(gpa, @typeInfo(EnumExplicit).Struct.fields.len + + fields_len); + ip.items.appendAssumeCapacity(.{ + .tag = tag, + .data = ip.addExtraAssumeCapacity(EnumExplicit{ + .decl = enum_type.decl, + .namespace = enum_type.namespace, + .int_tag_type = enum_type.tag_ty, + .fields_len = fields_len, + .names_map = names_map, + .values_map = values_map, + }), + }); + ip.extra.appendSliceAssumeCapacity(@ptrCast([]const u32, enum_type.names)); + ip.extra.appendSliceAssumeCapacity(@ptrCast([]const u32, enum_type.values)); + return @intToEnum(Index, ip.items.len - 1); +} + +pub fn getAssumeExists(ip: *const InternPool, key: Key) Index { + const adapter: KeyAdapter = .{ .intern_pool = ip }; const index = ip.map.getIndexAdapted(key, adapter).?; return @intToEnum(Index, index); } @@ -1979,6 +2254,7 @@ fn addMap(ip: *InternPool, gpa: Allocator) Allocator.Error!MapIndex { pub fn remove(ip: *InternPool, index: Index) void { _ = ip; _ = index; + @setCold(true); @panic("TODO this is a bit problematic to implement, could we maybe just never support a remove() operation on InternPool?"); } @@ -2336,7 +2612,7 @@ fn dumpFallible(ip: InternPool, arena: Allocator) anyerror!void { .type_slice => 0, .type_optional => 0, .type_error_union => @sizeOf(ErrorUnion), - .type_enum_explicit => @sizeOf(EnumExplicit), + .type_enum_explicit, .type_enum_nonexhaustive => @sizeOf(EnumExplicit), .type_enum_auto => @sizeOf(EnumAuto), .type_opaque => @sizeOf(Key.OpaqueType), .type_struct => @sizeOf(Module.Struct) + @sizeOf(Module.Namespace) + @sizeOf(Module.Decl), @@ -2448,3 +2724,50 @@ pub fn destroyUnion(ip: *InternPool, gpa: Allocator, index: Module.Union.Index) // allocation failures here, instead leaking the Union until garbage collection. }; } + +pub fn getOrPutString( + ip: *InternPool, + gpa: Allocator, + s: []const u8, +) Allocator.Error!NullTerminatedString { + const string_bytes = &ip.string_bytes; + const str_index = @intCast(u32, string_bytes.items.len); + try string_bytes.ensureUnusedCapacity(gpa, s.len + 1); + string_bytes.appendSliceAssumeCapacity(s); + const key: []const u8 = string_bytes.items[str_index..]; + const gop = try ip.string_table.getOrPutContextAdapted(gpa, key, std.hash_map.StringIndexAdapter{ + .bytes = string_bytes, + }, std.hash_map.StringIndexContext{ + .bytes = string_bytes, + }); + if (gop.found_existing) { + string_bytes.shrinkRetainingCapacity(str_index); + return @intToEnum(NullTerminatedString, gop.key_ptr.*); + } else { + gop.key_ptr.* = str_index; + string_bytes.appendAssumeCapacity(0); + return @intToEnum(NullTerminatedString, str_index); + } +} + +pub fn getString(ip: *InternPool, s: []const u8) OptionalNullTerminatedString { + if (ip.string_table.getKeyAdapted(s, std.hash_map.StringIndexAdapter{ + .bytes = &ip.string_bytes, + })) |index| { + return @intToEnum(NullTerminatedString, index).toOptional(); + } else { + return .none; + } +} + +pub fn stringToSlice(ip: InternPool, s: NullTerminatedString) [:0]const u8 { + const string_bytes = ip.string_bytes.items; + const start = @enumToInt(s); + var end: usize = start; + while (string_bytes[end] != 0) end += 1; + return string_bytes[start..end :0]; +} + +pub fn typeOf(ip: InternPool, index: Index) Index { + return ip.indexToKey(index).typeOf(); +} diff --git a/src/Module.zig b/src/Module.zig index 6478f7ce4f..6bcd148e67 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -886,29 +886,17 @@ pub const Decl = struct { /// Only returns it if the Decl is the owner. pub fn getInnerNamespaceIndex(decl: *Decl, mod: *Module) Namespace.OptionalIndex { if (!decl.owns_tv) return .none; - switch (decl.val.ip_index) { - .empty_struct_type => return .none, - .none => { - const ty = (decl.val.castTag(.ty) orelse return .none).data; - switch (ty.tag()) { - .enum_full, .enum_nonexhaustive => { - const enum_obj = ty.cast(Type.Payload.EnumFull).?.data; - return enum_obj.namespace.toOptional(); - }, - - else => return .none, - } - }, - else => return switch (mod.intern_pool.indexToKey(decl.val.ip_index)) { + return switch (decl.val.ip_index) { + .empty_struct_type => .none, + .none => .none, + else => switch (mod.intern_pool.indexToKey(decl.val.ip_index)) { .opaque_type => |opaque_type| opaque_type.namespace.toOptional(), .struct_type => |struct_type| struct_type.namespace, - .union_type => |union_type| { - const union_obj = mod.unionPtr(union_type.index); - return union_obj.namespace.toOptional(); - }, + .union_type => |union_type| mod.unionPtr(union_type.index).namespace.toOptional(), + .enum_type => |enum_type| enum_type.namespace, else => .none, }, - } + }; } /// Same as `getInnerNamespaceIndex` but additionally obtains the pointer. @@ -1135,28 +1123,6 @@ pub const Struct = struct { return mod.declPtr(s.owner_decl).srcLoc(mod); } - pub fn fieldSrcLoc(s: Struct, mod: *Module, query: FieldSrcQuery) SrcLoc { - @setCold(true); - const owner_decl = mod.declPtr(s.owner_decl); - const file = owner_decl.getFileScope(mod); - const tree = file.getTree(mod.gpa) catch |err| { - // In this case we emit a warning + a less precise source location. - log.warn("unable to load {s}: {s}", .{ - file.sub_file_path, @errorName(err), - }); - return s.srcLoc(mod); - }; - const node = owner_decl.relativeToNodeIndex(0); - - var buf: [2]Ast.Node.Index = undefined; - if (tree.fullContainerDecl(&buf, node)) |container_decl| { - return queryFieldSrc(tree.*, query, file, container_decl); - } else { - // This struct was generated using @Type - return s.srcLoc(mod); - } - } - pub fn haveFieldTypes(s: Struct) bool { return switch (s.status) { .none, @@ -1237,110 +1203,6 @@ pub const Struct = struct { } }; -/// Represents the data that an enum declaration provides, when the fields -/// are auto-numbered, and there are no declarations. The integer tag type -/// is inferred to be the smallest power of two unsigned int that fits -/// the number of fields. -pub const EnumSimple = struct { - /// The Decl that corresponds to the enum itself. - owner_decl: Decl.Index, - /// Set of field names in declaration order. - fields: NameMap, - - pub const NameMap = EnumFull.NameMap; - - pub fn srcLoc(self: EnumSimple, mod: *Module) SrcLoc { - const owner_decl = mod.declPtr(self.owner_decl); - return .{ - .file_scope = owner_decl.getFileScope(mod), - .parent_decl_node = owner_decl.src_node, - .lazy = LazySrcLoc.nodeOffset(0), - }; - } -}; - -/// Represents the data that an enum declaration provides, when there are no -/// declarations. However an integer tag type is provided, and the enum tag values -/// are explicitly provided. -pub const EnumNumbered = struct { - /// The Decl that corresponds to the enum itself. - owner_decl: Decl.Index, - /// An integer type which is used for the numerical value of the enum. - /// Whether zig chooses this type or the user specifies it, it is stored here. - tag_ty: Type, - /// Set of field names in declaration order. - fields: NameMap, - /// Maps integer tag value to field index. - /// Entries are in declaration order, same as `fields`. - /// If this hash map is empty, it means the enum tags are auto-numbered. - values: ValueMap, - - pub const NameMap = EnumFull.NameMap; - pub const ValueMap = EnumFull.ValueMap; - - pub fn srcLoc(self: EnumNumbered, mod: *Module) SrcLoc { - const owner_decl = mod.declPtr(self.owner_decl); - return .{ - .file_scope = owner_decl.getFileScope(mod), - .parent_decl_node = owner_decl.src_node, - .lazy = LazySrcLoc.nodeOffset(0), - }; - } -}; - -/// Represents the data that an enum declaration provides, when there is -/// at least one tag value explicitly specified, or at least one declaration. -pub const EnumFull = struct { - /// The Decl that corresponds to the enum itself. - owner_decl: Decl.Index, - /// An integer type which is used for the numerical value of the enum. - /// Whether zig chooses this type or the user specifies it, it is stored here. - tag_ty: Type, - /// Set of field names in declaration order. - fields: NameMap, - /// Maps integer tag value to field index. - /// Entries are in declaration order, same as `fields`. - /// If this hash map is empty, it means the enum tags are auto-numbered. - values: ValueMap, - /// Represents the declarations inside this enum. - namespace: Namespace.Index, - /// true if zig inferred this tag type, false if user specified it - tag_ty_inferred: bool, - - pub const NameMap = std.StringArrayHashMapUnmanaged(void); - pub const ValueMap = std.ArrayHashMapUnmanaged(Value, void, Value.ArrayHashContext, false); - - pub fn srcLoc(self: EnumFull, mod: *Module) SrcLoc { - const owner_decl = mod.declPtr(self.owner_decl); - return .{ - .file_scope = owner_decl.getFileScope(mod), - .parent_decl_node = owner_decl.src_node, - .lazy = LazySrcLoc.nodeOffset(0), - }; - } - - pub fn fieldSrcLoc(e: EnumFull, mod: *Module, query: FieldSrcQuery) SrcLoc { - @setCold(true); - const owner_decl = mod.declPtr(e.owner_decl); - const file = owner_decl.getFileScope(mod); - const tree = file.getTree(mod.gpa) catch |err| { - // In this case we emit a warning + a less precise source location. - log.warn("unable to load {s}: {s}", .{ - file.sub_file_path, @errorName(err), - }); - return e.srcLoc(mod); - }; - const node = owner_decl.relativeToNodeIndex(0); - var buf: [2]Ast.Node.Index = undefined; - if (tree.fullContainerDecl(&buf, node)) |container_decl| { - return queryFieldSrc(tree.*, query, file, container_decl); - } else { - // This enum was generated using @Type - return e.srcLoc(mod); - } - } -}; - pub const Union = struct { /// An enum type which is used for the tag of the union. /// This type is created even for untagged unions, even when the memory @@ -1427,28 +1289,6 @@ pub const Union = struct { }; } - pub fn fieldSrcLoc(u: Union, mod: *Module, query: FieldSrcQuery) SrcLoc { - @setCold(true); - const owner_decl = mod.declPtr(u.owner_decl); - const file = owner_decl.getFileScope(mod); - const tree = file.getTree(mod.gpa) catch |err| { - // In this case we emit a warning + a less precise source location. - log.warn("unable to load {s}: {s}", .{ - file.sub_file_path, @errorName(err), - }); - return u.srcLoc(mod); - }; - const node = owner_decl.relativeToNodeIndex(0); - - var buf: [2]Ast.Node.Index = undefined; - if (tree.fullContainerDecl(&buf, node)) |container_decl| { - return queryFieldSrc(tree.*, query, file, container_decl); - } else { - // This union was generated using @Type - return u.srcLoc(mod); - } - } - pub fn haveFieldTypes(u: Union) bool { return switch (u.status) { .none, @@ -7313,3 +7153,24 @@ pub fn typeToUnion(mod: *Module, ty: Type) ?*Union { const union_index = mod.intern_pool.indexToUnion(ty.ip_index).unwrap() orelse return null; return mod.unionPtr(union_index); } + +pub fn fieldSrcLoc(mod: *Module, owner_decl_index: Decl.Index, query: FieldSrcQuery) SrcLoc { + @setCold(true); + const owner_decl = mod.declPtr(owner_decl_index); + const file = owner_decl.getFileScope(mod); + const tree = file.getTree(mod.gpa) catch |err| { + // In this case we emit a warning + a less precise source location. + log.warn("unable to load {s}: {s}", .{ + file.sub_file_path, @errorName(err), + }); + return owner_decl.srcLoc(mod); + }; + const node = owner_decl.relativeToNodeIndex(0); + var buf: [2]Ast.Node.Index = undefined; + if (tree.fullContainerDecl(&buf, node)) |container_decl| { + return queryFieldSrc(tree.*, query, file, container_decl); + } else { + // This type was generated using @Type + return owner_decl.srcLoc(mod); + } +} diff --git a/src/Sema.zig b/src/Sema.zig index e9bf66565e..b94f995b46 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -2096,7 +2096,7 @@ fn failWithInvalidComptimeFieldStore(sema: *Sema, block: *Block, init_src: LazyS errdefer msg.destroy(sema.gpa); const struct_ty = mod.typeToStruct(container_ty) orelse break :msg msg; - const default_value_src = struct_ty.fieldSrcLoc(mod, .{ + const default_value_src = mod.fieldSrcLoc(struct_ty.owner_decl, .{ .index = field_index, .range = .value, }); @@ -2875,50 +2875,28 @@ 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; - - var new_decl_arena = std.heap.ArenaAllocator.init(gpa); - errdefer if (!done) new_decl_arena.deinit(); - const new_decl_arena_allocator = new_decl_arena.allocator(); - - const enum_obj = try new_decl_arena_allocator.create(Module.EnumFull); - const enum_ty_payload = try new_decl_arena_allocator.create(Type.Payload.EnumFull); - enum_ty_payload.* = .{ - .base = .{ .tag = if (small.nonexhaustive) .enum_nonexhaustive else .enum_full }, - .data = enum_obj, - }; - const enum_ty = Type.initPayload(&enum_ty_payload.base); - const enum_val = try Value.Tag.ty.create(new_decl_arena_allocator, enum_ty); const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ .ty = Type.type, - .val = enum_val, + .val = undefined, }, 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); - enum_obj.* = .{ - .owner_decl = new_decl_index, - .tag_ty = Type.null, - .tag_ty_inferred = true, - .fields = .{}, - .values = .{}, - .namespace = try mod.createNamespace(.{ - .parent = block.namespace.toOptional(), - .ty = enum_ty, - .file_scope = block.getFileScope(mod), - }), - }; + const new_namespace_index = try mod.createNamespace(.{ + .parent = block.namespace.toOptional(), + .ty = undefined, + .file_scope = block.getFileScope(mod), + }); + const new_namespace = mod.namespacePtr(new_namespace_index); + errdefer if (!done) mod.destroyNamespace(new_namespace_index); - try new_decl.finalizeNewArena(&new_decl_arena); - const decl_val = try sema.analyzeDeclVal(block, src, new_decl_index); - done = true; - - var decl_arena: std.heap.ArenaAllocator = undefined; - const decl_arena_allocator = new_decl.value_arena.?.acquire(gpa, &decl_arena); - defer new_decl.value_arena.?.release(&decl_arena); - - extra_index = try mod.scanNamespace(enum_obj.namespace, extra_index, decls_len, new_decl); + extra_index = try mod.scanNamespace(new_namespace_index, extra_index, decls_len, new_decl); const body = sema.code.extra[extra_index..][0..body_len]; extra_index += body.len; @@ -2927,7 +2905,31 @@ fn zirEnumDecl( const body_end = extra_index; extra_index += bit_bags_count; - { + const any_values = for (sema.code.extra[body_end..][0..bit_bags_count]) |bag| { + if (bag != 0) break true; + } else false; + + const 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, + }); + errdefer if (!done) mod.intern_pool.remove(incomplete_enum.index); + + new_decl.val = incomplete_enum.index.toValue(); + new_namespace.ty = incomplete_enum.index.toType(); + + const decl_val = try sema.analyzeDeclVal(block, src, new_decl_index); + done = true; + + const int_tag_ty = ty: { // We create a block for the field type instructions because they // may need to reference Decls from inside the enum namespace. // Within the field type, default value, and alignment expressions, the "owner decl" @@ -2957,7 +2959,7 @@ fn zirEnumDecl( .parent = null, .sema = sema, .src_decl = new_decl_index, - .namespace = enum_obj.namespace, + .namespace = new_namespace_index, .wip_capture_scope = wip_captures.scope, .instructions = .{}, .inlining = null, @@ -2976,35 +2978,22 @@ 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)}); } - enum_obj.tag_ty = try ty.copy(decl_arena_allocator); - enum_obj.tag_ty_inferred = false; + incomplete_enum.setTagType(&mod.intern_pool, ty.ip_index); + break :ty ty; } else if (fields_len == 0) { - enum_obj.tag_ty = try mod.intType(.unsigned, 0); - enum_obj.tag_ty_inferred = true; + break :ty try mod.intType(.unsigned, 0); } else { const bits = std.math.log2_int_ceil(usize, fields_len); - enum_obj.tag_ty = try mod.intType(.unsigned, bits); - enum_obj.tag_ty_inferred = true; + break :ty try mod.intType(.unsigned, bits); } - } + }; - if (small.nonexhaustive and enum_obj.tag_ty.zigTypeTag(mod) != .ComptimeInt) { - if (fields_len > 1 and std.math.log2_int(u64, fields_len) == enum_obj.tag_ty.bitSize(mod)) { + if (small.nonexhaustive and int_tag_ty.ip_index != .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", .{}); } } - try enum_obj.fields.ensureTotalCapacity(decl_arena_allocator, fields_len); - const any_values = for (sema.code.extra[body_end..][0..bit_bags_count]) |bag| { - if (bag != 0) break true; - } else false; - if (any_values) { - try enum_obj.values.ensureTotalCapacityContext(decl_arena_allocator, fields_len, .{ - .ty = enum_obj.tag_ty, - .mod = mod, - }); - } - var bit_bag_index: usize = body_end; var cur_bit_bag: u32 = undefined; var field_i: u32 = 0; @@ -3023,15 +3012,12 @@ fn zirEnumDecl( // doc comment extra_index += 1; - // This string needs to outlive the ZIR code. - const field_name = try decl_arena_allocator.dupe(u8, field_name_zir); - - const gop_field = enum_obj.fields.getOrPutAssumeCapacity(field_name); - if (gop_field.found_existing) { - const field_src = enum_obj.fieldSrcLoc(sema.mod, .{ .index = field_i }).lazy; - const other_field_src = enum_obj.fieldSrcLoc(sema.mod, .{ .index = gop_field.index }).lazy; + const field_name = try mod.intern_pool.getOrPutString(gpa, field_name_zir); + if (try incomplete_enum.addFieldName(&mod.intern_pool, gpa, field_name)) |other_index| { + 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 msg = msg: { - const msg = try sema.errMsg(block, field_src, "duplicate enum field '{s}'", .{field_name}); + const msg = try sema.errMsg(block, field_src, "duplicate enum field '{s}'", .{field_name_zir}); errdefer msg.destroy(gpa); try sema.errNote(block, other_field_src, msg, "other field here", .{}); break :msg msg; @@ -3045,7 +3031,7 @@ fn zirEnumDecl( const tag_inst = try sema.resolveInst(tag_val_ref); const tag_val = sema.resolveConstValue(block, .unneeded, tag_inst, "") catch |err| switch (err) { error.NeededSourceLocation => { - const value_src = enum_obj.fieldSrcLoc(sema.mod, .{ + const value_src = mod.fieldSrcLoc(new_decl_index, .{ .index = field_i, .range = .value, }).lazy; @@ -3055,19 +3041,14 @@ fn zirEnumDecl( else => |e| return e, }; last_tag_val = tag_val; - const copied_tag_val = try tag_val.copy(decl_arena_allocator); - const gop_val = enum_obj.values.getOrPutAssumeCapacityContext(copied_tag_val, .{ - .ty = enum_obj.tag_ty, - .mod = mod, - }); - if (gop_val.found_existing) { - const value_src = enum_obj.fieldSrcLoc(sema.mod, .{ + if (try incomplete_enum.addFieldValue(&mod.intern_pool, gpa, tag_val.ip_index)) |other_index| { + const value_src = mod.fieldSrcLoc(new_decl_index, .{ .index = field_i, .range = .value, }).lazy; - const other_field_src = enum_obj.fieldSrcLoc(sema.mod, .{ .index = gop_val.index }).lazy; + const other_field_src = mod.fieldSrcLoc(new_decl_index, .{ .index = other_index }).lazy; const msg = msg: { - const msg = try sema.errMsg(block, value_src, "enum tag value {} already taken", .{tag_val.fmtValue(enum_obj.tag_ty, sema.mod)}); + const msg = try sema.errMsg(block, value_src, "enum tag value {} already taken", .{tag_val.fmtValue(int_tag_ty, sema.mod)}); errdefer msg.destroy(gpa); try sema.errNote(block, other_field_src, msg, "other occurrence here", .{}); break :msg msg; @@ -3076,20 +3057,15 @@ fn zirEnumDecl( } } else if (any_values) { const tag_val = if (last_tag_val) |val| - try sema.intAdd(val, try mod.intValue(enum_obj.tag_ty, 1), enum_obj.tag_ty) + try sema.intAdd(val, try mod.intValue(int_tag_ty, 1), int_tag_ty) else - try mod.intValue(enum_obj.tag_ty, 0); + try mod.intValue(int_tag_ty, 0); last_tag_val = tag_val; - const copied_tag_val = try tag_val.copy(decl_arena_allocator); - const gop_val = enum_obj.values.getOrPutAssumeCapacityContext(copied_tag_val, .{ - .ty = enum_obj.tag_ty, - .mod = mod, - }); - if (gop_val.found_existing) { - const field_src = enum_obj.fieldSrcLoc(sema.mod, .{ .index = field_i }).lazy; - const other_field_src = enum_obj.fieldSrcLoc(sema.mod, .{ .index = gop_val.index }).lazy; + if (try incomplete_enum.addFieldValue(&mod.intern_pool, gpa, tag_val.ip_index)) |other_index| { + 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 msg = msg: { - const msg = try sema.errMsg(block, field_src, "enum tag value {} already taken", .{tag_val.fmtValue(enum_obj.tag_ty, sema.mod)}); + const msg = try sema.errMsg(block, field_src, "enum tag value {} already taken", .{tag_val.fmtValue(int_tag_ty, sema.mod)}); errdefer msg.destroy(gpa); try sema.errNote(block, other_field_src, msg, "other occurrence here", .{}); break :msg msg; @@ -3097,16 +3073,16 @@ fn zirEnumDecl( return sema.failWithOwnedErrorMsg(msg); } } else { - last_tag_val = try mod.intValue(enum_obj.tag_ty, field_i); + last_tag_val = try mod.intValue(int_tag_ty, field_i); } - if (!(try sema.intFitsInType(last_tag_val.?, enum_obj.tag_ty, null))) { - const value_src = enum_obj.fieldSrcLoc(sema.mod, .{ + if (!(try sema.intFitsInType(last_tag_val.?, int_tag_ty, null))) { + const value_src = mod.fieldSrcLoc(new_decl_index, .{ .index = field_i, .range = if (has_tag_value) .value else .name, }).lazy; const msg = try sema.errMsg(block, value_src, "enumeration value '{}' too large for type '{}'", .{ - last_tag_val.?.fmtValue(enum_obj.tag_ty, mod), enum_obj.tag_ty.fmt(mod), + last_tag_val.?.fmtValue(int_tag_ty, mod), int_tag_ty.fmt(mod), }); return sema.failWithOwnedErrorMsg(msg); } @@ -4356,7 +4332,7 @@ fn validateUnionInit( } const tag_ty = union_ty.unionTagTypeHypothetical(mod); - const enum_field_index = @intCast(u32, tag_ty.enumFieldIndex(field_name).?); + const enum_field_index = @intCast(u32, tag_ty.enumFieldIndex(field_name, mod).?); const tag_val = try Value.Tag.enum_field_index.create(sema.arena, enum_field_index); if (init_val) |val| { @@ -8334,7 +8310,7 @@ fn zirIntToEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A _ = try sema.checkIntType(block, operand_src, sema.typeOf(operand)); if (try sema.resolveMaybeUndefVal(operand)) |int_val| { - if (dest_ty.isNonexhaustiveEnum()) { + if (dest_ty.isNonexhaustiveEnum(mod)) { const int_tag_ty = try dest_ty.intTagType(mod); if (try sema.intFitsInType(int_val, int_tag_ty, null)) { return sema.addConstant(dest_ty, int_val); @@ -8383,7 +8359,7 @@ fn zirIntToEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A try sema.requireRuntimeBlock(block, src, operand_src); const result = try block.addTyOp(.intcast, dest_ty, operand); - if (block.wantSafety() and !dest_ty.isNonexhaustiveEnum() and + if (block.wantSafety() and !dest_ty.isNonexhaustiveEnum(mod) and sema.mod.backendSupportsFeature(.is_named_enum_value)) { const ok = try block.addUnOp(.is_named_enum_value, result); @@ -10518,7 +10494,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError var else_error_ty: ?Type = null; // Validate usage of '_' prongs. - if (special_prong == .under and (!operand_ty.isNonexhaustiveEnum() or union_originally)) { + if (special_prong == .under and (!operand_ty.isNonexhaustiveEnum(mod) or union_originally)) { const msg = msg: { const msg = try sema.errMsg( block, @@ -10543,8 +10519,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError switch (operand_ty.zigTypeTag(mod)) { .Union => unreachable, // handled in zirSwitchCond .Enum => { - seen_enum_fields = try gpa.alloc(?Module.SwitchProngSrc, operand_ty.enumFieldCount()); - empty_enum = seen_enum_fields.len == 0 and !operand_ty.isNonexhaustiveEnum(); + seen_enum_fields = try gpa.alloc(?Module.SwitchProngSrc, operand_ty.enumFieldCount(mod)); + empty_enum = seen_enum_fields.len == 0 and !operand_ty.isNonexhaustiveEnum(mod); @memset(seen_enum_fields, null); // `range_set` is used for non-exhaustive enum values that do not correspond to any tags. @@ -10599,7 +10575,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError } else true; if (special_prong == .@"else") { - if (all_tags_handled and !operand_ty.isNonexhaustiveEnum()) return sema.fail( + if (all_tags_handled and !operand_ty.isNonexhaustiveEnum(mod)) return sema.fail( block, special_prong_src, "unreachable else prong; all cases already handled", @@ -10617,7 +10593,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError for (seen_enum_fields, 0..) |seen_src, i| { if (seen_src != null) continue; - const field_name = operand_ty.enumFieldName(i); + const field_name = operand_ty.enumFieldName(i, mod); try sema.addFieldErrNote( operand_ty, i, @@ -10635,7 +10611,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); - } else if (special_prong == .none and operand_ty.isNonexhaustiveEnum() and !union_originally) { + } else if (special_prong == .none and operand_ty.isNonexhaustiveEnum(mod) and !union_originally) { return sema.fail( block, src, @@ -11159,7 +11135,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError return Air.Inst.Ref.unreachable_value; } if (mod.backendSupportsFeature(.is_named_enum_value) and block.wantSafety() and operand_ty.zigTypeTag(mod) == .Enum and - (!operand_ty.isNonexhaustiveEnum() or union_originally)) + (!operand_ty.isNonexhaustiveEnum(mod) or union_originally)) { try sema.zirDbgStmt(block, cond_dbg_node_index); const ok = try block.addUnOp(.is_named_enum_value, operand); @@ -11489,7 +11465,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError var emit_bb = false; if (special.is_inline) switch (operand_ty.zigTypeTag(mod)) { .Enum => { - if (operand_ty.isNonexhaustiveEnum() and !union_originally) { + if (operand_ty.isNonexhaustiveEnum(mod) and !union_originally) { return sema.fail(block, special_prong_src, "cannot enumerate values of type '{}' for 'inline else'", .{ operand_ty.fmt(mod), }); @@ -11629,7 +11605,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError case_block.inline_case_capture = .none; if (mod.backendSupportsFeature(.is_named_enum_value) and special.body.len != 0 and block.wantSafety() and - operand_ty.zigTypeTag(mod) == .Enum and (!operand_ty.isNonexhaustiveEnum() or union_originally)) + operand_ty.zigTypeTag(mod) == .Enum and (!operand_ty.isNonexhaustiveEnum(mod) or union_originally)) { try sema.zirDbgStmt(&case_block, cond_dbg_node_index); const ok = try case_block.addUnOp(.is_named_enum_value, operand); @@ -12081,7 +12057,7 @@ fn zirHasField(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai break :hf switch (ty.zigTypeTag(mod)) { .Struct => ty.structFields(mod).contains(field_name), .Union => ty.unionFields(mod).contains(field_name), - .Enum => ty.enumFields().contains(field_name), + .Enum => ty.enumFieldIndex(field_name, mod) != null, .Array => mem.eql(u8, field_name, "len"), else => return sema.fail(block, ty_src, "type '{}' does not support '@hasField'", .{ ty.fmt(sema.mod), @@ -16300,9 +16276,9 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai }, .Enum => { // TODO: look into memoizing this result. - const int_tag_ty = try ty.intTagType(mod); + const enum_type = mod.intern_pool.indexToKey(ty.ip_index).enum_type; - const is_exhaustive = Value.makeBool(!ty.isNonexhaustiveEnum()); + const is_exhaustive = Value.makeBool(enum_type.tag_mode != .nonexhaustive); var fields_anon_decl = try block.startAnonDecl(); defer fields_anon_decl.deinit(); @@ -16320,25 +16296,17 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai break :t try enum_field_ty_decl.val.toType().copy(fields_anon_decl.arena()); }; - const enum_fields = ty.enumFields(); - const enum_field_vals = try fields_anon_decl.arena().alloc(Value, enum_fields.count()); + const enum_field_vals = try fields_anon_decl.arena().alloc(Value, enum_type.names.len); for (enum_field_vals, 0..) |*field_val, i| { - var tag_val_payload: Value.Payload.U32 = .{ - .base = .{ .tag = .enum_field_index }, - .data = @intCast(u32, i), - }; - const tag_val = Value.initPayload(&tag_val_payload.base); - - const int_val = try tag_val.enumToInt(ty, mod); - - const name = enum_fields.keys()[i]; + const name_ip = enum_type.names[i]; + const name = mod.intern_pool.stringToSlice(name_ip); const name_val = v: { var anon_decl = try block.startAnonDecl(); defer anon_decl.deinit(); const bytes = try anon_decl.arena().dupeZ(u8, name); const new_decl = try anon_decl.finish( - try Type.array(anon_decl.arena(), bytes.len, try mod.intValue(Type.u8, 0), Type.u8, mod), + try Type.array(anon_decl.arena(), bytes.len, Value.zero_u8, Type.u8, mod), try Value.Tag.bytes.create(anon_decl.arena(), bytes[0 .. bytes.len + 1]), 0, // default alignment ); @@ -16350,7 +16318,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai // name: []const u8, name_val, // value: comptime_int, - int_val, + try mod.intValue(Type.comptime_int, i), }; field_val.* = try Value.Tag.aggregate.create(fields_anon_decl.arena(), enum_field_fields); } @@ -16370,12 +16338,12 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai break :v try Value.Tag.decl_ref.create(sema.arena, new_decl); }; - const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, ty.getNamespace(mod)); + const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, enum_type.namespace); const field_values = try sema.arena.create([4]Value); field_values.* = .{ // tag_type: type, - try Value.Tag.ty.create(sema.arena, int_tag_ty), + enum_type.tag_ty.toValue(), // fields: []const EnumField, fields_val, // decls: []const Declaration, @@ -16468,7 +16436,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai }); }; - const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, union_ty.getNamespace(mod)); + const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, union_ty.getNamespaceIndex(mod)); const enum_tag_ty_val = if (union_ty.unionTagType(mod)) |tag_ty| v: { const ty_val = try Value.Tag.ty.create(sema.arena, tag_ty); @@ -16631,7 +16599,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai }); }; - const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, struct_ty.getNamespace(mod)); + const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, struct_ty.getNamespaceIndex(mod)); const backing_integer_val = blk: { if (layout == .Packed) { @@ -16674,7 +16642,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai // TODO: look into memoizing this result. const opaque_ty = try sema.resolveTypeFields(ty); - const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, opaque_ty.getNamespace(mod)); + const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, opaque_ty.getNamespaceIndex(mod)); const field_values = try sema.arena.create([1]Value); field_values.* = .{ @@ -16700,7 +16668,7 @@ fn typeInfoDecls( block: *Block, src: LazySrcLoc, type_info_ty: Type, - opt_namespace: ?*Module.Namespace, + opt_namespace: Module.Namespace.OptionalIndex, ) CompileError!Value { const mod = sema.mod; var decls_anon_decl = try block.startAnonDecl(); @@ -16726,8 +16694,9 @@ fn typeInfoDecls( var seen_namespaces = std.AutoHashMap(*Namespace, void).init(sema.gpa); defer seen_namespaces.deinit(); - if (opt_namespace) |some| { - try sema.typeInfoNamespaceDecls(block, decls_anon_decl.arena(), some, &decl_vals, &seen_namespaces); + if (opt_namespace.unwrap()) |namespace_index| { + const namespace = mod.namespacePtr(namespace_index); + try sema.typeInfoNamespaceDecls(block, decls_anon_decl.arena(), namespace, &decl_vals, &seen_namespaces); } const new_decl = try decls_anon_decl.finish( @@ -17896,7 +17865,7 @@ fn unionInit( if (try sema.resolveMaybeUndefVal(init)) |init_val| { const tag_ty = union_ty.unionTagTypeHypothetical(mod); - const enum_field_index = @intCast(u32, tag_ty.enumFieldIndex(field_name).?); + const enum_field_index = @intCast(u32, tag_ty.enumFieldIndex(field_name, mod).?); const tag_val = try Value.Tag.enum_field_index.create(sema.arena, enum_field_index); return sema.addConstant(union_ty, try Value.Tag.@"union".create(sema.arena, .{ .tag = tag_val, @@ -17997,7 +17966,7 @@ fn zirStructInit( const field_name = sema.code.nullTerminatedString(field_type_extra.name_start); const field_index = try sema.unionFieldIndex(block, resolved_ty, field_name, field_src); const tag_ty = resolved_ty.unionTagTypeHypothetical(mod); - const enum_field_index = @intCast(u32, tag_ty.enumFieldIndex(field_name).?); + const enum_field_index = @intCast(u32, tag_ty.enumFieldIndex(field_name, mod).?); const tag_val = try Value.Tag.enum_field_index.create(sema.arena, enum_field_index); const init_inst = try sema.resolveInst(item.data.init); @@ -18754,7 +18723,7 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air operand_ty.fmt(mod), }), }; - if (enum_ty.enumFieldCount() == 0) { + if (enum_ty.enumFieldCount(mod) == 0) { // TODO I don't think this is the correct way to handle this but // it prevents a crash. return sema.fail(block, operand_src, "cannot get @tagName of empty enum '{}'", .{ @@ -18776,7 +18745,7 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air }; return sema.failWithOwnedErrorMsg(msg); }; - const field_name = enum_ty.enumFieldName(field_index); + const field_name = enum_ty.enumFieldName(field_index, mod); return sema.addStrLit(block, field_name); } try sema.requireRuntimeBlock(block, src, operand_src); @@ -19081,63 +19050,41 @@ fn zirReify( return sema.fail(block, src, "reified enums must have no decls", .{}); } - var new_decl_arena = std.heap.ArenaAllocator.init(gpa); - errdefer new_decl_arena.deinit(); - const new_decl_arena_allocator = new_decl_arena.allocator(); + 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. - // Define our empty enum decl - const enum_obj = try new_decl_arena_allocator.create(Module.EnumFull); - const enum_ty_payload = try new_decl_arena_allocator.create(Type.Payload.EnumFull); - enum_ty_payload.* = .{ - .base = .{ - .tag = if (!is_exhaustive_val.toBool(mod)) - .enum_nonexhaustive - else - .enum_full, - }, - .data = enum_obj, - }; - const enum_ty = Type.initPayload(&enum_ty_payload.base); - const enum_val = try Value.Tag.ty.create(new_decl_arena_allocator, enum_ty); const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{ .ty = Type.type, - .val = enum_val, + .val = undefined, }, name_strategy, "enum", inst); const new_decl = mod.declPtr(new_decl_index); new_decl.owns_tv = true; errdefer mod.abortAnonDecl(new_decl_index); - enum_obj.* = .{ - .owner_decl = new_decl_index, - .tag_ty = Type.null, - .tag_ty_inferred = false, - .fields = .{}, - .values = .{}, - .namespace = try mod.createNamespace(.{ - .parent = block.namespace.toOptional(), - .ty = enum_ty, - .file_scope = block.getFileScope(mod), - }), - }; - - // Enum tag type - const int_tag_ty = try tag_type_val.toType().copy(new_decl_arena_allocator); - - if (int_tag_ty.zigTypeTag(mod) != .Int) { - return sema.fail(block, src, "Type.Enum.tag_type must be an integer type", .{}); - } - enum_obj.tag_ty = int_tag_ty; - - // Fields - const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(mod)); - try enum_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len); - try enum_obj.values.ensureTotalCapacityContext(new_decl_arena_allocator, fields_len, .{ - .ty = enum_obj.tag_ty, - .mod = mod, + // Define our empty enum decl + const fields_len = @intCast(u32, try sema.usizeCast(block, src, fields_val.sliceLen(mod))); + const incomplete_enum = try mod.intern_pool.getIncompleteEnum(gpa, .{ + .decl = new_decl_index, + .namespace = .none, + .fields_len = fields_len, + .has_values = true, + .tag_mode = if (!is_exhaustive_val.toBool(mod)) + .nonexhaustive + else + .explicit, + .tag_ty = int_tag_ty.ip_index, }); + errdefer mod.intern_pool.remove(incomplete_enum.index); - var field_i: usize = 0; - while (field_i < fields_len) : (field_i += 1) { + new_decl.val = incomplete_enum.index.toValue(); + + for (0..fields_len) |field_i| { const elem_val = try fields_val.elemValue(mod, field_i); const field_struct_val: []const Value = elem_val.castTag(.aggregate).?.data; // TODO use reflection instead of magic numbers here @@ -19148,39 +19095,36 @@ fn zirReify( const field_name = try name_val.toAllocatedBytes( Type.const_slice_u8, - new_decl_arena_allocator, + sema.arena, mod, ); + const field_name_ip = try mod.intern_pool.getOrPutString(gpa, field_name); - if (!try sema.intFitsInType(value_val, enum_obj.tag_ty, null)) { + if (!try sema.intFitsInType(value_val, int_tag_ty, null)) { // TODO: better source location return sema.fail(block, src, "field '{s}' with enumeration value '{}' is too large for backing int type '{}'", .{ field_name, value_val.fmtValue(Type.comptime_int, mod), - enum_obj.tag_ty.fmt(mod), + int_tag_ty.fmt(mod), }); } - const gop_field = enum_obj.fields.getOrPutAssumeCapacity(field_name); - if (gop_field.found_existing) { + if (try incomplete_enum.addFieldName(&mod.intern_pool, gpa, field_name_ip)) |other_index| { const msg = msg: { const msg = try sema.errMsg(block, src, "duplicate enum field '{s}'", .{field_name}); 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(msg); } - const copied_tag_val = try value_val.copy(new_decl_arena_allocator); - const gop_val = enum_obj.values.getOrPutAssumeCapacityContext(copied_tag_val, .{ - .ty = enum_obj.tag_ty, - .mod = mod, - }); - if (gop_val.found_existing) { + if (try incomplete_enum.addFieldValue(&mod.intern_pool, gpa, value_val.ip_index)) |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; }; @@ -19188,7 +19132,6 @@ fn zirReify( } } - try new_decl.finalizeNewArena(&new_decl_arena); return sema.analyzeDeclVal(block, src, new_decl_index); }, .Opaque => { @@ -19307,26 +19250,29 @@ fn zirReify( new_namespace.ty = union_ty.toType(); // Tag type - var tag_ty_field_names: ?Module.EnumFull.NameMap = null; - var enum_field_names: ?*Module.EnumNumbered.NameMap = null; const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(mod)); + var explicit_tags_seen: []bool = &.{}; + var explicit_enum_info: ?InternPool.Key.EnumType = null; + var enum_field_names: []InternPool.NullTerminatedString = &.{}; if (tag_type_val.optionalValue(mod)) |payload_val| { - union_obj.tag_ty = try payload_val.toType().copy(new_decl_arena_allocator); + union_obj.tag_ty = payload_val.toType(); - if (union_obj.tag_ty.zigTypeTag(mod) != .Enum) { - return sema.fail(block, src, "Type.Union.tag_type must be an enum type", .{}); - } - tag_ty_field_names = try union_obj.tag_ty.enumFields().clone(sema.arena); + const enum_type = switch (mod.intern_pool.indexToKey(union_obj.tag_ty.ip_index)) { + .enum_type => |x| x, + else => return sema.fail(block, src, "Type.Union.tag_type must be an enum type", .{}), + }; + + explicit_enum_info = enum_type; + explicit_tags_seen = try sema.arena.alloc(bool, enum_type.names.len); + @memset(explicit_tags_seen, false); } else { - union_obj.tag_ty = try sema.generateUnionTagTypeSimple(block, fields_len, null); - enum_field_names = &union_obj.tag_ty.castTag(.enum_simple).?.data.fields; + enum_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, fields_len); } // Fields try union_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len); - var i: usize = 0; - while (i < fields_len) : (i += 1) { + for (0..fields_len) |i| { const elem_val = try fields_val.elemValue(mod, i); const field_struct_val = elem_val.castTag(.aggregate).?.data; // TODO use reflection instead of magic numbers here @@ -19343,13 +19289,14 @@ fn zirReify( mod, ); - if (enum_field_names) |set| { - set.putAssumeCapacity(field_name, {}); + const field_name_ip = try mod.intern_pool.getOrPutString(gpa, field_name); + + if (enum_field_names.len != 0) { + enum_field_names[i] = field_name_ip; } - if (tag_ty_field_names) |*names| { - const enum_has_field = names.orderedRemove(field_name); - if (!enum_has_field) { + if (explicit_enum_info) |tag_info| { + const enum_index = tag_info.nameIndex(mod.intern_pool, field_name_ip) orelse { const msg = msg: { const msg = try sema.errMsg(block, src, "no field named '{s}' in enum '{}'", .{ field_name, union_obj.tag_ty.fmt(mod) }); errdefer msg.destroy(gpa); @@ -19357,7 +19304,11 @@ fn zirReify( break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); - } + }; + // 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 = union_obj.fields.getOrPutAssumeCapacity(field_name); @@ -19409,22 +19360,26 @@ fn zirReify( } } - if (tag_ty_field_names) |names| { - if (names.count() > 0) { + if (explicit_enum_info) |tag_info| { + if (tag_info.names.len > fields_len) { const msg = msg: { const msg = try sema.errMsg(block, src, "enum field(s) missing in union", .{}); errdefer msg.destroy(gpa); const enum_ty = union_obj.tag_ty; - for (names.keys()) |field_name| { - const field_index = enum_ty.enumFieldIndex(field_name).?; - try sema.addFieldErrNote(enum_ty, field_index, msg, "field '{s}' missing, declared here", .{field_name}); + for (tag_info.names, 0..) |field_name, field_index| { + if (explicit_tags_seen[field_index]) continue; + try sema.addFieldErrNote(enum_ty, field_index, msg, "field '{s}' missing, declared here", .{ + mod.intern_pool.stringToSlice(field_name), + }); } try sema.addDeclaredHereNote(msg, union_obj.tag_ty); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); } + } else { + union_obj.tag_ty = try sema.generateUnionTagTypeSimple(block, enum_field_names, null); } try new_decl.finalizeNewArena(&new_decl_arena); @@ -23450,7 +23405,7 @@ fn explainWhyTypeIsComptimeInner( if (mod.typeToStruct(ty)) |struct_obj| { for (struct_obj.fields.values(), 0..) |field, i| { - const field_src_loc = struct_obj.fieldSrcLoc(sema.mod, .{ + const field_src_loc = mod.fieldSrcLoc(struct_obj.owner_decl, .{ .index = i, .range = .type, }); @@ -23469,7 +23424,7 @@ fn explainWhyTypeIsComptimeInner( if (mod.typeToUnion(ty)) |union_obj| { for (union_obj.fields.values(), 0..) |field, i| { - const field_src_loc = union_obj.fieldSrcLoc(sema.mod, .{ + const field_src_loc = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = i, .range = .type, }); @@ -24168,7 +24123,7 @@ fn fieldVal( } const union_ty = try sema.resolveTypeFields(child_type); if (union_ty.unionTagType(mod)) |enum_ty| { - if (enum_ty.enumFieldIndex(field_name)) |field_index_usize| { + if (enum_ty.enumFieldIndex(field_name, mod)) |field_index_usize| { const field_index = @intCast(u32, field_index_usize); return sema.addConstant( enum_ty, @@ -24184,7 +24139,7 @@ fn fieldVal( return inst; } } - const field_index_usize = child_type.enumFieldIndex(field_name) orelse + const field_index_usize = child_type.enumFieldIndex(field_name, mod) orelse return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name); const field_index = @intCast(u32, field_index_usize); const enum_val = try Value.Tag.enum_field_index.create(arena, field_index); @@ -24382,7 +24337,7 @@ fn fieldPtr( } const union_ty = try sema.resolveTypeFields(child_type); if (union_ty.unionTagType(mod)) |enum_ty| { - if (enum_ty.enumFieldIndex(field_name)) |field_index| { + if (enum_ty.enumFieldIndex(field_name, mod)) |field_index| { const field_index_u32 = @intCast(u32, field_index); var anon_decl = try block.startAnonDecl(); defer anon_decl.deinit(); @@ -24401,7 +24356,7 @@ fn fieldPtr( return inst; } } - const field_index = child_type.enumFieldIndex(field_name) orelse { + const field_index = child_type.enumFieldIndex(field_name, mod) orelse { return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name); }; const field_index_u32 = @intCast(u32, field_index); @@ -24996,7 +24951,7 @@ fn unionFieldPtr( .@"volatile" = union_ptr_ty.isVolatilePtr(mod), .@"addrspace" = union_ptr_ty.ptrAddressSpace(mod), }); - const enum_field_index = @intCast(u32, union_obj.tag_ty.enumFieldIndex(field_name).?); + const enum_field_index = @intCast(u32, union_obj.tag_ty.enumFieldIndex(field_name, mod).?); if (initializing and field.ty.zigTypeTag(mod) == .NoReturn) { const msg = msg: { @@ -25028,7 +24983,7 @@ fn unionFieldPtr( if (!tag_matches) { const msg = msg: { const active_index = tag_and_val.tag.castTag(.enum_field_index).?.data; - const active_field_name = union_obj.tag_ty.enumFieldName(active_index); + const active_field_name = union_obj.tag_ty.enumFieldName(active_index, mod); const msg = try sema.errMsg(block, src, "access of union field '{s}' while field '{s}' is active", .{ field_name, active_field_name }); errdefer msg.destroy(sema.gpa); try sema.addDeclaredHereNote(msg, union_ty); @@ -25083,7 +25038,7 @@ fn unionFieldVal( const union_obj = mod.typeToUnion(union_ty).?; const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src); const field = union_obj.fields.values()[field_index]; - const enum_field_index = @intCast(u32, union_obj.tag_ty.enumFieldIndex(field_name).?); + const enum_field_index = @intCast(u32, union_obj.tag_ty.enumFieldIndex(field_name, mod).?); if (try sema.resolveMaybeUndefVal(union_byval)) |union_val| { if (union_val.isUndef()) return sema.addConstUndef(field.ty); @@ -25102,7 +25057,7 @@ fn unionFieldVal( } else { const msg = msg: { const active_index = tag_and_val.tag.castTag(.enum_field_index).?.data; - const active_field_name = union_obj.tag_ty.enumFieldName(active_index); + const active_field_name = union_obj.tag_ty.enumFieldName(active_index, mod); const msg = try sema.errMsg(block, src, "access of union field '{s}' while field '{s}' is active", .{ field_name, active_field_name }); errdefer msg.destroy(sema.gpa); try sema.addDeclaredHereNote(msg, union_ty); @@ -26191,7 +26146,7 @@ fn coerceExtra( // enum literal to enum const val = try sema.resolveConstValue(block, .unneeded, inst, ""); const bytes = val.castTag(.enum_literal).?.data; - const field_index = dest_ty.enumFieldIndex(bytes) orelse { + const field_index = dest_ty.enumFieldIndex(bytes, mod) orelse { const msg = msg: { const msg = try sema.errMsg( block, @@ -28707,7 +28662,7 @@ fn coerceEnumToUnion( try sema.requireRuntimeBlock(block, inst_src, null); - if (tag_ty.isNonexhaustiveEnum()) { + if (tag_ty.isNonexhaustiveEnum(mod)) { const msg = msg: { const msg = try sema.errMsg(block, inst_src, "runtime coercion to union '{}' from non-exhaustive enum", .{ union_ty.fmt(sema.mod), @@ -31605,7 +31560,6 @@ pub fn resolveTypeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool { .error_set_single, .error_set_inferred, .error_set_merged, - .enum_simple, => false, .function => true, @@ -31646,14 +31600,6 @@ pub fn resolveTypeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool { const child_ty = ty.castTag(.anyframe_T).?.data; return sema.resolveTypeRequiresComptime(child_ty); }, - .enum_numbered => { - const tag_ty = ty.castTag(.enum_numbered).?.data.tag_ty; - return sema.resolveTypeRequiresComptime(tag_ty); - }, - .enum_full, .enum_nonexhaustive => { - const tag_ty = ty.cast(Type.Payload.EnumFull).?.data.tag_ty; - return sema.resolveTypeRequiresComptime(tag_ty); - }, }, else => switch (mod.intern_pool.indexToKey(ty.ip_index)) { .int_type => false, @@ -31760,7 +31706,7 @@ pub fn resolveTypeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool { .opaque_type => false, - .enum_type => @panic("TODO"), + .enum_type => |enum_type| try sema.resolveTypeRequiresComptime(enum_type.tag_ty.toType()), // values, not types .un => unreachable, @@ -32284,12 +32230,12 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name); if (gop.found_existing) { const msg = msg: { - const field_src = struct_obj.fieldSrcLoc(sema.mod, .{ .index = field_i }).lazy; + const field_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ .index = field_i }).lazy; const msg = try sema.errMsg(&block_scope, field_src, "duplicate struct field: '{s}'", .{field_name}); errdefer msg.destroy(gpa); const prev_field_index = struct_obj.fields.getIndex(field_name).?; - const prev_field_src = struct_obj.fieldSrcLoc(sema.mod, .{ .index = prev_field_index }); + const prev_field_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ .index = prev_field_index }); try sema.mod.errNoteNonLazy(prev_field_src, msg, "other field here", .{}); try sema.errNote(&block_scope, src, msg, "struct declared here", .{}); break :msg msg; @@ -32325,7 +32271,7 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void if (zir_field.type_ref != .none) { break :ty sema.resolveType(&block_scope, .unneeded, zir_field.type_ref) catch |err| switch (err) { error.NeededSourceLocation => { - const ty_src = struct_obj.fieldSrcLoc(sema.mod, .{ + const ty_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ .index = field_i, .range = .type, }).lazy; @@ -32341,7 +32287,7 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void const ty_ref = try sema.resolveBody(&block_scope, body, struct_obj.zir_index); break :ty sema.analyzeAsType(&block_scope, .unneeded, ty_ref) catch |err| switch (err) { error.NeededSourceLocation => { - const ty_src = struct_obj.fieldSrcLoc(sema.mod, .{ + const ty_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ .index = field_i, .range = .type, }).lazy; @@ -32360,7 +32306,7 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void if (field_ty.zigTypeTag(mod) == .Opaque) { const msg = msg: { - const ty_src = struct_obj.fieldSrcLoc(sema.mod, .{ + const ty_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ .index = field_i, .range = .type, }).lazy; @@ -32374,7 +32320,7 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void } if (field_ty.zigTypeTag(mod) == .NoReturn) { const msg = msg: { - const ty_src = struct_obj.fieldSrcLoc(sema.mod, .{ + const ty_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ .index = field_i, .range = .type, }).lazy; @@ -32388,7 +32334,7 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void } if (struct_obj.layout == .Extern and !try sema.validateExternType(field.ty, .struct_field)) { const msg = msg: { - const ty_src = struct_obj.fieldSrcLoc(sema.mod, .{ + const ty_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ .index = field_i, .range = .type, }); @@ -32403,7 +32349,7 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void return sema.failWithOwnedErrorMsg(msg); } else if (struct_obj.layout == .Packed and !(validatePackedType(field.ty, mod))) { const msg = msg: { - const ty_src = struct_obj.fieldSrcLoc(sema.mod, .{ + const ty_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ .index = field_i, .range = .type, }); @@ -32424,7 +32370,7 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void const align_ref = try sema.resolveBody(&block_scope, body, struct_obj.zir_index); field.abi_align = sema.analyzeAsAlign(&block_scope, .unneeded, align_ref) catch |err| switch (err) { error.NeededSourceLocation => { - const align_src = struct_obj.fieldSrcLoc(sema.mod, .{ + const align_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ .index = field_i, .range = .alignment, }).lazy; @@ -32452,7 +32398,7 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void const field = &struct_obj.fields.values()[field_i]; const coerced = sema.coerce(&block_scope, field.ty, init, .unneeded) catch |err| switch (err) { error.NeededSourceLocation => { - const init_src = struct_obj.fieldSrcLoc(sema.mod, .{ + const init_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ .index = field_i, .range = .value, }).lazy; @@ -32462,7 +32408,7 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void else => |e| return e, }; const default_val = (try sema.resolveMaybeUndefVal(coerced)) orelse { - const init_src = struct_obj.fieldSrcLoc(sema.mod, .{ + const init_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ .index = field_i, .range = .value, }).lazy; @@ -32573,9 +32519,11 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { try union_obj.fields.ensureTotalCapacity(decl_arena_allocator, fields_len); var int_tag_ty: Type = undefined; - var enum_field_names: ?*Module.EnumNumbered.NameMap = null; - var enum_value_map: ?*Module.EnumNumbered.ValueMap = null; - var tag_ty_field_names: ?Module.EnumFull.NameMap = null; + var enum_field_names: []InternPool.NullTerminatedString = &.{}; + var enum_field_vals: []InternPool.Index = &.{}; + var enum_field_vals_map: std.ArrayHashMapUnmanaged(Value, void, Value.ArrayHashContext, false) = .{}; + var explicit_tags_seen: []bool = &.{}; + var explicit_enum_info: ?InternPool.Key.EnumType = null; if (tag_type_ref != .none) { const tag_ty_src: LazySrcLoc = .{ .node_offset_container_tag = src.node_offset.x }; const provided_ty = try sema.resolveType(&block_scope, tag_ty_src, tag_type_ref); @@ -32601,27 +32549,26 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { return sema.failWithOwnedErrorMsg(msg); } } - union_obj.tag_ty = try sema.generateUnionTagTypeNumbered(&block_scope, fields_len, provided_ty, union_obj); - const enum_obj = union_obj.tag_ty.castTag(.enum_numbered).?.data; - enum_field_names = &enum_obj.fields; - enum_value_map = &enum_obj.values; + enum_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, fields_len); + enum_field_vals = try sema.arena.alloc(InternPool.Index, fields_len); } else { // The provided type is the enum tag type. - union_obj.tag_ty = try provided_ty.copy(decl_arena_allocator); - if (union_obj.tag_ty.zigTypeTag(mod) != .Enum) { - return sema.fail(&block_scope, tag_ty_src, "expected enum tag type, found '{}'", .{union_obj.tag_ty.fmt(sema.mod)}); - } + union_obj.tag_ty = provided_ty; + const enum_type = switch (mod.intern_pool.indexToKey(union_obj.tag_ty.ip_index)) { + .enum_type => |x| x, + else => return sema.fail(&block_scope, tag_ty_src, "expected enum tag type, found '{}'", .{union_obj.tag_ty.fmt(sema.mod)}), + }; // The fields of the union must match the enum exactly. - // Store a copy of the enum field names so we can check for - // missing or extraneous fields later. - tag_ty_field_names = try union_obj.tag_ty.enumFields().clone(sema.arena); + // A flag per field is used to check for missing and extraneous fields. + explicit_enum_info = enum_type; + explicit_tags_seen = try sema.arena.alloc(bool, enum_type.names.len); + @memset(explicit_tags_seen, false); } } else { // If auto_enum_tag is false, this is an untagged union. However, for semantic analysis // purposes, we still auto-generate an enum tag type the same way. That the union is // untagged is represented by the Type tag (union vs union_tagged). - union_obj.tag_ty = try sema.generateUnionTagTypeSimple(&block_scope, fields_len, union_obj); - enum_field_names = &union_obj.tag_ty.castTag(.enum_simple).?.data.fields; + enum_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, fields_len); } if (fields_len == 0) { @@ -32675,11 +32622,11 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { break :blk try sema.resolveInst(tag_ref); } else .none; - if (enum_value_map) |map| { + if (enum_field_vals.len != 0) { const copied_val = if (tag_ref != .none) blk: { const val = sema.semaUnionFieldVal(&block_scope, .unneeded, int_tag_ty, tag_ref) catch |err| switch (err) { error.NeededSourceLocation => { - const val_src = union_obj.fieldSrcLoc(sema.mod, .{ + const val_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = field_i, .range = .value, }).lazy; @@ -32690,25 +32637,24 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { }; last_tag_val = val; - // This puts the memory into the union arena, not the enum arena, but - // it is OK since they share the same lifetime. - break :blk try val.copy(decl_arena_allocator); + break :blk val; } else blk: { const val = if (last_tag_val) |val| - try sema.intAdd(val, try mod.intValue(int_tag_ty, 1), int_tag_ty) + try sema.intAdd(val, Value.one_comptime_int, int_tag_ty) else try mod.intValue(int_tag_ty, 0); last_tag_val = val; - break :blk try val.copy(decl_arena_allocator); + break :blk val; }; - const gop = map.getOrPutAssumeCapacityContext(copied_val, .{ + enum_field_vals[field_i] = copied_val.ip_index; + const gop = enum_field_vals_map.getOrPutAssumeCapacityContext(copied_val, .{ .ty = int_tag_ty, .mod = mod, }); if (gop.found_existing) { - const field_src = union_obj.fieldSrcLoc(sema.mod, .{ .index = field_i }).lazy; - const other_field_src = union_obj.fieldSrcLoc(sema.mod, .{ .index = gop.index }).lazy; + const field_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = field_i }).lazy; + const other_field_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = gop.index }).lazy; const msg = msg: { const msg = try sema.errMsg(&block_scope, field_src, "enum tag value {} already taken", .{copied_val.fmtValue(int_tag_ty, sema.mod)}); errdefer msg.destroy(gpa); @@ -32721,8 +32667,9 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { // This string needs to outlive the ZIR code. const field_name = try decl_arena_allocator.dupe(u8, field_name_zir); - if (enum_field_names) |set| { - set.putAssumeCapacity(field_name, {}); + const field_name_ip = try mod.intern_pool.getOrPutString(gpa, field_name); + if (enum_field_names.len != 0) { + enum_field_names[field_i] = field_name_ip; } const field_ty: Type = if (!has_type) @@ -32732,7 +32679,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { else sema.resolveType(&block_scope, .unneeded, field_type_ref) catch |err| switch (err) { error.NeededSourceLocation => { - const ty_src = union_obj.fieldSrcLoc(sema.mod, .{ + const ty_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = field_i, .range = .type, }).lazy; @@ -32749,12 +32696,12 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { const gop = union_obj.fields.getOrPutAssumeCapacity(field_name); if (gop.found_existing) { const msg = msg: { - const field_src = union_obj.fieldSrcLoc(sema.mod, .{ .index = field_i }).lazy; + const field_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = field_i }).lazy; const msg = try sema.errMsg(&block_scope, field_src, "duplicate union field: '{s}'", .{field_name}); errdefer msg.destroy(gpa); const prev_field_index = union_obj.fields.getIndex(field_name).?; - const prev_field_src = union_obj.fieldSrcLoc(sema.mod, .{ .index = prev_field_index }).lazy; + const prev_field_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = prev_field_index }).lazy; try sema.mod.errNoteNonLazy(prev_field_src.toSrcLoc(decl, mod), msg, "other field here", .{}); try sema.errNote(&block_scope, src, msg, "union declared here", .{}); break :msg msg; @@ -32762,26 +32709,31 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { return sema.failWithOwnedErrorMsg(msg); } - if (tag_ty_field_names) |*names| { - const enum_has_field = names.orderedRemove(field_name); - if (!enum_has_field) { + if (explicit_enum_info) |tag_info| { + const enum_index = tag_info.nameIndex(mod.intern_pool, field_name_ip) orelse { const msg = msg: { - const ty_src = union_obj.fieldSrcLoc(sema.mod, .{ + const ty_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = field_i, .range = .type, }).lazy; - const msg = try sema.errMsg(&block_scope, ty_src, "no field named '{s}' in enum '{}'", .{ field_name, union_obj.tag_ty.fmt(sema.mod) }); + const msg = try sema.errMsg(&block_scope, ty_src, "no field named '{s}' in enum '{}'", .{ + field_name, union_obj.tag_ty.fmt(sema.mod), + }); errdefer msg.destroy(sema.gpa); try sema.addDeclaredHereNote(msg, union_obj.tag_ty); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); - } + }; + // 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; } if (field_ty.zigTypeTag(mod) == .Opaque) { const msg = msg: { - const ty_src = union_obj.fieldSrcLoc(sema.mod, .{ + const ty_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = field_i, .range = .type, }).lazy; @@ -32795,7 +32747,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { } if (union_obj.layout == .Extern and !try sema.validateExternType(field_ty, .union_field)) { const msg = msg: { - const ty_src = union_obj.fieldSrcLoc(sema.mod, .{ + const ty_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = field_i, .range = .type, }); @@ -32810,7 +32762,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { return sema.failWithOwnedErrorMsg(msg); } else if (union_obj.layout == .Packed and !(validatePackedType(field_ty, mod))) { const msg = msg: { - const ty_src = union_obj.fieldSrcLoc(sema.mod, .{ + const ty_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = field_i, .range = .type, }); @@ -32833,7 +32785,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { if (align_ref != .none) { gop.value_ptr.abi_align = sema.resolveAlign(&block_scope, .unneeded, align_ref) catch |err| switch (err) { error.NeededSourceLocation => { - const align_src = union_obj.fieldSrcLoc(sema.mod, .{ + const align_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = field_i, .range = .alignment, }).lazy; @@ -32847,22 +32799,28 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { } } - if (tag_ty_field_names) |names| { - if (names.count() > 0) { + if (explicit_enum_info) |tag_info| { + if (tag_info.names.len > fields_len) { const msg = msg: { const msg = try sema.errMsg(&block_scope, src, "enum field(s) missing in union", .{}); errdefer msg.destroy(sema.gpa); const enum_ty = union_obj.tag_ty; - for (names.keys()) |field_name| { - const field_index = enum_ty.enumFieldIndex(field_name).?; - try sema.addFieldErrNote(enum_ty, field_index, msg, "field '{s}' missing, declared here", .{field_name}); + for (tag_info.names, 0..) |field_name, field_index| { + if (explicit_tags_seen[field_index]) continue; + try sema.addFieldErrNote(enum_ty, field_index, msg, "field '{s}' missing, declared here", .{ + mod.intern_pool.stringToSlice(field_name), + }); } try sema.addDeclaredHereNote(msg, union_obj.tag_ty); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); } + } else if (enum_field_vals.len != 0) { + union_obj.tag_ty = try sema.generateUnionTagTypeNumbered(&block_scope, enum_field_names, enum_field_vals, union_obj); + } else { + union_obj.tag_ty = try sema.generateUnionTagTypeSimple(&block_scope, enum_field_names, union_obj); } } @@ -32874,25 +32832,12 @@ fn semaUnionFieldVal(sema: *Sema, block: *Block, src: LazySrcLoc, int_tag_ty: Ty fn generateUnionTagTypeNumbered( sema: *Sema, block: *Block, - fields_len: u32, - int_ty: Type, + enum_field_names: []const InternPool.NullTerminatedString, + enum_field_vals: []const InternPool.Index, union_obj: *Module.Union, ) !Type { const mod = sema.mod; - var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); - errdefer new_decl_arena.deinit(); - const new_decl_arena_allocator = new_decl_arena.allocator(); - - const enum_obj = try new_decl_arena_allocator.create(Module.EnumNumbered); - const enum_ty_payload = try new_decl_arena_allocator.create(Type.Payload.EnumNumbered); - enum_ty_payload.* = .{ - .base = .{ .tag = .enum_numbered }, - .data = enum_obj, - }; - const enum_ty = Type.initPayload(&enum_ty_payload.base); - const enum_val = try Value.Tag.ty.create(new_decl_arena_allocator, enum_ty); - const src_decl = mod.declPtr(block.src_decl); const new_decl_index = try mod.allocateNewDecl(block.namespace, src_decl.src_node, block.wip_capture_scope); errdefer mod.destroyDecl(new_decl_index); @@ -32903,53 +32848,45 @@ fn generateUnionTagTypeNumbered( }; try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, block.namespace, .{ .ty = Type.type, - .val = enum_val, + .val = undefined, }, name); - sema.mod.declPtr(new_decl_index).name_fully_qualified = true; - const new_decl = mod.declPtr(new_decl_index); + new_decl.name_fully_qualified = true; new_decl.owns_tv = true; new_decl.name_fully_qualified = true; errdefer mod.abortAnonDecl(new_decl_index); - const copied_int_ty = try int_ty.copy(new_decl_arena_allocator); - enum_obj.* = .{ - .owner_decl = new_decl_index, - .tag_ty = copied_int_ty, - .fields = .{}, - .values = .{}, - }; - // Here we pre-allocate the maps using the decl arena. - try enum_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len); - try enum_obj.values.ensureTotalCapacityContext(new_decl_arena_allocator, fields_len, .{ - .ty = copied_int_ty, - .mod = mod, - }); - try new_decl.finalizeNewArena(&new_decl_arena); - return enum_ty; + const enum_ty = try mod.intern(.{ .enum_type = .{ + .decl = new_decl_index, + .namespace = .none, + .tag_ty = if (enum_field_vals.len == 0) + .noreturn_type + else + mod.intern_pool.typeOf(enum_field_vals[0]), + .names = enum_field_names, + .values = enum_field_vals, + .tag_mode = .explicit, + } }); + errdefer mod.intern_pool.remove(enum_ty); + + new_decl.val = enum_ty.toValue(); + + return enum_ty.toType(); } -fn generateUnionTagTypeSimple(sema: *Sema, block: *Block, fields_len: usize, maybe_union_obj: ?*Module.Union) !Type { +fn generateUnionTagTypeSimple( + sema: *Sema, + block: *Block, + enum_field_names: []const InternPool.NullTerminatedString, + maybe_union_obj: ?*Module.Union, +) !Type { const mod = sema.mod; - var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); - errdefer new_decl_arena.deinit(); - const new_decl_arena_allocator = new_decl_arena.allocator(); - - const enum_obj = try new_decl_arena_allocator.create(Module.EnumSimple); - const enum_ty_payload = try new_decl_arena_allocator.create(Type.Payload.EnumSimple); - enum_ty_payload.* = .{ - .base = .{ .tag = .enum_simple }, - .data = enum_obj, - }; - const enum_ty = Type.initPayload(&enum_ty_payload.base); - const enum_val = try Value.Tag.ty.create(new_decl_arena_allocator, enum_ty); - const new_decl_index = new_decl_index: { const union_obj = maybe_union_obj orelse { break :new_decl_index try mod.createAnonymousDecl(block, .{ .ty = Type.type, - .val = enum_val, + .val = undefined, }); }; const src_decl = mod.declPtr(block.src_decl); @@ -32962,24 +32899,31 @@ fn generateUnionTagTypeSimple(sema: *Sema, block: *Block, fields_len: usize, may }; try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, block.namespace, .{ .ty = Type.type, - .val = enum_val, + .val = undefined, }, name); - sema.mod.declPtr(new_decl_index).name_fully_qualified = true; + mod.declPtr(new_decl_index).name_fully_qualified = true; break :new_decl_index new_decl_index; }; + const enum_ty = try mod.intern(.{ .enum_type = .{ + .decl = new_decl_index, + .namespace = .none, + .tag_ty = if (enum_field_names.len == 0) + .noreturn_type + else + (try mod.smallestUnsignedInt(enum_field_names.len - 1)).ip_index, + .names = enum_field_names, + .values = &.{}, + .tag_mode = .auto, + } }); + errdefer mod.intern_pool.remove(enum_ty); + const new_decl = mod.declPtr(new_decl_index); new_decl.owns_tv = true; + new_decl.val = enum_ty.toValue(); errdefer mod.abortAnonDecl(new_decl_index); - enum_obj.* = .{ - .owner_decl = new_decl_index, - .fields = .{}, - }; - // Here we pre-allocate the maps using the decl arena. - try enum_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len); - try new_decl.finalizeNewArena(&new_decl_arena); - return enum_ty; + return enum_ty.toType(); } fn getBuiltin(sema: *Sema, name: []const u8) CompileError!Air.Inst.Ref { @@ -33098,57 +33042,6 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { return Value.empty_struct; }, - .enum_numbered => { - const resolved_ty = try sema.resolveTypeFields(ty); - const enum_obj = resolved_ty.castTag(.enum_numbered).?.data; - // An explicit tag type is always provided for enum_numbered. - if (!(try sema.typeHasRuntimeBits(enum_obj.tag_ty))) { - return null; - } - if (enum_obj.fields.count() == 1) { - if (enum_obj.values.count() == 0) { - return Value.enum_field_0; // auto-numbered - } else { - return enum_obj.values.keys()[0]; - } - } else { - return null; - } - }, - .enum_full => { - const resolved_ty = try sema.resolveTypeFields(ty); - const enum_obj = resolved_ty.castTag(.enum_full).?.data; - if (!(try sema.typeHasRuntimeBits(enum_obj.tag_ty))) { - return null; - } - switch (enum_obj.fields.count()) { - 0 => return Value.@"unreachable", - 1 => if (enum_obj.values.count() == 0) { - return Value.enum_field_0; // auto-numbered - } else { - return enum_obj.values.keys()[0]; - }, - else => return null, - } - }, - .enum_simple => { - const resolved_ty = try sema.resolveTypeFields(ty); - const enum_simple = resolved_ty.castTag(.enum_simple).?.data; - switch (enum_simple.fields.count()) { - 0 => return Value.@"unreachable", - 1 => return Value.enum_field_0, - else => return null, - } - }, - .enum_nonexhaustive => { - const tag_ty = ty.castTag(.enum_nonexhaustive).?.data.tag_ty; - if (tag_ty.zigTypeTag(mod) != .ComptimeInt and !(try sema.typeHasRuntimeBits(tag_ty))) { - return Value.enum_field_0; - } else { - return null; - } - }, - .array => { if (ty.arrayLen(mod) == 0) return Value.initTag(.empty_array); @@ -33295,7 +33188,28 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { return only.toValue(); }, .opaque_type => null, - .enum_type => @panic("TODO"), + .enum_type => |enum_type| switch (enum_type.tag_mode) { + .nonexhaustive => { + if (enum_type.tag_ty != .comptime_int_type and + !(try sema.typeHasRuntimeBits(enum_type.tag_ty.toType()))) + { + return Value.enum_field_0; + } else { + return null; + } + }, + .auto, .explicit => switch (enum_type.names.len) { + 0 => return Value.@"unreachable", + 1 => { + if (enum_type.values.len == 0) { + return Value.enum_field_0; // auto-numbered + } else { + return enum_type.values[0].toValue(); + } + }, + else => return null, + }, + }, // values, not types .un => unreachable, @@ -33701,7 +33615,6 @@ pub fn typeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool { .error_set_single, .error_set_inferred, .error_set_merged, - .enum_simple, => false, .function => true, @@ -33742,14 +33655,6 @@ pub fn typeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool { const child_ty = ty.castTag(.anyframe_T).?.data; return sema.typeRequiresComptime(child_ty); }, - .enum_numbered => { - const tag_ty = ty.castTag(.enum_numbered).?.data.tag_ty; - return sema.typeRequiresComptime(tag_ty); - }, - .enum_full, .enum_nonexhaustive => { - const tag_ty = ty.cast(Type.Payload.EnumFull).?.data.tag_ty; - return sema.typeRequiresComptime(tag_ty); - }, }, else => switch (mod.intern_pool.indexToKey(ty.ip_index)) { .int_type => return false, @@ -33865,7 +33770,7 @@ pub fn typeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool { }, .opaque_type => false, - .enum_type => @panic("TODO"), + .enum_type => |enum_type| try sema.typeRequiresComptime(enum_type.tag_ty.toType()), // values, not types .un => unreachable, @@ -34435,42 +34340,19 @@ fn intInRange(sema: *Sema, tag_ty: Type, int_val: Value, end: usize) !bool { /// Asserts the type is an enum. fn enumHasInt(sema: *Sema, ty: Type, int: Value) CompileError!bool { const mod = sema.mod; - switch (ty.tag()) { - .enum_nonexhaustive => unreachable, - .enum_full => { - const enum_full = ty.castTag(.enum_full).?.data; - const tag_ty = enum_full.tag_ty; - if (enum_full.values.count() == 0) { - return sema.intInRange(tag_ty, int, enum_full.fields.count()); - } else { - return enum_full.values.containsContext(int, .{ - .ty = tag_ty, - .mod = sema.mod, - }); - } - }, - .enum_numbered => { - const enum_obj = ty.castTag(.enum_numbered).?.data; - const tag_ty = enum_obj.tag_ty; - if (enum_obj.values.count() == 0) { - return sema.intInRange(tag_ty, int, enum_obj.fields.count()); - } else { - return enum_obj.values.containsContext(int, .{ - .ty = tag_ty, - .mod = sema.mod, - }); - } - }, - .enum_simple => { - const enum_simple = ty.castTag(.enum_simple).?.data; - const fields_len = enum_simple.fields.count(); - const bits = std.math.log2_int_ceil(usize, fields_len); - const tag_ty = try mod.intType(.unsigned, bits); - return sema.intInRange(tag_ty, int, fields_len); - }, - - else => unreachable, + const enum_type = mod.intern_pool.indexToKey(ty.ip_index).enum_type; + assert(enum_type.tag_mode != .nonexhaustive); + if (enum_type.values.len == 0) { + // auto-numbered + return sema.intInRange(enum_type.tag_ty.toType(), int, enum_type.names.len); } + + // The `tagValueIndex` function call below relies on the type being the integer tag type. + // `getCoerced` assumes the value will fit the new type. + if (!(try sema.intFitsInType(int, enum_type.tag_ty.toType(), null))) return false; + const int_coerced = try mod.intern_pool.getCoerced(sema.gpa, int.ip_index, enum_type.tag_ty); + + return enum_type.tagValueIndex(mod.intern_pool, int_coerced) != null; } fn intAddWithOverflow( diff --git a/src/TypedValue.zig b/src/TypedValue.zig index cf9888f357..5f295e42f3 100644 --- a/src/TypedValue.zig +++ b/src/TypedValue.zig @@ -198,7 +198,7 @@ pub fn print( .empty_array => return writer.writeAll(".{}"), .enum_literal => return writer.print(".{}", .{std.zig.fmtId(val.castTag(.enum_literal).?.data)}), .enum_field_index => { - return writer.print(".{s}", .{ty.enumFieldName(val.castTag(.enum_field_index).?.data)}); + return writer.print(".{s}", .{ty.enumFieldName(val.castTag(.enum_field_index).?.data, mod)}); }, .bytes => return writer.print("\"{}\"", .{std.zig.fmtEscapes(val.castTag(.bytes).?.data)}), .str_lit => { diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index c1409e4977..a2f4f81053 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -3101,24 +3101,12 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue { }, .Enum => { if (val.castTag(.enum_field_index)) |field_index| { - switch (ty.tag()) { - .enum_simple => return WValue{ .imm32 = field_index.data }, - .enum_full, .enum_nonexhaustive => { - const enum_full = ty.cast(Type.Payload.EnumFull).?.data; - if (enum_full.values.count() != 0) { - const tag_val = enum_full.values.keys()[field_index.data]; - return func.lowerConstant(tag_val, enum_full.tag_ty); - } else { - return WValue{ .imm32 = field_index.data }; - } - }, - .enum_numbered => { - const index = field_index.data; - const enum_data = ty.castTag(.enum_numbered).?.data; - const enum_val = enum_data.values.keys()[index]; - return func.lowerConstant(enum_val, enum_data.tag_ty); - }, - else => return func.fail("TODO: lowerConstant for enum tag: {}", .{ty.tag()}), + const enum_type = mod.intern_pool.indexToKey(ty.ip_index).enum_type; + if (enum_type.values.len != 0) { + const tag_val = enum_type.values[field_index.data]; + return func.lowerConstant(tag_val.toValue(), enum_type.tag_ty.toType()); + } else { + return WValue{ .imm32 = field_index.data }; } } else { const int_tag_ty = try ty.intTagType(mod); @@ -3240,21 +3228,12 @@ fn valueAsI32(func: *const CodeGen, val: Value, ty: Type) !i32 { switch (ty.zigTypeTag(mod)) { .Enum => { if (val.castTag(.enum_field_index)) |field_index| { - switch (ty.tag()) { - .enum_simple => return @bitCast(i32, field_index.data), - .enum_full, .enum_nonexhaustive => { - const enum_full = ty.cast(Type.Payload.EnumFull).?.data; - if (enum_full.values.count() != 0) { - const tag_val = enum_full.values.keys()[field_index.data]; - return func.valueAsI32(tag_val, enum_full.tag_ty); - } else return @bitCast(i32, field_index.data); - }, - .enum_numbered => { - const index = field_index.data; - const enum_data = ty.castTag(.enum_numbered).?.data; - return func.valueAsI32(enum_data.values.keys()[index], enum_data.tag_ty); - }, - else => unreachable, + const enum_type = mod.intern_pool.indexToKey(ty.ip_index).enum_type; + if (enum_type.values.len != 0) { + const tag_val = enum_type.values[field_index.data]; + return func.valueAsI32(tag_val.toValue(), enum_type.tag_ty.toType()); + } else { + return @bitCast(i32, field_index.data); } } else { const int_tag_ty = try ty.intTagType(mod); @@ -6836,7 +6815,8 @@ fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 { // TODO: Make switch implementation generic so we can use a jump table for this when the tags are not sparse. // generate an if-else chain for each tag value as well as constant. - for (enum_ty.enumFields().keys(), 0..) |tag_name, field_index| { + for (enum_ty.enumFields(mod), 0..) |tag_name_ip, field_index| { + const tag_name = mod.intern_pool.stringToSlice(tag_name_ip); // for each tag name, create an unnamed const, // and then get a pointer to its value. const name_ty = try mod.arrayType(.{ @@ -6846,7 +6826,7 @@ fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 { }); const string_bytes = &mod.string_literal_bytes; try string_bytes.ensureUnusedCapacity(mod.gpa, tag_name.len); - const gop = try mod.string_literal_table.getOrPutContextAdapted(mod.gpa, tag_name, Module.StringLiteralAdapter{ + const gop = try mod.string_literal_table.getOrPutContextAdapted(mod.gpa, @as([]const u8, tag_name), Module.StringLiteralAdapter{ .bytes = string_bytes, }, Module.StringLiteralContext{ .bytes = string_bytes, diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 7b93ff2059..72f416ca87 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -2016,7 +2016,7 @@ fn genLazy(self: *Self, lazy_sym: link.File.LazySymbol) InnerError!void { const ret_reg = param_regs[0]; const enum_mcv = MCValue{ .register = param_regs[1] }; - var exitlude_jump_relocs = try self.gpa.alloc(u32, enum_ty.enumFieldCount()); + var exitlude_jump_relocs = try self.gpa.alloc(u32, enum_ty.enumFieldCount(mod)); defer self.gpa.free(exitlude_jump_relocs); const data_reg = try self.register_manager.allocReg(null, gp); @@ -2027,9 +2027,10 @@ fn genLazy(self: *Self, lazy_sym: link.File.LazySymbol) InnerError!void { var data_off: i32 = 0; for ( exitlude_jump_relocs, - enum_ty.enumFields().keys(), + enum_ty.enumFields(mod), 0.., - ) |*exitlude_jump_reloc, tag_name, index| { + ) |*exitlude_jump_reloc, tag_name_ip, index| { + const tag_name = mod.intern_pool.stringToSlice(tag_name_ip); var tag_pl = Value.Payload.U32{ .base = .{ .tag = .enum_field_index }, .data = @intCast(u32, index), @@ -11413,7 +11414,7 @@ fn airUnionInit(self: *Self, inst: Air.Inst.Index) !void { const union_obj = mod.typeToUnion(union_ty).?; const field_name = union_obj.fields.keys()[extra.field_index]; const tag_ty = union_obj.tag_ty; - const field_index = @intCast(u32, tag_ty.enumFieldIndex(field_name).?); + const field_index = @intCast(u32, tag_ty.enumFieldIndex(field_name, mod).?); var tag_pl = Value.Payload.U32{ .base = .{ .tag = .enum_field_index }, .data = field_index }; const tag_val = Value.initPayload(&tag_pl.base); const tag_int_val = try tag_val.enumToInt(tag_ty, mod); diff --git a/src/codegen.zig b/src/codegen.zig index 5c022392bf..148a69016a 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -156,7 +156,8 @@ pub fn generateLazySymbol( return Result.ok; } else if (lazy_sym.ty.zigTypeTag(mod) == .Enum) { alignment.* = 1; - for (lazy_sym.ty.enumFields().keys()) |tag_name| { + for (lazy_sym.ty.enumFields(mod)) |tag_name_ip| { + const tag_name = mod.intern_pool.stringToSlice(tag_name_ip); try code.ensureUnusedCapacity(tag_name.len + 1); code.appendSliceAssumeCapacity(tag_name); code.appendAssumeCapacity(0); @@ -1229,26 +1230,15 @@ pub fn genTypedValue( }, .Enum => { if (typed_value.val.castTag(.enum_field_index)) |field_index| { - switch (typed_value.ty.tag()) { - .enum_simple => { - return GenResult.mcv(.{ .immediate = field_index.data }); - }, - .enum_numbered, .enum_full, .enum_nonexhaustive => { - const enum_values = if (typed_value.ty.castTag(.enum_numbered)) |pl| - pl.data.values - else - typed_value.ty.cast(Type.Payload.EnumFull).?.data.values; - if (enum_values.count() != 0) { - const tag_val = enum_values.keys()[field_index.data]; - return genTypedValue(bin_file, src_loc, .{ - .ty = try typed_value.ty.intTagType(mod), - .val = tag_val, - }, owner_decl_index); - } else { - return GenResult.mcv(.{ .immediate = field_index.data }); - } - }, - else => unreachable, + const enum_type = mod.intern_pool.indexToKey(typed_value.ty.ip_index).enum_type; + if (enum_type.values.len != 0) { + const tag_val = enum_type.values[field_index.data]; + return genTypedValue(bin_file, src_loc, .{ + .ty = enum_type.tag_ty.toType(), + .val = tag_val.toValue(), + }, owner_decl_index); + } else { + return GenResult.mcv(.{ .immediate = field_index.data }); } } else { const int_tag_ty = try typed_value.ty.intTagType(mod); diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 872bdb94d3..a67d39471a 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1288,27 +1288,12 @@ pub const DeclGen = struct { switch (val.tag()) { .enum_field_index => { const field_index = val.castTag(.enum_field_index).?.data; - switch (ty.tag()) { - .enum_simple => return writer.print("{d}", .{field_index}), - .enum_full, .enum_nonexhaustive => { - const enum_full = ty.cast(Type.Payload.EnumFull).?.data; - if (enum_full.values.count() != 0) { - const tag_val = enum_full.values.keys()[field_index]; - return dg.renderValue(writer, enum_full.tag_ty, tag_val, location); - } else { - return writer.print("{d}", .{field_index}); - } - }, - .enum_numbered => { - const enum_obj = ty.castTag(.enum_numbered).?.data; - if (enum_obj.values.count() != 0) { - const tag_val = enum_obj.values.keys()[field_index]; - return dg.renderValue(writer, enum_obj.tag_ty, tag_val, location); - } else { - return writer.print("{d}", .{field_index}); - } - }, - else => unreachable, + const enum_type = mod.intern_pool.indexToKey(ty.ip_index).enum_type; + if (enum_type.values.len != 0) { + const tag_val = enum_type.values[field_index]; + return dg.renderValue(writer, enum_type.tag_ty.toType(), tag_val.toValue(), location); + } else { + return writer.print("{d}", .{field_index}); } }, else => { @@ -2539,7 +2524,8 @@ pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void { try w.writeByte('('); try o.dg.renderTypeAndName(w, enum_ty, .{ .identifier = "tag" }, Const, 0, .complete); try w.writeAll(") {\n switch (tag) {\n"); - for (enum_ty.enumFields().keys(), 0..) |name, index| { + for (enum_ty.enumFields(mod), 0..) |name_ip, index| { + const name = mod.intern_pool.stringToSlice(name_ip); var tag_pl: Value.Payload.U32 = .{ .base = .{ .tag = .enum_field_index }, .data = @intCast(u32, index), @@ -6930,7 +6916,7 @@ fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue { const field: CValue = if (union_ty.unionTagTypeSafety(mod)) |tag_ty| field: { const layout = union_ty.unionGetLayout(mod); if (layout.tag_size != 0) { - const field_index = tag_ty.enumFieldIndex(field_name).?; + const field_index = tag_ty.enumFieldIndex(field_name, mod).?; var tag_pl: Value.Payload.U32 = .{ .base = .{ .tag = .enum_field_index }, diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index c299253442..583c08583c 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1516,30 +1516,25 @@ pub const Object = struct { return enum_di_ty; } - const field_names = ty.enumFields().keys(); + const ip = &mod.intern_pool; + const enum_type = ip.indexToKey(ty.ip_index).enum_type; - const enumerators = try gpa.alloc(*llvm.DIEnumerator, field_names.len); + const enumerators = try gpa.alloc(*llvm.DIEnumerator, enum_type.names.len); defer gpa.free(enumerators); - var buf_field_index: Value.Payload.U32 = .{ - .base = .{ .tag = .enum_field_index }, - .data = undefined, - }; - const field_index_val = Value.initPayload(&buf_field_index.base); - - const int_ty = try ty.intTagType(mod); + const int_ty = enum_type.tag_ty.toType(); const int_info = ty.intInfo(mod); assert(int_info.bits != 0); - for (field_names, 0..) |field_name, i| { - const field_name_z = try gpa.dupeZ(u8, field_name); - defer gpa.free(field_name_z); + for (enum_type.names, 0..) |field_name_ip, i| { + const field_name_z = ip.stringToSlice(field_name_ip); - buf_field_index.data = @intCast(u32, i); - const field_int_val = try field_index_val.enumToInt(ty, mod); - - var bigint_space: Value.BigIntSpace = undefined; - const bigint = field_int_val.toBigInt(&bigint_space, mod); + var bigint_space: InternPool.Key.Int.Storage.BigIntSpace = undefined; + const storage = if (enum_type.values.len != 0) + ip.indexToKey(enum_type.values[i]).int.storage + else + InternPool.Key.Int.Storage{ .u64 = i }; + const bigint = storage.toBigInt(&bigint_space); if (bigint.limbs.len == 1) { enumerators[i] = dib.createEnumerator(field_name_z, bigint.limbs[0], int_info.signedness == .unsigned); @@ -8852,23 +8847,22 @@ pub const FuncGen = struct { fn getIsNamedEnumValueFunction(self: *FuncGen, enum_ty: Type) !*llvm.Value { const mod = self.dg.module; - const enum_decl = enum_ty.getOwnerDecl(mod); + const enum_type = mod.intern_pool.indexToKey(enum_ty.ip_index).enum_type; // TODO: detect when the type changes and re-emit this function. - const gop = try self.dg.object.named_enum_map.getOrPut(self.dg.gpa, enum_decl); + const gop = try self.dg.object.named_enum_map.getOrPut(self.dg.gpa, enum_type.decl); if (gop.found_existing) return gop.value_ptr.*; - errdefer assert(self.dg.object.named_enum_map.remove(enum_decl)); + errdefer assert(self.dg.object.named_enum_map.remove(enum_type.decl)); var arena_allocator = std.heap.ArenaAllocator.init(self.gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - const fqn = try mod.declPtr(enum_decl).getFullyQualifiedName(mod); + const fqn = try mod.declPtr(enum_type.decl).getFullyQualifiedName(mod); defer self.gpa.free(fqn); const llvm_fn_name = try std.fmt.allocPrintZ(arena, "__zig_is_named_enum_value_{s}", .{fqn}); - const int_tag_ty = try enum_ty.intTagType(mod); - const param_types = [_]*llvm.Type{try self.dg.lowerType(int_tag_ty)}; + const param_types = [_]*llvm.Type{try self.dg.lowerType(enum_type.tag_ty.toType())}; const llvm_ret_ty = try self.dg.lowerType(Type.bool); const fn_type = llvm.functionType(llvm_ret_ty, ¶m_types, param_types.len, .False); @@ -8891,13 +8885,12 @@ pub const FuncGen = struct { self.builder.positionBuilderAtEnd(entry_block); self.builder.clearCurrentDebugLocation(); - const fields = enum_ty.enumFields(); const named_block = self.context.appendBasicBlock(fn_val, "Named"); const unnamed_block = self.context.appendBasicBlock(fn_val, "Unnamed"); const tag_int_value = fn_val.getParam(0); - const switch_instr = self.builder.buildSwitch(tag_int_value, unnamed_block, @intCast(c_uint, fields.count())); + const switch_instr = self.builder.buildSwitch(tag_int_value, unnamed_block, @intCast(c_uint, enum_type.names.len)); - for (fields.keys(), 0..) |_, field_index| { + for (enum_type.names, 0..) |_, field_index| { const this_tag_int_value = int: { var tag_val_payload: Value.Payload.U32 = .{ .base = .{ .tag = .enum_field_index }, @@ -8930,18 +8923,18 @@ pub const FuncGen = struct { fn getEnumTagNameFunction(self: *FuncGen, enum_ty: Type) !*llvm.Value { const mod = self.dg.module; - const enum_decl = enum_ty.getOwnerDecl(mod); + const enum_type = mod.intern_pool.indexToKey(enum_ty.ip_index).enum_type; // TODO: detect when the type changes and re-emit this function. - const gop = try self.dg.object.decl_map.getOrPut(self.dg.gpa, enum_decl); + const gop = try self.dg.object.decl_map.getOrPut(self.dg.gpa, enum_type.decl); if (gop.found_existing) return gop.value_ptr.*; - errdefer assert(self.dg.object.decl_map.remove(enum_decl)); + errdefer assert(self.dg.object.decl_map.remove(enum_type.decl)); var arena_allocator = std.heap.ArenaAllocator.init(self.gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - const fqn = try mod.declPtr(enum_decl).getFullyQualifiedName(mod); + const fqn = try mod.declPtr(enum_type.decl).getFullyQualifiedName(mod); defer self.gpa.free(fqn); const llvm_fn_name = try std.fmt.allocPrintZ(arena, "__zig_tag_name_{s}", .{fqn}); @@ -8950,8 +8943,7 @@ pub const FuncGen = struct { const usize_llvm_ty = try self.dg.lowerType(Type.usize); const slice_alignment = slice_ty.abiAlignment(mod); - const int_tag_ty = try enum_ty.intTagType(mod); - const param_types = [_]*llvm.Type{try self.dg.lowerType(int_tag_ty)}; + const param_types = [_]*llvm.Type{try self.dg.lowerType(enum_type.tag_ty.toType())}; const fn_type = llvm.functionType(llvm_ret_ty, ¶m_types, param_types.len, .False); const fn_val = self.dg.object.llvm_module.addFunction(llvm_fn_name, fn_type); @@ -8973,16 +8965,16 @@ pub const FuncGen = struct { self.builder.positionBuilderAtEnd(entry_block); self.builder.clearCurrentDebugLocation(); - const fields = enum_ty.enumFields(); const bad_value_block = self.context.appendBasicBlock(fn_val, "BadValue"); const tag_int_value = fn_val.getParam(0); - const switch_instr = self.builder.buildSwitch(tag_int_value, bad_value_block, @intCast(c_uint, fields.count())); + const switch_instr = self.builder.buildSwitch(tag_int_value, bad_value_block, @intCast(c_uint, enum_type.names.len)); const array_ptr_indices = [_]*llvm.Value{ usize_llvm_ty.constNull(), usize_llvm_ty.constNull(), }; - for (fields.keys(), 0..) |name, field_index| { + for (enum_type.names, 0..) |name_ip, field_index| { + const name = mod.intern_pool.stringToSlice(name_ip); const str_init = self.context.constString(name.ptr, @intCast(c_uint, name.len), .False); const str_init_llvm_ty = str_init.typeOf(); const str_global = self.dg.object.llvm_module.addGlobal(str_init_llvm_ty, ""); @@ -9429,7 +9421,7 @@ pub const FuncGen = struct { const tag_int = blk: { const tag_ty = union_ty.unionTagTypeHypothetical(mod); const union_field_name = union_obj.fields.keys()[extra.field_index]; - const enum_field_index = tag_ty.enumFieldIndex(union_field_name).?; + const enum_field_index = tag_ty.enumFieldIndex(union_field_name, mod).?; var tag_val_payload: Value.Payload.U32 = .{ .base = .{ .tag = .enum_field_index }, .data = @intCast(u32, enum_field_index), diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index d1e8d9601b..e20e127800 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -401,14 +401,9 @@ pub const DeclState = struct { dbg_info_buffer.appendSliceAssumeCapacity(enum_name); dbg_info_buffer.appendAssumeCapacity(0); - const fields = ty.enumFields(); - const values: ?Module.EnumFull.ValueMap = switch (ty.tag()) { - .enum_full, .enum_nonexhaustive => ty.cast(Type.Payload.EnumFull).?.data.values, - .enum_simple => null, - .enum_numbered => ty.castTag(.enum_numbered).?.data.values, - else => unreachable, - }; - for (fields.keys(), 0..) |field_name, field_i| { + const enum_type = mod.intern_pool.indexToKey(ty.ip_index).enum_type; + for (enum_type.names, 0..) |field_name_index, field_i| { + const field_name = mod.intern_pool.stringToSlice(field_name_index); // DW.AT.enumerator try dbg_info_buffer.ensureUnusedCapacity(field_name.len + 2 + @sizeOf(u64)); dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.enum_variant)); @@ -416,14 +411,14 @@ pub const DeclState = struct { dbg_info_buffer.appendSliceAssumeCapacity(field_name); dbg_info_buffer.appendAssumeCapacity(0); // DW.AT.const_value, DW.FORM.data8 - const value: u64 = if (values) |vals| value: { - if (vals.count() == 0) break :value @intCast(u64, field_i); // auto-numbered - const value = vals.keys()[field_i]; + const value: u64 = value: { + if (enum_type.values.len == 0) break :value field_i; // auto-numbered + const value = enum_type.values[field_i]; // TODO do not assume a 64bit enum value - could be bigger. // See https://github.com/ziglang/zig/issues/645 - const field_int_val = try value.enumToInt(ty, mod); + const field_int_val = try value.toValue().enumToInt(ty, mod); break :value @bitCast(u64, field_int_val.toSignedInt(mod)); - } else @intCast(u64, field_i); + }; mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), value, target_endian); } diff --git a/src/type.zig b/src/type.zig index ab02b29d49..a2644ebff4 100644 --- a/src/type.zig +++ b/src/type.zig @@ -62,12 +62,6 @@ pub const Type = struct { .tuple, .anon_struct, => return .Struct, - - .enum_full, - .enum_nonexhaustive, - .enum_simple, - .enum_numbered, - => return .Enum, }, else => switch (mod.intern_pool.indexToKey(ty.ip_index)) { .int_type => return .Int, @@ -566,22 +560,6 @@ pub const Type = struct { return true; }, - - .enum_full, .enum_nonexhaustive => { - const a_enum_obj = a.cast(Payload.EnumFull).?.data; - const b_enum_obj = (b.cast(Payload.EnumFull) orelse return false).data; - return a_enum_obj == b_enum_obj; - }, - .enum_simple => { - const a_enum_obj = a.cast(Payload.EnumSimple).?.data; - const b_enum_obj = (b.cast(Payload.EnumSimple) orelse return false).data; - return a_enum_obj == b_enum_obj; - }, - .enum_numbered => { - const a_enum_obj = a.cast(Payload.EnumNumbered).?.data; - const b_enum_obj = (b.cast(Payload.EnumNumbered) orelse return false).data; - return a_enum_obj == b_enum_obj; - }, } } @@ -727,22 +705,6 @@ pub const Type = struct { field_val.hash(field_ty, hasher, mod); } }, - - .enum_full, .enum_nonexhaustive => { - const enum_obj: *const Module.EnumFull = ty.cast(Payload.EnumFull).?.data; - std.hash.autoHash(hasher, std.builtin.TypeId.Enum); - std.hash.autoHash(hasher, enum_obj); - }, - .enum_simple => { - const enum_obj: *const Module.EnumSimple = ty.cast(Payload.EnumSimple).?.data; - std.hash.autoHash(hasher, std.builtin.TypeId.Enum); - std.hash.autoHash(hasher, enum_obj); - }, - .enum_numbered => { - const enum_obj: *const Module.EnumNumbered = ty.cast(Payload.EnumNumbered).?.data; - std.hash.autoHash(hasher, std.builtin.TypeId.Enum); - std.hash.autoHash(hasher, enum_obj); - }, } } @@ -920,9 +882,6 @@ pub const Type = struct { .error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet), .error_set_inferred => return self.copyPayloadShallow(allocator, Payload.ErrorSetInferred), .error_set_single => return self.copyPayloadShallow(allocator, Payload.Name), - .enum_simple => return self.copyPayloadShallow(allocator, Payload.EnumSimple), - .enum_numbered => return self.copyPayloadShallow(allocator, Payload.EnumNumbered), - .enum_full, .enum_nonexhaustive => return self.copyPayloadShallow(allocator, Payload.EnumFull), } } @@ -995,25 +954,6 @@ pub const Type = struct { while (true) { const t = ty.tag(); switch (t) { - .enum_full, .enum_nonexhaustive => { - const enum_full = ty.cast(Payload.EnumFull).?.data; - return writer.print("({s} decl={d})", .{ - @tagName(t), enum_full.owner_decl, - }); - }, - .enum_simple => { - const enum_simple = ty.castTag(.enum_simple).?.data; - return writer.print("({s} decl={d})", .{ - @tagName(t), enum_simple.owner_decl, - }); - }, - .enum_numbered => { - const enum_numbered = ty.castTag(.enum_numbered).?.data; - return writer.print("({s} decl={d})", .{ - @tagName(t), enum_numbered.owner_decl, - }); - }, - .function => { const payload = ty.castTag(.function).?.data; try writer.writeAll("fn("); @@ -1199,22 +1139,6 @@ pub const Type = struct { .inferred_alloc_const => unreachable, .inferred_alloc_mut => unreachable, - .enum_full, .enum_nonexhaustive => { - const enum_full = ty.cast(Payload.EnumFull).?.data; - const decl = mod.declPtr(enum_full.owner_decl); - try decl.renderFullyQualifiedName(mod, writer); - }, - .enum_simple => { - const enum_simple = ty.castTag(.enum_simple).?.data; - const decl = mod.declPtr(enum_simple.owner_decl); - try decl.renderFullyQualifiedName(mod, writer); - }, - .enum_numbered => { - const enum_numbered = ty.castTag(.enum_numbered).?.data; - const decl = mod.declPtr(enum_numbered.owner_decl); - try decl.renderFullyQualifiedName(mod, writer); - }, - .error_set_inferred => { const func = ty.castTag(.error_set_inferred).?.data.func; @@ -1500,7 +1424,10 @@ pub const Type = struct { const decl = mod.declPtr(opaque_type.decl); try decl.renderFullyQualifiedName(mod, writer); }, - .enum_type => @panic("TODO"), + .enum_type => |enum_type| { + const decl = mod.declPtr(enum_type.decl); + try decl.renderFullyQualifiedName(mod, writer); + }, // values, not types .un => unreachable, @@ -1593,19 +1520,6 @@ pub const Type = struct { } }, - .enum_full => { - const enum_full = ty.castTag(.enum_full).?.data; - return enum_full.tag_ty.hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat); - }, - .enum_simple => { - const enum_simple = ty.castTag(.enum_simple).?.data; - return enum_simple.fields.count() >= 2; - }, - .enum_numbered, .enum_nonexhaustive => { - const int_tag_ty = try ty.intTagType(mod); - return int_tag_ty.hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat); - }, - .array => return ty.arrayLen(mod) != 0 and try ty.childType(mod).hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat), .array_sentinel => return ty.childType(mod).hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat), @@ -1766,7 +1680,7 @@ pub const Type = struct { }, .opaque_type => true, - .enum_type => @panic("TODO"), + .enum_type => |enum_type| enum_type.tag_ty.toType().hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat), // values, not types .un => unreachable, @@ -1789,9 +1703,7 @@ pub const Type = struct { .empty_struct_type => false, .none => switch (ty.tag()) { - .pointer, - .enum_numbered, - => true, + .pointer => true, .error_set, .error_set_single, @@ -1799,17 +1711,12 @@ pub const Type = struct { .error_set_merged, // These are function bodies, not function pointers. .function, - .enum_simple, .error_union, .anyframe_T, .tuple, .anon_struct, => false, - .enum_full, - .enum_nonexhaustive, - => !ty.cast(Payload.EnumFull).?.data.tag_ty_inferred, - .inferred_alloc_mut => unreachable, .inferred_alloc_const => unreachable, @@ -1886,7 +1793,10 @@ pub const Type = struct { .tagged => false, }, .opaque_type => false, - .enum_type => @panic("TODO"), + .enum_type => |enum_type| switch (enum_type.tag_mode) { + .auto => false, + .explicit, .nonexhaustive => true, + }, // values, not types .un => unreachable, @@ -2116,11 +2026,6 @@ pub const Type = struct { return AbiAlignmentAdvanced{ .scalar = big_align }; }, - .enum_full, .enum_nonexhaustive, .enum_simple, .enum_numbered => { - const int_tag_ty = try ty.intTagType(mod); - return AbiAlignmentAdvanced{ .scalar = int_tag_ty.abiAlignment(mod) }; - }, - .inferred_alloc_const, .inferred_alloc_mut, => unreachable, @@ -2283,7 +2188,7 @@ pub const Type = struct { return abiAlignmentAdvancedUnion(ty, mod, strat, union_obj, union_type.hasTag()); }, .opaque_type => return AbiAlignmentAdvanced{ .scalar = 1 }, - .enum_type => @panic("TODO"), + .enum_type => |enum_type| return AbiAlignmentAdvanced{ .scalar = enum_type.tag_ty.toType().abiAlignment(mod) }, // values, not types .un => unreachable, @@ -2475,11 +2380,6 @@ pub const Type = struct { return AbiSizeAdvanced{ .scalar = ty.structFieldOffset(field_count, mod) }; }, - .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => { - const int_tag_ty = try ty.intTagType(mod); - return AbiSizeAdvanced{ .scalar = int_tag_ty.abiSize(mod) }; - }, - .array => { const payload = ty.castTag(.array).?.data; switch (try payload.elem_type.abiSizeAdvanced(mod, strat)) { @@ -2705,7 +2605,7 @@ pub const Type = struct { return abiSizeAdvancedUnion(ty, mod, strat, union_obj, union_type.hasTag()); }, .opaque_type => unreachable, // no size available - .enum_type => @panic("TODO"), + .enum_type => |enum_type| return AbiSizeAdvanced{ .scalar = enum_type.tag_ty.toType().abiSize(mod) }, // values, not types .un => unreachable, @@ -2823,11 +2723,6 @@ pub const Type = struct { return total; }, - .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => { - const int_tag_ty = try ty.intTagType(mod); - return try bitSizeAdvanced(int_tag_ty, mod, opt_sema); - }, - .array => { const payload = ty.castTag(.array).?.data; const elem_size = std.math.max(payload.elem_type.abiAlignment(mod), payload.elem_type.abiSize(mod)); @@ -2964,7 +2859,7 @@ pub const Type = struct { return size; }, .opaque_type => unreachable, - .enum_type => @panic("TODO"), + .enum_type => |enum_type| return bitSizeAdvanced(enum_type.tag_ty.toType(), mod, opt_sema), // values, not types .un => unreachable, @@ -3433,7 +3328,7 @@ pub const Type = struct { pub fn unionTagFieldIndex(ty: Type, enum_tag: Value, mod: *Module) ?usize { const union_obj = mod.typeToUnion(ty).?; const index = union_obj.tag_ty.enumTagFieldIndex(enum_tag, mod) orelse return null; - const name = union_obj.tag_ty.enumFieldName(index); + const name = union_obj.tag_ty.enumFieldName(index, mod); return union_obj.fields.getIndex(name); } @@ -3690,15 +3585,6 @@ pub const Type = struct { while (true) switch (ty.ip_index) { .none => switch (ty.tag()) { - .enum_full, .enum_nonexhaustive => ty = ty.cast(Payload.EnumFull).?.data.tag_ty, - .enum_numbered => ty = ty.castTag(.enum_numbered).?.data.tag_ty, - .enum_simple => { - const enum_obj = ty.castTag(.enum_simple).?.data; - const field_count = enum_obj.fields.count(); - if (field_count == 0) return .{ .signedness = .unsigned, .bits = 0 }; - return .{ .signedness = .unsigned, .bits = smallestUnsignedBits(field_count - 1) }; - }, - .error_set, .error_set_single, .error_set_inferred, .error_set_merged => { // TODO revisit this when error sets support custom int types return .{ .signedness = .unsigned, .bits = 16 }; @@ -3728,7 +3614,7 @@ pub const Type = struct { assert(struct_obj.layout == .Packed); ty = struct_obj.backing_int_ty; }, - .enum_type => @panic("TODO"), + .enum_type => |enum_type| ty = enum_type.tag_ty.toType(), .ptr_type => unreachable, .array_type => unreachable, @@ -3964,47 +3850,6 @@ pub const Type = struct { return Value.empty_struct; }, - .enum_numbered => { - const enum_numbered = ty.castTag(.enum_numbered).?.data; - // An explicit tag type is always provided for enum_numbered. - if (enum_numbered.tag_ty.hasRuntimeBits(mod)) { - return null; - } - assert(enum_numbered.fields.count() == 1); - return enum_numbered.values.keys()[0]; - }, - .enum_full => { - const enum_full = ty.castTag(.enum_full).?.data; - if (enum_full.tag_ty.hasRuntimeBits(mod)) { - return null; - } - switch (enum_full.fields.count()) { - 0 => return Value.@"unreachable", - 1 => if (enum_full.values.count() == 0) { - return Value.enum_field_0; // auto-numbered - } else { - return enum_full.values.keys()[0]; - }, - else => return null, - } - }, - .enum_simple => { - const enum_simple = ty.castTag(.enum_simple).?.data; - switch (enum_simple.fields.count()) { - 0 => return Value.@"unreachable", - 1 => return Value.enum_field_0, - else => return null, - } - }, - .enum_nonexhaustive => { - const tag_ty = ty.castTag(.enum_nonexhaustive).?.data.tag_ty; - if (!tag_ty.hasRuntimeBits(mod)) { - return Value.enum_field_0; - } else { - return null; - } - }, - .array => { if (ty.arrayLen(mod) == 0) return Value.initTag(.empty_array); @@ -4123,7 +3968,28 @@ pub const Type = struct { return only.toValue(); }, .opaque_type => return null, - .enum_type => @panic("TODO"), + .enum_type => |enum_type| switch (enum_type.tag_mode) { + .nonexhaustive => { + if (enum_type.tag_ty != .comptime_int_type and + !enum_type.tag_ty.toType().hasRuntimeBits(mod)) + { + return Value.enum_field_0; + } else { + return null; + } + }, + .auto, .explicit => switch (enum_type.names.len) { + 0 => return Value.@"unreachable", + 1 => { + if (enum_type.values.len == 0) { + return Value.enum_field_0; // auto-numbered + } else { + return enum_type.values[0].toValue(); + } + }, + else => return null, + }, + }, // values, not types .un => unreachable, @@ -4151,7 +4017,6 @@ pub const Type = struct { .error_set_single, .error_set_inferred, .error_set_merged, - .enum_simple, => false, // These are function bodies, not function pointers. @@ -4191,14 +4056,6 @@ pub const Type = struct { const child_ty = ty.castTag(.anyframe_T).?.data; return child_ty.comptimeOnly(mod); }, - .enum_numbered => { - const tag_ty = ty.castTag(.enum_numbered).?.data.tag_ty; - return tag_ty.comptimeOnly(mod); - }, - .enum_full, .enum_nonexhaustive => { - const tag_ty = ty.cast(Type.Payload.EnumFull).?.data.tag_ty; - return tag_ty.comptimeOnly(mod); - }, }, else => switch (mod.intern_pool.indexToKey(ty.ip_index)) { .int_type => false, @@ -4293,7 +4150,7 @@ pub const Type = struct { .opaque_type => false, - .enum_type => @panic("TODO"), + .enum_type => |enum_type| enum_type.tag_ty.toType().comptimeOnly(mod), // values, not types .un => unreachable, @@ -4346,19 +4203,14 @@ pub const Type = struct { /// Returns null if the type has no namespace. pub fn getNamespaceIndex(ty: Type, mod: *Module) Module.Namespace.OptionalIndex { - return switch (ty.ip_index) { - .none => switch (ty.tag()) { - .enum_full => ty.castTag(.enum_full).?.data.namespace.toOptional(), - .enum_nonexhaustive => ty.castTag(.enum_nonexhaustive).?.data.namespace.toOptional(), - else => .none, - }, - else => switch (mod.intern_pool.indexToKey(ty.ip_index)) { - .opaque_type => |opaque_type| opaque_type.namespace.toOptional(), - .struct_type => |struct_type| struct_type.namespace, - .union_type => |union_type| mod.unionPtr(union_type.index).namespace.toOptional(), + if (ty.ip_index == .none) return .none; + return switch (mod.intern_pool.indexToKey(ty.ip_index)) { + .opaque_type => |opaque_type| opaque_type.namespace.toOptional(), + .struct_type => |struct_type| struct_type.namespace, + .union_type => |union_type| mod.unionPtr(union_type.index).namespace.toOptional(), + .enum_type => |enum_type| enum_type.namespace, - else => .none, - }, + else => .none, }; } @@ -4444,29 +4296,23 @@ pub const Type = struct { /// Asserts the type is an enum or a union. pub fn intTagType(ty: Type, mod: *Module) !Type { - return switch (ty.ip_index) { - .none => switch (ty.tag()) { - .enum_full, .enum_nonexhaustive => ty.cast(Payload.EnumFull).?.data.tag_ty, - .enum_numbered => ty.castTag(.enum_numbered).?.data.tag_ty, - .enum_simple => { - const enum_simple = ty.castTag(.enum_simple).?.data; - const field_count = enum_simple.fields.count(); - const bits: u16 = if (field_count == 0) 0 else std.math.log2_int_ceil(usize, field_count); - return mod.intType(.unsigned, bits); - }, - else => unreachable, - }, - else => switch (mod.intern_pool.indexToKey(ty.ip_index)) { - .union_type => |union_type| mod.unionPtr(union_type.index).tag_ty.intTagType(mod), - else => unreachable, - }, + return switch (mod.intern_pool.indexToKey(ty.ip_index)) { + .union_type => |union_type| mod.unionPtr(union_type.index).tag_ty.intTagType(mod), + .enum_type => |enum_type| enum_type.tag_ty.toType(), + else => unreachable, }; } - pub fn isNonexhaustiveEnum(ty: Type) bool { - return switch (ty.tag()) { - .enum_nonexhaustive => true, - else => false, + pub fn isNonexhaustiveEnum(ty: Type, mod: *Module) bool { + return switch (ty.ip_index) { + .none => false, + else => switch (mod.intern_pool.indexToKey(ty.ip_index)) { + .enum_type => |enum_type| switch (enum_type.tag_mode) { + .nonexhaustive => true, + .auto, .explicit => false, + }, + else => false, + }, }; } @@ -4510,25 +4356,26 @@ pub const Type = struct { return try Tag.error_set_merged.create(arena, names); } - pub fn enumFields(ty: Type) Module.EnumFull.NameMap { - return switch (ty.tag()) { - .enum_full, .enum_nonexhaustive => ty.cast(Payload.EnumFull).?.data.fields, - .enum_simple => ty.castTag(.enum_simple).?.data.fields, - .enum_numbered => ty.castTag(.enum_numbered).?.data.fields, - else => unreachable, - }; + pub fn enumFields(ty: Type, mod: *Module) []const InternPool.NullTerminatedString { + return mod.intern_pool.indexToKey(ty.ip_index).enum_type.names; } - pub fn enumFieldCount(ty: Type) usize { - return ty.enumFields().count(); + pub fn enumFieldCount(ty: Type, mod: *Module) usize { + return mod.intern_pool.indexToKey(ty.ip_index).enum_type.names.len; } - pub fn enumFieldName(ty: Type, field_index: usize) []const u8 { - return ty.enumFields().keys()[field_index]; + pub fn enumFieldName(ty: Type, field_index: usize, mod: *Module) [:0]const u8 { + const ip = &mod.intern_pool; + const field_name = ip.indexToKey(ty.ip_index).enum_type.names[field_index]; + return ip.stringToSlice(field_name); } - pub fn enumFieldIndex(ty: Type, field_name: []const u8) ?usize { - return ty.enumFields().getIndex(field_name); + pub fn enumFieldIndex(ty: Type, field_name: []const u8, mod: *Module) ?usize { + const ip = &mod.intern_pool; + const enum_type = ip.indexToKey(ty.ip_index).enum_type; + // If the string is not interned, then the field certainly is not present. + const field_name_interned = ip.getString(field_name).unwrap() orelse return null; + return enum_type.nameIndex(ip.*, field_name_interned); } /// Asserts `ty` is an enum. `enum_tag` can either be `enum_field_index` or @@ -4538,50 +4385,20 @@ pub const Type = struct { if (enum_tag.castTag(.enum_field_index)) |payload| { return @as(usize, payload.data); } - const S = struct { - fn fieldWithRange(int_ty: Type, int_val: Value, end: usize, m: *Module) ?usize { - if (int_val.compareAllWithZero(.lt, m)) return null; - const end_val = m.intValue(int_ty, end) catch |err| switch (err) { - // TODO: eliminate this failure condition - error.OutOfMemory => @panic("OOM"), - }; - if (int_val.compareScalar(.gte, end_val, int_ty, m)) return null; - return @intCast(usize, int_val.toUnsignedInt(m)); - } - }; - switch (ty.tag()) { - .enum_full, .enum_nonexhaustive => { - const enum_full = ty.cast(Payload.EnumFull).?.data; - const tag_ty = enum_full.tag_ty; - if (enum_full.values.count() == 0) { - return S.fieldWithRange(tag_ty, enum_tag, enum_full.fields.count(), mod); - } else { - return enum_full.values.getIndexContext(enum_tag, .{ - .ty = tag_ty, - .mod = mod, - }); - } - }, - .enum_numbered => { - const enum_obj = ty.castTag(.enum_numbered).?.data; - const tag_ty = enum_obj.tag_ty; - if (enum_obj.values.count() == 0) { - return S.fieldWithRange(tag_ty, enum_tag, enum_obj.fields.count(), mod); - } else { - return enum_obj.values.getIndexContext(enum_tag, .{ - .ty = tag_ty, - .mod = mod, - }); - } - }, - .enum_simple => { - const enum_simple = ty.castTag(.enum_simple).?.data; - const fields_len = enum_simple.fields.count(); - const bits = std.math.log2_int_ceil(usize, fields_len); - const tag_ty = mod.intType(.unsigned, bits) catch @panic("TODO: handle OOM here"); - return S.fieldWithRange(tag_ty, enum_tag, fields_len, mod); - }, - else => unreachable, + const ip = &mod.intern_pool; + const enum_type = ip.indexToKey(ty.ip_index).enum_type; + const tag_ty = enum_type.tag_ty.toType(); + if (enum_type.values.len == 0) { + if (enum_tag.compareAllWithZero(.lt, mod)) return null; + const end_val = mod.intValue(tag_ty, enum_type.names.len) catch |err| switch (err) { + // TODO: eliminate this failure condition + error.OutOfMemory => @panic("OOM"), + }; + if (enum_tag.compareScalar(.gte, end_val, tag_ty, mod)) return null; + return @intCast(usize, enum_tag.toUnsignedInt(mod)); + } else { + assert(ip.typeOf(enum_tag.ip_index) == enum_type.tag_ty); + return enum_type.tagValueIndex(ip.*, enum_tag.ip_index); } } @@ -4905,18 +4722,6 @@ pub const Type = struct { switch (ty.ip_index) { .empty_struct_type => return null, .none => switch (ty.tag()) { - .enum_full, .enum_nonexhaustive => { - const enum_full = ty.cast(Payload.EnumFull).?.data; - return enum_full.srcLoc(mod); - }, - .enum_numbered => { - const enum_numbered = ty.castTag(.enum_numbered).?.data; - return enum_numbered.srcLoc(mod); - }, - .enum_simple => { - const enum_simple = ty.castTag(.enum_simple).?.data; - return enum_simple.srcLoc(mod); - }, .error_set => { const error_set = ty.castTag(.error_set).?.data; return error_set.srcLoc(mod); @@ -4934,6 +4739,7 @@ pub const Type = struct { return union_obj.srcLoc(mod); }, .opaque_type => |opaque_type| mod.opaqueSrcLoc(opaque_type), + .enum_type => |enum_type| mod.declPtr(enum_type.decl).srcLoc(mod), else => null, }, } @@ -4946,15 +4752,6 @@ pub const Type = struct { pub fn getOwnerDeclOrNull(ty: Type, mod: *Module) ?Module.Decl.Index { switch (ty.ip_index) { .none => switch (ty.tag()) { - .enum_full, .enum_nonexhaustive => { - const enum_full = ty.cast(Payload.EnumFull).?.data; - return enum_full.owner_decl; - }, - .enum_numbered => return ty.castTag(.enum_numbered).?.data.owner_decl, - .enum_simple => { - const enum_simple = ty.castTag(.enum_simple).?.data; - return enum_simple.owner_decl; - }, .error_set => { const error_set = ty.castTag(.error_set).?.data; return error_set.owner_decl; @@ -4972,6 +4769,7 @@ pub const Type = struct { return union_obj.owner_decl; }, .opaque_type => |opaque_type| opaque_type.decl, + .enum_type => |enum_type| enum_type.decl, else => null, }, } @@ -5012,10 +4810,6 @@ pub const Type = struct { /// The type is the inferred error set of a specific function. error_set_inferred, error_set_merged, - enum_simple, - enum_numbered, - enum_full, - enum_nonexhaustive, pub const last_no_payload_tag = Tag.inferred_alloc_const; pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; @@ -5040,9 +4834,6 @@ pub const Type = struct { .function => Payload.Function, .error_union => Payload.ErrorUnion, .error_set_single => Payload.Name, - .enum_full, .enum_nonexhaustive => Payload.EnumFull, - .enum_simple => Payload.EnumSimple, - .enum_numbered => Payload.EnumNumbered, .tuple => Payload.Tuple, .anon_struct => Payload.AnonStruct, }; @@ -5341,21 +5132,6 @@ pub const Type = struct { values: []Value, }; }; - - pub const EnumFull = struct { - base: Payload, - data: *Module.EnumFull, - }; - - pub const EnumSimple = struct { - base: Payload = .{ .tag = .enum_simple }, - data: *Module.EnumSimple, - }; - - pub const EnumNumbered = struct { - base: Payload = .{ .tag = .enum_numbered }, - data: *Module.EnumNumbered, - }; }; pub const @"u1": Type = .{ .ip_index = .u1_type, .legacy = undefined }; diff --git a/src/value.zig b/src/value.zig index dfeaa44428..3f7e8050a4 100644 --- a/src/value.zig +++ b/src/value.zig @@ -675,80 +675,50 @@ pub const Value = struct { const field_index = switch (val.tag()) { .enum_field_index => val.castTag(.enum_field_index).?.data, .the_only_possible_value => blk: { - assert(ty.enumFieldCount() == 1); + assert(ty.enumFieldCount(mod) == 1); break :blk 0; }, .enum_literal => i: { const name = val.castTag(.enum_literal).?.data; - break :i ty.enumFieldIndex(name).?; + break :i ty.enumFieldIndex(name, mod).?; }, // Assume it is already an integer and return it directly. else => return val, }; - switch (ty.tag()) { - .enum_full, .enum_nonexhaustive => { - const enum_full = ty.cast(Type.Payload.EnumFull).?.data; - if (enum_full.values.count() != 0) { - return enum_full.values.keys()[field_index]; - } else { - // Field index and integer values are the same. - return mod.intValue(enum_full.tag_ty, field_index); - } - }, - .enum_numbered => { - const enum_obj = ty.castTag(.enum_numbered).?.data; - if (enum_obj.values.count() != 0) { - return enum_obj.values.keys()[field_index]; - } else { - // Field index and integer values are the same. - return mod.intValue(enum_obj.tag_ty, field_index); - } - }, - .enum_simple => { - // Field index and integer values are the same. - const tag_ty = try ty.intTagType(mod); - return mod.intValue(tag_ty, field_index); - }, - else => unreachable, + const enum_type = mod.intern_pool.indexToKey(ty.ip_index).enum_type; + if (enum_type.values.len != 0) { + return enum_type.values[field_index].toValue(); + } else { + // Field index and integer values are the same. + return mod.intValue(enum_type.tag_ty.toType(), field_index); } } pub fn tagName(val: Value, ty: Type, mod: *Module) []const u8 { if (ty.zigTypeTag(mod) == .Union) return val.unionTag().tagName(ty.unionTagTypeHypothetical(mod), mod); + const enum_type = mod.intern_pool.indexToKey(ty.ip_index).enum_type; + const field_index = switch (val.tag()) { .enum_field_index => val.castTag(.enum_field_index).?.data, .the_only_possible_value => blk: { - assert(ty.enumFieldCount() == 1); + assert(ty.enumFieldCount(mod) == 1); break :blk 0; }, .enum_literal => return val.castTag(.enum_literal).?.data, else => field_index: { - const values = switch (ty.tag()) { - .enum_full, .enum_nonexhaustive => ty.cast(Type.Payload.EnumFull).?.data.values, - .enum_numbered => ty.castTag(.enum_numbered).?.data.values, - .enum_simple => Module.EnumFull.ValueMap{}, - else => unreachable, - }; - if (values.entries.len == 0) { + if (enum_type.values.len == 0) { // auto-numbered enum break :field_index @intCast(u32, val.toUnsignedInt(mod)); } - const int_tag_ty = ty.intTagType(mod) catch |err| switch (err) { - error.OutOfMemory => @panic("OOM"), // TODO handle this failure - }; - break :field_index @intCast(u32, values.getIndexContext(val, .{ .ty = int_tag_ty, .mod = mod }).?); + const field_index = enum_type.tagValueIndex(mod.intern_pool, val.ip_index).?; + break :field_index @intCast(u32, field_index); }, }; - const fields = switch (ty.tag()) { - .enum_full, .enum_nonexhaustive => ty.cast(Type.Payload.EnumFull).?.data.fields, - .enum_numbered => ty.castTag(.enum_numbered).?.data.fields, - .enum_simple => ty.castTag(.enum_simple).?.data.fields, - else => unreachable, - }; - return fields.keys()[field_index]; + const field_name = enum_type.names[field_index]; + return mod.intern_pool.stringToSlice(field_name); } /// Asserts the value is an integer.