stage2: move enum types into the InternPool

Unlike unions and structs, enums are actually *encoded* into the
InternPool directly, rather than using the SegmentedList trick. This
results in them being quite compact, and greatly improved the ergonomics
of using enum types throughout the compiler.

It did however require introducing a new concept to the InternPool which
is an "incomplete" item - something that is added to gain a permanent
Index, but which is then mutated in place. This was necessary because
enum tag values and tag types may reference the namespaces created by
the enum itself, which required constructing the namespace, decl, and
calling analyzeDecl on the decl, which required the decl value, which
required the enum type, which required an InternPool index to be
assigned and for it to be meaningful.

The API for updating enums in place turned out to be quite slick and
efficient - the methods directly populate pre-allocated arrays and
return the information necessary to output the same compilation errors
as before.
This commit is contained in:
Andrew Kelley 2023-05-12 00:07:32 -07:00
parent 404cbc36c5
commit 5881a2d637
13 changed files with 934 additions and 1178 deletions

View file

@ -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,

View file

@ -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();
}

View file

@ -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);
}
}

File diff suppressed because it is too large Load diff

View file

@ -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 => {

View file

@ -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,

View file

@ -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);

View file

@ -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);

View file

@ -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 },

View file

@ -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, &param_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, &param_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),

View file

@ -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);
}

View file

@ -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 };

View file

@ -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.