Commit 5881a2d637
Changed files (13)
src
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,
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);
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 },
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),
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);
}
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,
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);
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();
+}
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);
+ }
+}
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;
- 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();
+ // Because these three things each reference each other, `undefined`
+ // placeholders are used before being set after the enum type gains an
+ // InternPool index.
- 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);
+ var done = false;
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),
- }),
- };
-
- 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);
+ 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);
- 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);
-fn generateUnionTagTypeSimple(sema: *Sema, block: *Block, fields_len: usize, maybe_union_obj: ?*Module.Union) !Type {
- const mod = sema.mod;
+ new_decl.val = enum_ty.toValue();
- var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
- errdefer new_decl_arena.deinit();
- const new_decl_arena_allocator = new_decl_arena.allocator();
+ return enum_ty.toType();
+}
- 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);
+fn generateUnionTagTypeSimple(
+ sema: *Sema,
+ block: *Block,
+ enum_field_names: []const InternPool.NullTerminatedString,
+ maybe_union_obj: ?*Module.Union,
+) !Type {
+ const mod = sema.mod;
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(
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 };
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 => {
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.