Commit 7ef1eb1c27

Andrew Kelley <andrew@ziglang.org>
2023-08-17 23:25:18
InternPool: safer enum API
The key changes in this commit are: ```diff - names: []const NullTerminatedString, + names: NullTerminatedString.Slice, - values: []const Index, + values: Index.Slice, ``` Which eliminates the slices from `InternPool.Key.EnumType` and replaces them with structs that contain `start` and `len` indexes. This makes the lifetime of `EnumType` change from expiring with updates to InternPool, to expiring when the InternPool is garbage-collected, which is currently never. This is gearing up for a larger change I started working on locally which moves union types into InternPool. As a bonus, I fixed some unnecessary instances of `@as`.
1 parent 8c1329b
src/codegen/llvm.zig
@@ -1909,12 +1909,12 @@ pub const Object = struct {
                 const int_info = ty.intInfo(mod);
                 assert(int_info.bits != 0);
 
-                for (enum_type.names, 0..) |field_name_ip, i| {
+                for (enum_type.names.get(ip), 0..) |field_name_ip, i| {
                     const field_name_z = ip.stringToSlice(field_name_ip);
 
                     var bigint_space: Value.BigIntSpace = undefined;
                     const bigint = if (enum_type.values.len != 0)
-                        enum_type.values[i].toValue().toBigInt(&bigint_space, mod)
+                        enum_type.values.get(ip)[i].toValue().toBigInt(&bigint_space, mod)
                     else
                         std.math.big.int.Mutable.init(&bigint_space.limbs, i).toConst();
 
@@ -9206,7 +9206,8 @@ pub const FuncGen = struct {
     fn getEnumTagNameFunction(self: *FuncGen, enum_ty: Type) !Builder.Function.Index {
         const o = self.dg.object;
         const mod = o.module;
-        const enum_type = mod.intern_pool.indexToKey(enum_ty.toIntern()).enum_type;
+        const ip = &mod.intern_pool;
+        const enum_type = ip.indexToKey(enum_ty.toIntern()).enum_type;
 
         // TODO: detect when the type changes and re-emit this function.
         const gop = try o.decl_map.getOrPut(o.gpa, enum_type.decl);
@@ -9218,7 +9219,7 @@ pub const FuncGen = struct {
         const fqn = try mod.declPtr(enum_type.decl).getFullyQualifiedName(mod);
         const function_index = try o.builder.addFunction(
             try o.builder.fnType(ret_ty, &.{try o.lowerType(enum_type.tag_ty.toType())}, .normal),
-            try o.builder.fmt("__zig_tag_name_{}", .{fqn.fmt(&mod.intern_pool)}),
+            try o.builder.fmt("__zig_tag_name_{}", .{fqn.fmt(ip)}),
             toLlvmAddressSpace(.generic, mod.getTarget()),
         );
 
@@ -9241,8 +9242,8 @@ pub const FuncGen = struct {
             try wip.@"switch"(tag_int_value, bad_value_block, @intCast(enum_type.names.len));
         defer wip_switch.finish(&wip);
 
-        for (enum_type.names, 0..) |name, field_index| {
-            const name_string = try o.builder.string(mod.intern_pool.stringToSlice(name));
+        for (enum_type.names.get(ip), 0..) |name, field_index| {
+            const name_string = try o.builder.string(ip.stringToSlice(name));
             const name_init = try o.builder.stringNullConst(name_string);
             const name_variable_index =
                 try o.builder.addVariable(.empty, name_init.typeOf(&o.builder), .default);
src/link/Dwarf.zig
@@ -388,9 +388,10 @@ pub const DeclState = struct {
                 try ty.print(dbg_info_buffer.writer(), mod);
                 try dbg_info_buffer.append(0);
 
-                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);
+                const ip = &mod.intern_pool;
+                const enum_type = ip.indexToKey(ty.ip_index).enum_type;
+                for (enum_type.names.get(ip), 0..) |field_name_index, field_i| {
+                    const field_name = ip.stringToSlice(field_name_index);
                     // DW.AT.enumerator
                     try dbg_info_buffer.ensureUnusedCapacity(field_name.len + 2 + @sizeOf(u64));
                     dbg_info_buffer.appendAssumeCapacity(@intFromEnum(AbbrevKind.enum_variant));
@@ -400,7 +401,7 @@ pub const DeclState = struct {
                     // DW.AT.const_value, DW.FORM.data8
                     const value: u64 = value: {
                         if (enum_type.values.len == 0) break :value field_i; // auto-numbered
-                        const value = enum_type.values[field_i];
+                        const value = enum_type.values.get(ip)[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.toValue().intFromEnum(ty, mod);
src/InternPool.zig
@@ -105,7 +105,7 @@ pub const OptionalMapIndex = enum(u32) {
 
     pub fn unwrap(oi: OptionalMapIndex) ?MapIndex {
         if (oi == .none) return null;
-        return @as(MapIndex, @enumFromInt(@intFromEnum(oi)));
+        return @enumFromInt(@intFromEnum(oi));
     }
 };
 
@@ -114,7 +114,7 @@ pub const MapIndex = enum(u32) {
     _,
 
     pub fn toOptional(i: MapIndex) OptionalMapIndex {
-        return @as(OptionalMapIndex, @enumFromInt(@intFromEnum(i)));
+        return @enumFromInt(@intFromEnum(i));
     }
 };
 
@@ -218,7 +218,7 @@ pub const OptionalNullTerminatedString = enum(u32) {
 
     pub fn unwrap(oi: OptionalNullTerminatedString) ?NullTerminatedString {
         if (oi == .none) return null;
-        return @as(NullTerminatedString, @enumFromInt(@intFromEnum(oi)));
+        return @enumFromInt(@intFromEnum(oi));
     }
 };
 
@@ -415,11 +415,11 @@ pub const Key = union(enum) {
         /// explicitly provided tag type or auto-numbered.
         tag_ty: Index,
         /// Set of field names in declaration order.
-        names: []const NullTerminatedString,
+        names: NullTerminatedString.Slice,
         /// Maps integer tag value to field index.
         /// Entries are in declaration order, same as `fields`.
         /// If this is empty, it means the enum tags are auto-numbered.
-        values: []const Index,
+        values: Index.Slice,
         tag_mode: TagMode,
         /// This is ignored by `get` but will always be provided by `indexToKey`.
         names_map: OptionalMapIndex = .none,
@@ -441,9 +441,9 @@ pub const Key = union(enum) {
         /// Look up field index based on field name.
         pub fn nameIndex(self: EnumType, ip: *const InternPool, name: NullTerminatedString) ?u32 {
             const map = &ip.maps.items[@intFromEnum(self.names_map.unwrap().?)];
-            const adapter: NullTerminatedString.Adapter = .{ .strings = self.names };
+            const adapter: NullTerminatedString.Adapter = .{ .strings = self.names.get(ip) };
             const field_index = map.getIndexAdapted(name, adapter) orelse return null;
-            return @as(u32, @intCast(field_index));
+            return @intCast(field_index);
         }
 
         /// Look up field index based on tag value.
@@ -461,9 +461,9 @@ pub const Key = union(enum) {
             };
             if (self.values_map.unwrap()) |values_map| {
                 const map = &ip.maps.items[@intFromEnum(values_map)];
-                const adapter: Index.Adapter = .{ .indexes = self.values };
+                const adapter: Index.Adapter = .{ .indexes = self.values.get(ip) };
                 const field_index = map.getIndexAdapted(int_tag_val, adapter) orelse return null;
-                return @as(u32, @intCast(field_index));
+                return @intCast(field_index);
             }
             // Auto-numbered enum. Convert `int_tag_val` to field index.
             const field_index = switch (ip.indexToKey(int_tag_val).int.storage) {
@@ -497,8 +497,8 @@ pub const Key = union(enum) {
                 .namespace = self.namespace,
                 .tag_ty = self.tag_ty,
                 .tag_mode = self.tag_mode,
-                .names = &.{},
-                .values = &.{},
+                .names = .{ .start = 0, .len = 0 },
+                .values = .{ .start = 0, .len = 0 },
             };
         }
 
@@ -2570,7 +2570,7 @@ pub const Alignment = enum(u6) {
     pub fn fromByteUnits(n: u64) Alignment {
         if (n == 0) return .none;
         assert(std.math.isPowerOfTwo(n));
-        return @as(Alignment, @enumFromInt(@ctz(n)));
+        return @enumFromInt(@ctz(n));
     }
 
     pub fn fromNonzeroByteUnits(n: u64) Alignment {
@@ -2647,11 +2647,11 @@ pub const PackedU64 = packed struct(u64) {
     b: u32,
 
     pub fn get(x: PackedU64) u64 {
-        return @as(u64, @bitCast(x));
+        return @bitCast(x);
     }
 
     pub fn init(x: u64) PackedU64 {
-        return @as(PackedU64, @bitCast(x));
+        return @bitCast(x);
     }
 };
 
@@ -2714,7 +2714,7 @@ pub const Float64 = struct {
 
     pub fn get(self: Float64) f64 {
         const int_bits = @as(u64, self.piece0) | (@as(u64, self.piece1) << 32);
-        return @as(f64, @bitCast(int_bits));
+        return @bitCast(int_bits);
     }
 
     fn pack(val: f64) Float64 {
@@ -2736,7 +2736,7 @@ pub const Float80 = struct {
         const int_bits = @as(u80, self.piece0) |
             (@as(u80, self.piece1) << 32) |
             (@as(u80, self.piece2) << 64);
-        return @as(f80, @bitCast(int_bits));
+        return @bitCast(int_bits);
     }
 
     fn pack(val: f80) Float80 {
@@ -2761,7 +2761,7 @@ pub const Float128 = struct {
             (@as(u128, self.piece1) << 32) |
             (@as(u128, self.piece2) << 64) |
             (@as(u128, self.piece3) << 96);
-        return @as(f128, @bitCast(int_bits));
+        return @bitCast(int_bits);
     }
 
     fn pack(val: f128) Float128 {
@@ -2968,16 +2968,18 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
 
         .type_enum_auto => {
             const enum_auto = ip.extraDataTrail(EnumAuto, data);
-            const names = @as(
-                []const NullTerminatedString,
-                @ptrCast(ip.extra.items[enum_auto.end..][0..enum_auto.data.fields_len]),
-            );
             return .{ .enum_type = .{
                 .decl = enum_auto.data.decl,
                 .namespace = enum_auto.data.namespace,
                 .tag_ty = enum_auto.data.int_tag_type,
-                .names = names,
-                .values = &.{},
+                .names = .{
+                    .start = @intCast(enum_auto.end),
+                    .len = enum_auto.data.fields_len,
+                },
+                .values = .{
+                    .start = 0,
+                    .len = 0,
+                },
                 .tag_mode = .auto,
                 .names_map = enum_auto.data.names_map.toOptional(),
                 .values_map = .none,
@@ -3443,21 +3445,19 @@ fn extraFuncCoerced(ip: *const InternPool, extra_index: u32) Key.Func {
 
 fn indexToKeyEnum(ip: *const InternPool, data: u32, tag_mode: Key.EnumType.TagMode) Key {
     const enum_explicit = ip.extraDataTrail(EnumExplicit, data);
-    const names = @as(
-        []const NullTerminatedString,
-        @ptrCast(ip.extra.items[enum_explicit.end..][0..enum_explicit.data.fields_len]),
-    );
-    const values = if (enum_explicit.data.values_map != .none) @as(
-        []const Index,
-        @ptrCast(ip.extra.items[enum_explicit.end + names.len ..][0..enum_explicit.data.fields_len]),
-    ) else &[0]Index{};
-
+    const fields_len = enum_explicit.data.fields_len;
     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,
+        .names = .{
+            .start = @intCast(enum_explicit.end),
+            .len = fields_len,
+        },
+        .values = .{
+            .start = @intCast(enum_explicit.end + fields_len),
+            .len = if (enum_explicit.data.values_map != .none) fields_len else 0,
+        },
         .tag_mode = tag_mode,
         .names_map = enum_explicit.data.names_map.toOptional(),
         .values_map = enum_explicit.data.values_map,
@@ -3506,7 +3506,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
                     .tag = .type_slice,
                     .data = @intFromEnum(ptr_type_index),
                 });
-                return @as(Index, @enumFromInt(ip.items.len - 1));
+                return @enumFromInt(ip.items.len - 1);
             }
 
             var ptr_type_adjusted = ptr_type;
@@ -3530,7 +3530,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
                             .child = array_type.child,
                         }),
                     });
-                    return @as(Index, @enumFromInt(ip.items.len - 1));
+                    return @enumFromInt(ip.items.len - 1);
                 }
             }
 
@@ -3643,7 +3643,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
             assert(anon_struct_type.types.len == anon_struct_type.values.len);
             for (anon_struct_type.types) |elem| assert(elem != .none);
 
-            const fields_len = @as(u32, @intCast(anon_struct_type.types.len));
+            const fields_len: u32 = @intCast(anon_struct_type.types.len);
             if (anon_struct_type.names.len == 0) {
                 try ip.extra.ensureUnusedCapacity(
                     gpa,
@@ -3655,9 +3655,9 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
                         .fields_len = fields_len,
                     }),
                 });
-                ip.extra.appendSliceAssumeCapacity(@as([]const u32, @ptrCast(anon_struct_type.types)));
-                ip.extra.appendSliceAssumeCapacity(@as([]const u32, @ptrCast(anon_struct_type.values)));
-                return @as(Index, @enumFromInt(ip.items.len - 1));
+                ip.extra.appendSliceAssumeCapacity(@ptrCast(anon_struct_type.types));
+                ip.extra.appendSliceAssumeCapacity(@ptrCast(anon_struct_type.values));
+                return @enumFromInt(ip.items.len - 1);
             }
 
             assert(anon_struct_type.names.len == anon_struct_type.types.len);
@@ -3672,10 +3672,10 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
                     .fields_len = fields_len,
                 }),
             });
-            ip.extra.appendSliceAssumeCapacity(@as([]const u32, @ptrCast(anon_struct_type.types)));
-            ip.extra.appendSliceAssumeCapacity(@as([]const u32, @ptrCast(anon_struct_type.values)));
-            ip.extra.appendSliceAssumeCapacity(@as([]const u32, @ptrCast(anon_struct_type.names)));
-            return @as(Index, @enumFromInt(ip.items.len - 1));
+            ip.extra.appendSliceAssumeCapacity(@ptrCast(anon_struct_type.types));
+            ip.extra.appendSliceAssumeCapacity(@ptrCast(anon_struct_type.values));
+            ip.extra.appendSliceAssumeCapacity(@ptrCast(anon_struct_type.names));
+            return @enumFromInt(ip.items.len - 1);
         },
 
         .union_type => |union_type| {
@@ -3696,38 +3696,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
             });
         },
 
-        .enum_type => |enum_type| {
-            assert(enum_type.tag_ty == .noreturn_type or ip.isIntegerType(enum_type.tag_ty));
-            for (enum_type.values) |value| assert(ip.typeOf(value) == enum_type.tag_ty);
-            assert(enum_type.names_map == .none);
-            assert(enum_type.values_map == .none);
-
-            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 = @as(u32, @intCast(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,
-                            .int_tag_type = enum_type.tag_ty,
-                            .names_map = names_map,
-                            .fields_len = fields_len,
-                        }),
-                    });
-                    ip.extra.appendSliceAssumeCapacity(@as([]const u32, @ptrCast(enum_type.names)));
-                    return @as(Index, @enumFromInt(ip.items.len - 1));
-                },
-                .explicit => return finishGetEnum(ip, gpa, enum_type, .type_enum_explicit),
-                .nonexhaustive => return finishGetEnum(ip, gpa, enum_type, .type_enum_nonexhaustive),
-            }
-        },
-
+        .enum_type => unreachable, // use getEnum() or getIncompleteEnum() instead
         .func_type => unreachable, // use getFuncType() instead
         .extern_func => unreachable, // use getExternFunc() instead
         .func => unreachable, // use getFuncInstance() or getFuncDecl() instead
@@ -3915,7 +3884,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
                             .lazy_ty = lazy_ty,
                         }),
                     });
-                    return @as(Index, @enumFromInt(ip.items.len - 1));
+                    return @enumFromInt(ip.items.len - 1);
                 },
             }
             switch (int.ty) {
@@ -4056,7 +4025,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
                                 .value = casted,
                             }),
                         });
-                        return @as(Index, @enumFromInt(ip.items.len - 1));
+                        return @enumFromInt(ip.items.len - 1);
                     } else |_| {}
 
                     const tag: Tag = if (big_int.positive) .int_positive else .int_negative;
@@ -4071,7 +4040,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
                                 .value = casted,
                             }),
                         });
-                        return @as(Index, @enumFromInt(ip.items.len - 1));
+                        return @enumFromInt(ip.items.len - 1);
                     }
 
                     var buf: [2]Limb = undefined;
@@ -4234,7 +4203,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
                     .tag = .only_possible_value,
                     .data = @intFromEnum(aggregate.ty),
                 });
-                return @as(Index, @enumFromInt(ip.items.len - 1));
+                return @enumFromInt(ip.items.len - 1);
             }
 
             switch (ty_key) {
@@ -4262,7 +4231,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
                         .tag = .only_possible_value,
                         .data = @intFromEnum(aggregate.ty),
                     });
-                    return @as(Index, @enumFromInt(ip.items.len - 1));
+                    return @enumFromInt(ip.items.len - 1);
                 },
                 else => {},
             }
@@ -4301,7 +4270,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
                         .elem_val = elem,
                     }),
                 });
-                return @as(Index, @enumFromInt(ip.items.len - 1));
+                return @enumFromInt(ip.items.len - 1);
             }
 
             if (child == .u8_type) bytes: {
@@ -4345,7 +4314,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
                         .bytes = string,
                     }),
                 });
-                return @as(Index, @enumFromInt(ip.items.len - 1));
+                return @enumFromInt(ip.items.len - 1);
             }
 
             try ip.extra.ensureUnusedCapacity(
@@ -4358,7 +4327,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
                     .ty = aggregate.ty,
                 }),
             });
-            ip.extra.appendSliceAssumeCapacity(@as([]const u32, @ptrCast(aggregate.storage.elems)));
+            ip.extra.appendSliceAssumeCapacity(@ptrCast(aggregate.storage.elems));
             if (sentinel != .none) ip.extra.appendAssumeCapacity(@intFromEnum(sentinel));
         },
 
@@ -4384,7 +4353,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
                     .result = memoized_call.result,
                 }),
             });
-            ip.extra.appendSliceAssumeCapacity(@as([]const u32, @ptrCast(memoized_call.arg_values)));
+            ip.extra.appendSliceAssumeCapacity(@ptrCast(memoized_call.arg_values));
         },
     }
     return @enumFromInt(ip.items.len - 1);
@@ -5017,7 +4986,7 @@ pub const IncompleteEnumType = struct {
             .strings = @as([]const NullTerminatedString, @ptrCast(strings)),
         };
         const gop = try map.getOrPutAdapted(gpa, name, adapter);
-        if (gop.found_existing) return @as(u32, @intCast(gop.index));
+        if (gop.found_existing) return @intCast(gop.index);
         ip.extra.items[self.names_start + field_index] = @intFromEnum(name);
         return null;
     }
@@ -5038,7 +5007,7 @@ pub const IncompleteEnumType = struct {
             .indexes = @as([]const Index, @ptrCast(indexes)),
         };
         const gop = try map.getOrPutAdapted(gpa, value, adapter);
-        if (gop.found_existing) return @as(u32, @intCast(gop.index));
+        if (gop.found_existing) return @intCast(gop.index);
         ip.extra.items[self.values_start + field_index] = @intFromEnum(value);
         return null;
     }
@@ -5158,36 +5127,94 @@ fn getIncompleteEnumExplicit(
     };
 }
 
+pub const GetEnumInit = struct {
+    decl: Module.Decl.Index,
+    namespace: Module.Namespace.OptionalIndex,
+    tag_ty: Index,
+    names: []const NullTerminatedString,
+    values: []const Index,
+    tag_mode: Key.EnumType.TagMode,
+};
+
+pub fn getEnum(ip: *InternPool, gpa: Allocator, ini: GetEnumInit) Allocator.Error!Index {
+    const adapter: KeyAdapter = .{ .intern_pool = ip };
+    const gop = try ip.map.getOrPutAdapted(gpa, Key{
+        .enum_type = .{
+            // Only the decl is used for hashing and equality.
+            .decl = ini.decl,
+
+            .namespace = undefined,
+            .tag_ty = undefined,
+            .names = undefined,
+            .values = undefined,
+            .tag_mode = undefined,
+            .names_map = undefined,
+            .values_map = undefined,
+        },
+    }, adapter);
+    if (gop.found_existing) return @enumFromInt(gop.index);
+    errdefer _ = ip.map.pop();
+    try ip.items.ensureUnusedCapacity(gpa, 1);
+
+    assert(ini.tag_ty == .noreturn_type or ip.isIntegerType(ini.tag_ty));
+    for (ini.values) |value| assert(ip.typeOf(value) == ini.tag_ty);
+
+    switch (ini.tag_mode) {
+        .auto => {
+            const names_map = try ip.addMap(gpa);
+            try addStringsToMap(ip, gpa, names_map, ini.names);
+
+            const fields_len: u32 = @intCast(ini.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 = ini.decl,
+                    .namespace = ini.namespace,
+                    .int_tag_type = ini.tag_ty,
+                    .names_map = names_map,
+                    .fields_len = fields_len,
+                }),
+            });
+            ip.extra.appendSliceAssumeCapacity(@ptrCast(ini.names));
+            return @enumFromInt(ip.items.len - 1);
+        },
+        .explicit => return finishGetEnum(ip, gpa, ini, .type_enum_explicit),
+        .nonexhaustive => return finishGetEnum(ip, gpa, ini, .type_enum_nonexhaustive),
+    }
+}
+
 pub fn finishGetEnum(
     ip: *InternPool,
     gpa: Allocator,
-    enum_type: Key.EnumType,
+    ini: GetEnumInit,
     tag: Tag,
 ) Allocator.Error!Index {
     const names_map = try ip.addMap(gpa);
-    try addStringsToMap(ip, gpa, names_map, enum_type.names);
+    try addStringsToMap(ip, gpa, names_map, ini.names);
 
-    const values_map: OptionalMapIndex = if (enum_type.values.len == 0) .none else m: {
+    const values_map: OptionalMapIndex = if (ini.values.len == 0) .none else m: {
         const values_map = try ip.addMap(gpa);
-        try addIndexesToMap(ip, gpa, values_map, enum_type.values);
+        try addIndexesToMap(ip, gpa, values_map, ini.values);
         break :m values_map.toOptional();
     };
-    const fields_len = @as(u32, @intCast(enum_type.names.len));
+    const fields_len: u32 = @intCast(ini.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,
+            .decl = ini.decl,
+            .namespace = ini.namespace,
+            .int_tag_type = ini.tag_ty,
             .fields_len = fields_len,
             .names_map = names_map,
             .values_map = values_map,
         }),
     });
-    ip.extra.appendSliceAssumeCapacity(@ptrCast(enum_type.names));
-    ip.extra.appendSliceAssumeCapacity(@ptrCast(enum_type.values));
+    ip.extra.appendSliceAssumeCapacity(@ptrCast(ini.names));
+    ip.extra.appendSliceAssumeCapacity(@ptrCast(ini.values));
     return @enumFromInt(ip.items.len - 1);
 }
 
@@ -5486,7 +5513,7 @@ pub fn slicePtrType(ip: *const InternPool, i: Index) Index {
     }
     const item = ip.items.get(@intFromEnum(i));
     switch (item.tag) {
-        .type_slice => return @as(Index, @enumFromInt(item.data)),
+        .type_slice => return @enumFromInt(item.data),
         else => unreachable, // not a slice type
     }
 }
@@ -5618,7 +5645,7 @@ pub fn getCoerced(ip: *InternPool, gpa: Allocator, val: Index, new_ty: Index) Al
                 return ip.get(gpa, .{ .enum_tag = .{
                     .ty = new_ty,
                     .int = if (enum_type.values.len != 0)
-                        enum_type.values[index]
+                        enum_type.values.get(ip)[index]
                     else
                         try ip.get(gpa, .{ .int = .{
                             .ty = enum_type.tag_ty,
@@ -6362,7 +6389,7 @@ pub fn createStruct(
     }
     const ptr = try ip.allocated_structs.addOne(gpa);
     ptr.* = initialization;
-    return @as(Module.Struct.Index, @enumFromInt(ip.allocated_structs.len - 1));
+    return @enumFromInt(ip.allocated_structs.len - 1);
 }
 
 pub fn destroyStruct(ip: *InternPool, gpa: Allocator, index: Module.Struct.Index) void {
@@ -6384,7 +6411,7 @@ pub fn createUnion(
     }
     const ptr = try ip.allocated_unions.addOne(gpa);
     ptr.* = initialization;
-    return @as(Module.Union.Index, @enumFromInt(ip.allocated_unions.len - 1));
+    return @enumFromInt(ip.allocated_unions.len - 1);
 }
 
 pub fn destroyUnion(ip: *InternPool, gpa: Allocator, index: Module.Union.Index) void {
@@ -6406,7 +6433,7 @@ pub fn createDecl(
     }
     const ptr = try ip.allocated_decls.addOne(gpa);
     ptr.* = initialization;
-    return @as(Module.Decl.Index, @enumFromInt(ip.allocated_decls.len - 1));
+    return @enumFromInt(ip.allocated_decls.len - 1);
 }
 
 pub fn destroyDecl(ip: *InternPool, gpa: Allocator, index: Module.Decl.Index) void {
@@ -6428,7 +6455,7 @@ pub fn createNamespace(
     }
     const ptr = try ip.allocated_namespaces.addOne(gpa);
     ptr.* = initialization;
-    return @as(Module.Namespace.Index, @enumFromInt(ip.allocated_namespaces.len - 1));
+    return @enumFromInt(ip.allocated_namespaces.len - 1);
 }
 
 pub fn destroyNamespace(ip: *InternPool, gpa: Allocator, index: Module.Namespace.Index) void {
@@ -6495,11 +6522,11 @@ pub fn getOrPutTrailingString(
     });
     if (gop.found_existing) {
         string_bytes.shrinkRetainingCapacity(str_index);
-        return @as(NullTerminatedString, @enumFromInt(gop.key_ptr.*));
+        return @enumFromInt(gop.key_ptr.*);
     } else {
         gop.key_ptr.* = str_index;
         string_bytes.appendAssumeCapacity(0);
-        return @as(NullTerminatedString, @enumFromInt(str_index));
+        return @enumFromInt(str_index);
     }
 }
 
@@ -6725,7 +6752,7 @@ pub fn typeOf(ip: *const InternPool, index: Index) Index {
 /// Assumes that the enum's field indexes equal its value tags.
 pub fn toEnum(ip: *const InternPool, comptime E: type, i: Index) E {
     const int = ip.indexToKey(i).enum_tag.int;
-    return @as(E, @enumFromInt(ip.indexToKey(int).int.storage.u64));
+    return @enumFromInt(ip.indexToKey(int).int.storage.u64);
 }
 
 pub fn aggregateTypeLen(ip: *const InternPool, ty: Index) u64 {
@@ -6758,9 +6785,9 @@ pub fn funcTypeReturnType(ip: *const InternPool, ty: Index) Index {
         else => unreachable,
     };
     assert(child_item.tag == .type_function);
-    return @as(Index, @enumFromInt(ip.extra.items[
+    return @enumFromInt(ip.extra.items[
         child_item.data + std.meta.fieldIndex(Tag.TypeFunction, "return_type").?
-    ]));
+    ]);
 }
 
 pub fn isNoReturn(ip: *const InternPool, ty: Index) bool {
@@ -6791,9 +6818,9 @@ pub fn getBackingDecl(ip: *const InternPool, val: Index) Module.Decl.OptionalInd
         switch (ip.items.items(.tag)[base]) {
             inline .ptr_decl,
             .ptr_mut_decl,
-            => |tag| return @as(Module.Decl.OptionalIndex, @enumFromInt(ip.extra.items[
+            => |tag| return @enumFromInt(ip.extra.items[
                 ip.items.items(.data)[base] + std.meta.fieldIndex(tag.Payload(), "decl").?
-            ])),
+            ]),
             inline .ptr_eu_payload,
             .ptr_opt_payload,
             .ptr_elem,
src/Module.zig
@@ -6655,7 +6655,7 @@ pub fn enumValueFieldIndex(mod: *Module, ty: Type, field_index: u32) Allocator.E
 
     return (try ip.get(gpa, .{ .enum_tag = .{
         .ty = ty.toIntern(),
-        .int = enum_type.values[field_index],
+        .int = enum_type.values.get(ip)[field_index],
     } })).toValue();
 }
 
src/Sema.zig
@@ -17170,14 +17170,14 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
             for (enum_field_vals, 0..) |*field_val, i| {
                 const enum_type = ip.indexToKey(ty.toIntern()).enum_type;
                 const value_val = if (enum_type.values.len > 0)
-                    try mod.intern_pool.getCoerced(gpa, enum_type.values[i], .comptime_int_type)
+                    try mod.intern_pool.getCoerced(gpa, enum_type.values.get(ip)[i], .comptime_int_type)
                 else
                     try mod.intern(.{ .int = .{
                         .ty = .comptime_int_type,
                         .storage = .{ .u64 = @as(u64, @intCast(i)) },
                     } });
                 // TODO: write something like getCoercedInts to avoid needing to dupe
-                const name = try sema.arena.dupe(u8, ip.stringToSlice(enum_type.names[i]));
+                const name = try sema.arena.dupe(u8, ip.stringToSlice(enum_type.names.get(ip)[i]));
                 const name_val = v: {
                     var anon_decl = try block.startAnonDecl();
                     defer anon_decl.deinit();
@@ -20601,7 +20601,7 @@ fn zirReify(
                         errdefer msg.destroy(gpa);
 
                         const enum_ty = union_obj.tag_ty;
-                        for (tag_info.names, 0..) |field_name, field_index| {
+                        for (tag_info.names.get(ip), 0..) |field_name, field_index| {
                             if (explicit_tags_seen[field_index]) continue;
                             try sema.addFieldErrNote(enum_ty, field_index, msg, "field '{}' missing, declared here", .{
                                 field_name.fmt(ip),
@@ -35420,7 +35420,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
                 errdefer msg.destroy(sema.gpa);
 
                 const enum_ty = union_obj.tag_ty;
-                for (tag_info.names, 0..) |field_name, field_index| {
+                for (tag_info.names.get(ip), 0..) |field_name, field_index| {
                     if (explicit_tags_seen[field_index]) continue;
                     try sema.addFieldErrNote(enum_ty, field_index, msg, "field '{}' missing, declared here", .{
                         field_name.fmt(ip),
@@ -35452,12 +35452,13 @@ fn generateUnionTagTypeNumbered(
 ) !Type {
     const mod = sema.mod;
     const gpa = sema.gpa;
+    const ip = &mod.intern_pool;
 
     const src_decl = mod.declPtr(block.src_decl);
     const new_decl_index = try mod.allocateNewDecl(block.namespace, src_decl.src_node, block.wip_capture_scope);
     errdefer mod.destroyDecl(new_decl_index);
     const fqn = try union_obj.getFullyQualifiedName(mod);
-    const name = try mod.intern_pool.getOrPutStringFmt(gpa, "@typeInfo({}).Union.tag_type.?", .{fqn.fmt(&mod.intern_pool)});
+    const name = try ip.getOrPutStringFmt(gpa, "@typeInfo({}).Union.tag_type.?", .{fqn.fmt(ip)});
     try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, .{
         .ty = Type.noreturn,
         .val = Value.@"unreachable",
@@ -35469,17 +35470,17 @@ fn generateUnionTagTypeNumbered(
     new_decl.owns_tv = true;
     new_decl.name_fully_qualified = true;
 
-    const enum_ty = try mod.intern(.{ .enum_type = .{
+    const enum_ty = try ip.getEnum(gpa, .{
         .decl = new_decl_index,
         .namespace = .none,
         .tag_ty = if (enum_field_vals.len == 0)
             (try mod.intType(.unsigned, 0)).toIntern()
         else
-            mod.intern_pool.typeOf(enum_field_vals[0]),
+            ip.typeOf(enum_field_vals[0]),
         .names = enum_field_names,
         .values = enum_field_vals,
         .tag_mode = .explicit,
-    } });
+    });
 
     new_decl.ty = Type.type;
     new_decl.val = enum_ty.toValue();
@@ -35495,6 +35496,7 @@ fn generateUnionTagTypeSimple(
     maybe_union_obj: ?*Module.Union,
 ) !Type {
     const mod = sema.mod;
+    const ip = &mod.intern_pool;
     const gpa = sema.gpa;
 
     const new_decl_index = new_decl_index: {
@@ -35508,7 +35510,7 @@ fn generateUnionTagTypeSimple(
         const new_decl_index = try mod.allocateNewDecl(block.namespace, src_decl.src_node, block.wip_capture_scope);
         errdefer mod.destroyDecl(new_decl_index);
         const fqn = try union_obj.getFullyQualifiedName(mod);
-        const name = try mod.intern_pool.getOrPutStringFmt(gpa, "@typeInfo({}).Union.tag_type.?", .{fqn.fmt(&mod.intern_pool)});
+        const name = try ip.getOrPutStringFmt(gpa, "@typeInfo({}).Union.tag_type.?", .{fqn.fmt(ip)});
         try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, .{
             .ty = Type.noreturn,
             .val = Value.@"unreachable",
@@ -35518,7 +35520,7 @@ fn generateUnionTagTypeSimple(
     };
     errdefer mod.abortAnonDecl(new_decl_index);
 
-    const enum_ty = try mod.intern(.{ .enum_type = .{
+    const enum_ty = try ip.getEnum(gpa, .{
         .decl = new_decl_index,
         .namespace = .none,
         .tag_ty = if (enum_field_names.len == 0)
@@ -35528,7 +35530,7 @@ fn generateUnionTagTypeSimple(
         .names = enum_field_names,
         .values = &.{},
         .tag_mode = .auto,
-    } });
+    });
 
     const new_decl = mod.declPtr(new_decl_index);
     new_decl.owns_tv = true;
@@ -35625,6 +35627,7 @@ fn getBuiltinType(sema: *Sema, name: []const u8) CompileError!Type {
 /// TODO assert the return value matches `ty.onePossibleValue`
 pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
     const mod = sema.mod;
+    const ip = &mod.intern_pool;
     return switch (ty.toIntern()) {
         .u0_type,
         .i0_type,
@@ -35718,7 +35721,7 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
         .none,
         => unreachable,
 
-        _ => switch (mod.intern_pool.items.items(.tag)[@intFromEnum(ty.toIntern())]) {
+        _ => switch (ip.items.items(.tag)[@intFromEnum(ty.toIntern())]) {
             .type_int_signed, // i0 handled above
             .type_int_unsigned, // u0 handled above
             .type_pointer,
@@ -35801,7 +35804,7 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
             .type_union_tagged,
             .type_union_untagged,
             .type_union_safety,
-            => switch (mod.intern_pool.indexToKey(ty.toIntern())) {
+            => switch (ip.indexToKey(ty.toIntern())) {
                 inline .array_type, .vector_type => |seq_type, seq_tag| {
                     const has_sentinel = seq_tag == .array_type and seq_type.sentinel != .none;
                     if (seq_type.len + @intFromBool(has_sentinel) == 0) return (try mod.intern(.{ .aggregate = .{
@@ -35930,7 +35933,7 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
                                     .storage = .{ .u64 = 0 },
                                 } })
                             else
-                                enum_type.values[0]).toValue(), ty),
+                                enum_type.values.get(ip)[0]).toValue(), ty),
                             else => return null,
                         }
                     },
src/type.zig
@@ -2434,11 +2434,11 @@ pub const Type = struct {
     /// resolves field types rather than asserting they are already resolved.
     pub fn onePossibleValue(starting_type: Type, mod: *Module) !?Value {
         var ty = starting_type;
-
+        const ip = &mod.intern_pool;
         while (true) switch (ty.toIntern()) {
             .empty_struct_type => return Value.empty_struct,
 
-            else => switch (mod.intern_pool.indexToKey(ty.toIntern())) {
+            else => switch (ip.indexToKey(ty.toIntern())) {
                 .int_type => |int_type| {
                     if (int_type.bits == 0) {
                         return try mod.intValue(ty, 0);
@@ -2619,7 +2619,7 @@ pub const Type = struct {
                                     } });
                                     return only.toValue();
                                 } else {
-                                    return enum_type.values[0].toValue();
+                                    return enum_type.values.get(ip)[0].toValue();
                                 }
                             },
                             else => return null,
@@ -2967,7 +2967,8 @@ pub const Type = struct {
     }
 
     pub fn enumFields(ty: Type, mod: *Module) []const InternPool.NullTerminatedString {
-        return mod.intern_pool.indexToKey(ty.toIntern()).enum_type.names;
+        const ip = &mod.intern_pool;
+        return ip.indexToKey(ty.toIntern()).enum_type.names.get(ip);
     }
 
     pub fn enumFieldCount(ty: Type, mod: *Module) usize {
@@ -2975,7 +2976,8 @@ pub const Type = struct {
     }
 
     pub fn enumFieldName(ty: Type, field_index: usize, mod: *Module) InternPool.NullTerminatedString {
-        return mod.intern_pool.indexToKey(ty.toIntern()).enum_type.names[field_index];
+        const ip = &mod.intern_pool;
+        return ip.indexToKey(ty.toIntern()).enum_type.names.get(ip)[field_index];
     }
 
     pub fn enumFieldIndex(ty: Type, field_name: InternPool.NullTerminatedString, mod: *Module) ?u32 {
src/TypedValue.zig
@@ -238,7 +238,7 @@ pub fn print(
                 }
                 const enum_type = ip.indexToKey(ty.toIntern()).enum_type;
                 if (enum_type.tagValueIndex(ip, val.toIntern())) |tag_index| {
-                    try writer.print(".{i}", .{enum_type.names[tag_index].fmt(ip)});
+                    try writer.print(".{i}", .{enum_type.names.get(ip)[tag_index].fmt(ip)});
                     return;
                 }
                 try writer.writeAll("@enumFromInt(");
src/value.zig
@@ -426,7 +426,7 @@ pub const Value = struct {
                     // Assume it is already an integer and return it directly.
                     .simple_type, .int_type => val,
                     .enum_type => |enum_type| if (enum_type.values.len != 0)
-                        enum_type.values[field_index].toValue()
+                        enum_type.values.get(ip)[field_index].toValue()
                     else // Field index and integer values are the same.
                         mod.intValue(enum_type.tag_ty.toType(), field_index),
                     else => unreachable,