Commit 7e19c95668

Andrew Kelley <andrew@ziglang.org>
2023-05-21 02:55:40
Sema: move `inferred_alloc_const/mut_type` to InternPool
Now, all types are migrated to use `InternPool`. The `Type.Tag` enum is deleted in this commit.
1 parent 65d65f5
src/codegen/c.zig
@@ -5367,116 +5367,111 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
     // Ensure complete type definition is visible before accessing fields.
     _ = try f.typeToIndex(struct_ty, .complete);
 
-    const field_name: CValue = switch (struct_ty.ip_index) {
-        .none => switch (struct_ty.tag()) {
-            else => unreachable,
-        },
-        else => switch (mod.intern_pool.indexToKey(struct_ty.ip_index)) {
-            .struct_type => switch (struct_ty.containerLayout(mod)) {
-                .Auto, .Extern => if (struct_ty.isSimpleTuple(mod))
-                    .{ .field = extra.field_index }
-                else
-                    .{ .identifier = struct_ty.structFieldName(extra.field_index, mod) },
-                .Packed => {
-                    const struct_obj = mod.typeToStruct(struct_ty).?;
-                    const int_info = struct_ty.intInfo(mod);
-
-                    const bit_offset_ty = try mod.intType(.unsigned, Type.smallestUnsignedBits(int_info.bits - 1));
+    const field_name: CValue = switch (mod.intern_pool.indexToKey(struct_ty.ip_index)) {
+        .struct_type => switch (struct_ty.containerLayout(mod)) {
+            .Auto, .Extern => if (struct_ty.isSimpleTuple(mod))
+                .{ .field = extra.field_index }
+            else
+                .{ .identifier = struct_ty.structFieldName(extra.field_index, mod) },
+            .Packed => {
+                const struct_obj = mod.typeToStruct(struct_ty).?;
+                const int_info = struct_ty.intInfo(mod);
 
-                    const bit_offset = struct_obj.packedFieldBitOffset(mod, extra.field_index);
-                    const bit_offset_val = try mod.intValue(bit_offset_ty, bit_offset);
+                const bit_offset_ty = try mod.intType(.unsigned, Type.smallestUnsignedBits(int_info.bits - 1));
 
-                    const field_int_signedness = if (inst_ty.isAbiInt(mod))
-                        inst_ty.intInfo(mod).signedness
-                    else
-                        .unsigned;
-                    const field_int_ty = try mod.intType(field_int_signedness, @intCast(u16, inst_ty.bitSize(mod)));
+                const bit_offset = struct_obj.packedFieldBitOffset(mod, extra.field_index);
+                const bit_offset_val = try mod.intValue(bit_offset_ty, bit_offset);
 
-                    const temp_local = try f.allocLocal(inst, field_int_ty);
-                    try f.writeCValue(writer, temp_local, .Other);
-                    try writer.writeAll(" = zig_wrap_");
-                    try f.object.dg.renderTypeForBuiltinFnName(writer, field_int_ty);
-                    try writer.writeAll("((");
-                    try f.renderType(writer, field_int_ty);
+                const field_int_signedness = if (inst_ty.isAbiInt(mod))
+                    inst_ty.intInfo(mod).signedness
+                else
+                    .unsigned;
+                const field_int_ty = try mod.intType(field_int_signedness, @intCast(u16, inst_ty.bitSize(mod)));
+
+                const temp_local = try f.allocLocal(inst, field_int_ty);
+                try f.writeCValue(writer, temp_local, .Other);
+                try writer.writeAll(" = zig_wrap_");
+                try f.object.dg.renderTypeForBuiltinFnName(writer, field_int_ty);
+                try writer.writeAll("((");
+                try f.renderType(writer, field_int_ty);
+                try writer.writeByte(')');
+                const cant_cast = int_info.bits > 64;
+                if (cant_cast) {
+                    if (field_int_ty.bitSize(mod) > 64) return f.fail("TODO: C backend: implement casting between types > 64 bits", .{});
+                    try writer.writeAll("zig_lo_");
+                    try f.object.dg.renderTypeForBuiltinFnName(writer, struct_ty);
+                    try writer.writeByte('(');
+                }
+                if (bit_offset > 0) {
+                    try writer.writeAll("zig_shr_");
+                    try f.object.dg.renderTypeForBuiltinFnName(writer, struct_ty);
+                    try writer.writeByte('(');
+                }
+                try f.writeCValue(writer, struct_byval, .Other);
+                if (bit_offset > 0) {
+                    try writer.writeAll(", ");
+                    try f.object.dg.renderValue(writer, bit_offset_ty, bit_offset_val, .FunctionArgument);
                     try writer.writeByte(')');
-                    const cant_cast = int_info.bits > 64;
-                    if (cant_cast) {
-                        if (field_int_ty.bitSize(mod) > 64) return f.fail("TODO: C backend: implement casting between types > 64 bits", .{});
-                        try writer.writeAll("zig_lo_");
-                        try f.object.dg.renderTypeForBuiltinFnName(writer, struct_ty);
-                        try writer.writeByte('(');
-                    }
-                    if (bit_offset > 0) {
-                        try writer.writeAll("zig_shr_");
-                        try f.object.dg.renderTypeForBuiltinFnName(writer, struct_ty);
-                        try writer.writeByte('(');
-                    }
-                    try f.writeCValue(writer, struct_byval, .Other);
-                    if (bit_offset > 0) {
-                        try writer.writeAll(", ");
-                        try f.object.dg.renderValue(writer, bit_offset_ty, bit_offset_val, .FunctionArgument);
-                        try writer.writeByte(')');
-                    }
-                    if (cant_cast) try writer.writeByte(')');
-                    try f.object.dg.renderBuiltinInfo(writer, field_int_ty, .bits);
-                    try writer.writeAll(");\n");
-                    if (inst_ty.eql(field_int_ty, f.object.dg.module)) return temp_local;
+                }
+                if (cant_cast) try writer.writeByte(')');
+                try f.object.dg.renderBuiltinInfo(writer, field_int_ty, .bits);
+                try writer.writeAll(");\n");
+                if (inst_ty.eql(field_int_ty, f.object.dg.module)) return temp_local;
 
-                    const local = try f.allocLocal(inst, inst_ty);
-                    try writer.writeAll("memcpy(");
-                    try f.writeCValue(writer, .{ .local_ref = local.new_local }, .FunctionArgument);
-                    try writer.writeAll(", ");
-                    try f.writeCValue(writer, .{ .local_ref = temp_local.new_local }, .FunctionArgument);
-                    try writer.writeAll(", sizeof(");
-                    try f.renderType(writer, inst_ty);
-                    try writer.writeAll("));\n");
-                    try freeLocal(f, inst, temp_local.new_local, 0);
-                    return local;
-                },
+                const local = try f.allocLocal(inst, inst_ty);
+                try writer.writeAll("memcpy(");
+                try f.writeCValue(writer, .{ .local_ref = local.new_local }, .FunctionArgument);
+                try writer.writeAll(", ");
+                try f.writeCValue(writer, .{ .local_ref = temp_local.new_local }, .FunctionArgument);
+                try writer.writeAll(", sizeof(");
+                try f.renderType(writer, inst_ty);
+                try writer.writeAll("));\n");
+                try freeLocal(f, inst, temp_local.new_local, 0);
+                return local;
             },
+        },
 
-            .anon_struct_type => |anon_struct_type| if (anon_struct_type.names.len == 0)
-                .{ .field = extra.field_index }
-            else
-                .{ .identifier = struct_ty.structFieldName(extra.field_index, mod) },
+        .anon_struct_type => |anon_struct_type| if (anon_struct_type.names.len == 0)
+            .{ .field = extra.field_index }
+        else
+            .{ .identifier = struct_ty.structFieldName(extra.field_index, mod) },
+
+        .union_type => |union_type| field_name: {
+            const union_obj = mod.unionPtr(union_type.index);
+            if (union_obj.layout == .Packed) {
+                const operand_lval = if (struct_byval == .constant) blk: {
+                    const operand_local = try f.allocLocal(inst, struct_ty);
+                    try f.writeCValue(writer, operand_local, .Other);
+                    try writer.writeAll(" = ");
+                    try f.writeCValue(writer, struct_byval, .Initializer);
+                    try writer.writeAll(";\n");
+                    break :blk operand_local;
+                } else struct_byval;
 
-            .union_type => |union_type| field_name: {
-                const union_obj = mod.unionPtr(union_type.index);
-                if (union_obj.layout == .Packed) {
-                    const operand_lval = if (struct_byval == .constant) blk: {
-                        const operand_local = try f.allocLocal(inst, struct_ty);
-                        try f.writeCValue(writer, operand_local, .Other);
-                        try writer.writeAll(" = ");
-                        try f.writeCValue(writer, struct_byval, .Initializer);
-                        try writer.writeAll(";\n");
-                        break :blk operand_local;
-                    } else struct_byval;
-
-                    const local = try f.allocLocal(inst, inst_ty);
-                    try writer.writeAll("memcpy(&");
-                    try f.writeCValue(writer, local, .Other);
-                    try writer.writeAll(", &");
-                    try f.writeCValue(writer, operand_lval, .Other);
-                    try writer.writeAll(", sizeof(");
-                    try f.renderType(writer, inst_ty);
-                    try writer.writeAll("));\n");
-
-                    if (struct_byval == .constant) {
-                        try freeLocal(f, inst, operand_lval.new_local, 0);
-                    }
+                const local = try f.allocLocal(inst, inst_ty);
+                try writer.writeAll("memcpy(&");
+                try f.writeCValue(writer, local, .Other);
+                try writer.writeAll(", &");
+                try f.writeCValue(writer, operand_lval, .Other);
+                try writer.writeAll(", sizeof(");
+                try f.renderType(writer, inst_ty);
+                try writer.writeAll("));\n");
 
-                    return local;
-                } else {
-                    const name = union_obj.fields.keys()[extra.field_index];
-                    break :field_name if (union_type.hasTag()) .{
-                        .payload_identifier = name,
-                    } else .{
-                        .identifier = name,
-                    };
+                if (struct_byval == .constant) {
+                    try freeLocal(f, inst, operand_lval.new_local, 0);
                 }
-            },
-            else => unreachable,
+
+                return local;
+            } else {
+                const name = union_obj.fields.keys()[extra.field_index];
+                break :field_name if (union_type.hasTag()) .{
+                    .payload_identifier = name,
+                } else .{
+                    .identifier = name,
+                };
+            }
         },
+        else => unreachable,
     };
 
     const local = try f.allocLocal(inst, inst_ty);
src/codegen/llvm.zig
@@ -381,12 +381,7 @@ pub const Object = struct {
 
     /// This is an ArrayHashMap as opposed to a HashMap because in `flushModule` we
     /// want to iterate over it while adding entries to it.
-    pub const DITypeMap = std.ArrayHashMapUnmanaged(
-        Type,
-        AnnotatedDITypePtr,
-        Type.HashContext32,
-        true,
-    );
+    pub const DITypeMap = std.AutoArrayHashMapUnmanaged(InternPool.Index, AnnotatedDITypePtr);
 
     pub fn create(gpa: Allocator, options: link.Options) !*Object {
         const obj = try gpa.create(Object);
@@ -1437,7 +1432,7 @@ pub const Object = struct {
         const gpa = o.gpa;
         // Be careful not to reference this `gop` variable after any recursive calls
         // to `lowerDebugType`.
-        const gop = try o.di_type_map.getOrPutContext(gpa, ty, .{ .mod = o.module });
+        const gop = try o.di_type_map.getOrPut(gpa, ty.toIntern());
         if (gop.found_existing) {
             const annotated = gop.value_ptr.*;
             const di_type = annotated.toDIType();
@@ -1450,7 +1445,7 @@ pub const Object = struct {
             };
             return o.lowerDebugTypeImpl(entry, resolve, di_type);
         }
-        errdefer assert(o.di_type_map.orderedRemoveContext(ty, .{ .mod = o.module }));
+        errdefer assert(o.di_type_map.orderedRemove(ty.toIntern()));
         const entry: Object.DITypeMap.Entry = .{
             .key_ptr = gop.key_ptr,
             .value_ptr = gop.value_ptr,
@@ -1465,7 +1460,7 @@ pub const Object = struct {
         resolve: DebugResolveStatus,
         opt_fwd_decl: ?*llvm.DIType,
     ) Allocator.Error!*llvm.DIType {
-        const ty = gop.key_ptr.*;
+        const ty = gop.key_ptr.toType();
         const gpa = o.gpa;
         const target = o.target;
         const dib = o.di_builder.?;
@@ -1498,7 +1493,7 @@ pub const Object = struct {
                     const enum_di_ty = try o.makeEmptyNamespaceDIType(owner_decl_index);
                     // The recursive call to `lowerDebugType` via `makeEmptyNamespaceDIType`
                     // means we can't use `gop` anymore.
-                    try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(enum_di_ty), .{ .mod = o.module });
+                    try o.di_type_map.put(gpa, ty.toIntern(), AnnotatedDITypePtr.initFull(enum_di_ty));
                     return enum_di_ty;
                 }
 
@@ -1558,7 +1553,7 @@ pub const Object = struct {
                     "",
                 );
                 // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
-                try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(enum_di_ty), .{ .mod = o.module });
+                try o.di_type_map.put(gpa, ty.toIntern(), AnnotatedDITypePtr.initFull(enum_di_ty));
                 return enum_di_ty;
             },
             .Float => {
@@ -1577,7 +1572,7 @@ pub const Object = struct {
             },
             .Pointer => {
                 // Normalize everything that the debug info does not represent.
-                const ptr_info = ty.ptrInfoIp(mod.intern_pool);
+                const ptr_info = Type.ptrInfoIp(mod.intern_pool, ty.toIntern());
 
                 if (ptr_info.sentinel != .none or
                     ptr_info.address_space != .generic or
@@ -1603,7 +1598,7 @@ pub const Object = struct {
                     });
                     const ptr_di_ty = try o.lowerDebugType(bland_ptr_ty, resolve);
                     // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
-                    try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.init(ptr_di_ty, resolve), .{ .mod = o.module });
+                    try o.di_type_map.put(gpa, ty.toIntern(), AnnotatedDITypePtr.init(ptr_di_ty, resolve));
                     return ptr_di_ty;
                 }
 
@@ -1682,7 +1677,7 @@ pub const Object = struct {
                     );
                     dib.replaceTemporary(fwd_decl, full_di_ty);
                     // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
-                    try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .mod = o.module });
+                    try o.di_type_map.put(gpa, ty.toIntern(), AnnotatedDITypePtr.initFull(full_di_ty));
                     return full_di_ty;
                 }
 
@@ -1696,7 +1691,7 @@ pub const Object = struct {
                     name,
                 );
                 // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
-                try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(ptr_di_ty), .{ .mod = o.module });
+                try o.di_type_map.put(gpa, ty.toIntern(), AnnotatedDITypePtr.initFull(ptr_di_ty));
                 return ptr_di_ty;
             },
             .Opaque => {
@@ -1718,7 +1713,7 @@ pub const Object = struct {
                 );
                 // The recursive call to `lowerDebugType` va `namespaceToDebugScope`
                 // means we can't use `gop` anymore.
-                try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(opaque_di_ty), .{ .mod = o.module });
+                try o.di_type_map.put(gpa, ty.toIntern(), AnnotatedDITypePtr.initFull(opaque_di_ty));
                 return opaque_di_ty;
             },
             .Array => {
@@ -1729,7 +1724,7 @@ pub const Object = struct {
                     @intCast(c_int, ty.arrayLen(mod)),
                 );
                 // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
-                try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(array_di_ty), .{ .mod = o.module });
+                try o.di_type_map.put(gpa, ty.toIntern(), AnnotatedDITypePtr.initFull(array_di_ty));
                 return array_di_ty;
             },
             .Vector => {
@@ -1761,7 +1756,7 @@ pub const Object = struct {
                     ty.vectorLen(mod),
                 );
                 // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
-                try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(vector_di_ty), .{ .mod = o.module });
+                try o.di_type_map.put(gpa, ty.toIntern(), AnnotatedDITypePtr.initFull(vector_di_ty));
                 return vector_di_ty;
             },
             .Optional => {
@@ -1777,7 +1772,7 @@ pub const Object = struct {
                 if (ty.optionalReprIsPayload(mod)) {
                     const ptr_di_ty = try o.lowerDebugType(child_ty, resolve);
                     // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
-                    try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.init(ptr_di_ty, resolve), .{ .mod = o.module });
+                    try o.di_type_map.put(gpa, ty.toIntern(), AnnotatedDITypePtr.init(ptr_di_ty, resolve));
                     return ptr_di_ty;
                 }
 
@@ -1850,7 +1845,7 @@ pub const Object = struct {
                 );
                 dib.replaceTemporary(fwd_decl, full_di_ty);
                 // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
-                try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .mod = o.module });
+                try o.di_type_map.put(gpa, ty.toIntern(), AnnotatedDITypePtr.initFull(full_di_ty));
                 return full_di_ty;
             },
             .ErrorUnion => {
@@ -1858,7 +1853,7 @@ pub const Object = struct {
                 if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) {
                     const err_set_di_ty = try o.lowerDebugType(Type.anyerror, .full);
                     // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
-                    try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(err_set_di_ty), .{ .mod = o.module });
+                    try o.di_type_map.put(gpa, ty.toIntern(), AnnotatedDITypePtr.initFull(err_set_di_ty));
                     return err_set_di_ty;
                 }
                 const name = try ty.nameAlloc(gpa, o.module);
@@ -1941,7 +1936,7 @@ pub const Object = struct {
                 );
                 dib.replaceTemporary(fwd_decl, full_di_ty);
                 // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
-                try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .mod = o.module });
+                try o.di_type_map.put(gpa, ty.toIntern(), AnnotatedDITypePtr.initFull(full_di_ty));
                 return full_di_ty;
             },
             .ErrorSet => {
@@ -2038,7 +2033,7 @@ pub const Object = struct {
                         );
                         dib.replaceTemporary(fwd_decl, full_di_ty);
                         // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
-                        try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .mod = o.module });
+                        try o.di_type_map.put(gpa, ty.toIntern(), AnnotatedDITypePtr.initFull(full_di_ty));
                         return full_di_ty;
                     },
                     .struct_type => |struct_type| s: {
@@ -2057,7 +2052,7 @@ pub const Object = struct {
                             dib.replaceTemporary(fwd_decl, struct_di_ty);
                             // The recursive call to `lowerDebugType` via `makeEmptyNamespaceDIType`
                             // means we can't use `gop` anymore.
-                            try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(struct_di_ty), .{ .mod = o.module });
+                            try o.di_type_map.put(gpa, ty.toIntern(), AnnotatedDITypePtr.initFull(struct_di_ty));
                             return struct_di_ty;
                         }
                     },
@@ -2070,7 +2065,7 @@ pub const Object = struct {
                     dib.replaceTemporary(fwd_decl, struct_di_ty);
                     // The recursive call to `lowerDebugType` via `makeEmptyNamespaceDIType`
                     // means we can't use `gop` anymore.
-                    try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(struct_di_ty), .{ .mod = o.module });
+                    try o.di_type_map.put(gpa, ty.toIntern(), AnnotatedDITypePtr.initFull(struct_di_ty));
                     return struct_di_ty;
                 }
 
@@ -2126,7 +2121,7 @@ pub const Object = struct {
                 );
                 dib.replaceTemporary(fwd_decl, full_di_ty);
                 // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
-                try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .mod = o.module });
+                try o.di_type_map.put(gpa, ty.toIntern(), AnnotatedDITypePtr.initFull(full_di_ty));
                 return full_di_ty;
             },
             .Union => {
@@ -2155,7 +2150,7 @@ pub const Object = struct {
                     dib.replaceTemporary(fwd_decl, union_di_ty);
                     // The recursive call to `lowerDebugType` via `makeEmptyNamespaceDIType`
                     // means we can't use `gop` anymore.
-                    try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(union_di_ty), .{ .mod = o.module });
+                    try o.di_type_map.put(gpa, ty.toIntern(), AnnotatedDITypePtr.initFull(union_di_ty));
                     return union_di_ty;
                 }
 
@@ -2182,7 +2177,7 @@ pub const Object = struct {
                     dib.replaceTemporary(fwd_decl, full_di_ty);
                     // The recursive call to `lowerDebugType` via `makeEmptyNamespaceDIType`
                     // means we can't use `gop` anymore.
-                    try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .mod = o.module });
+                    try o.di_type_map.put(gpa, ty.toIntern(), AnnotatedDITypePtr.initFull(full_di_ty));
                     return full_di_ty;
                 }
 
@@ -2241,7 +2236,7 @@ pub const Object = struct {
                 if (layout.tag_size == 0) {
                     dib.replaceTemporary(fwd_decl, union_di_ty);
                     // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
-                    try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(union_di_ty), .{ .mod = o.module });
+                    try o.di_type_map.put(gpa, ty.toIntern(), AnnotatedDITypePtr.initFull(union_di_ty));
                     return union_di_ty;
                 }
 
@@ -2302,7 +2297,7 @@ pub const Object = struct {
                 );
                 dib.replaceTemporary(fwd_decl, full_di_ty);
                 // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
-                try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .mod = o.module });
+                try o.di_type_map.put(gpa, ty.toIntern(), AnnotatedDITypePtr.initFull(full_di_ty));
                 return full_di_ty;
             },
             .Fn => {
@@ -2349,7 +2344,7 @@ pub const Object = struct {
                     0,
                 );
                 // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
-                try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(fn_di_ty), .{ .mod = o.module });
+                try o.di_type_map.put(gpa, ty.toIntern(), AnnotatedDITypePtr.initFull(fn_di_ty));
                 return fn_di_ty;
             },
             .ComptimeInt => unreachable,
src/link/Dwarf.zig
@@ -87,12 +87,7 @@ pub const DeclState = struct {
     dbg_info: std.ArrayList(u8),
     abbrev_type_arena: std.heap.ArenaAllocator,
     abbrev_table: std.ArrayListUnmanaged(AbbrevEntry) = .{},
-    abbrev_resolver: std.HashMapUnmanaged(
-        Type,
-        u32,
-        Type.HashContext64,
-        std.hash_map.default_max_load_percentage,
-    ) = .{},
+    abbrev_resolver: std.AutoHashMapUnmanaged(InternPool.Index, u32) = .{},
     abbrev_relocs: std.ArrayListUnmanaged(AbbrevRelocation) = .{},
     exprloc_relocs: std.ArrayListUnmanaged(ExprlocRelocation) = .{},
 
@@ -142,9 +137,7 @@ pub const DeclState = struct {
     /// @symbol signifies a type abbreviation posititioned somewhere in the .debug_abbrev section
     /// which we use as our target of the relocation.
     fn addTypeRelocGlobal(self: *DeclState, atom_index: Atom.Index, ty: Type, offset: u32) !void {
-        const resolv = self.abbrev_resolver.getContext(ty, .{
-            .mod = self.mod,
-        }) orelse blk: {
+        const resolv = self.abbrev_resolver.get(ty.toIntern()) orelse blk: {
             const sym_index = @intCast(u32, self.abbrev_table.items.len);
             try self.abbrev_table.append(self.gpa, .{
                 .atom_index = atom_index,
@@ -152,12 +145,8 @@ pub const DeclState = struct {
                 .offset = undefined,
             });
             log.debug("%{d}: {}", .{ sym_index, ty.fmt(self.mod) });
-            try self.abbrev_resolver.putNoClobberContext(self.gpa, ty, sym_index, .{
-                .mod = self.mod,
-            });
-            break :blk self.abbrev_resolver.getContext(ty, .{
-                .mod = self.mod,
-            }).?;
+            try self.abbrev_resolver.putNoClobber(self.gpa, ty.toIntern(), sym_index);
+            break :blk sym_index;
         };
         log.debug("{x}: %{d} + 0", .{ offset, resolv });
         try self.abbrev_relocs.append(self.gpa, .{
src/Air.zig
@@ -905,6 +905,8 @@ pub const Inst = struct {
         const_slice_u8_sentinel_0_type = @enumToInt(InternPool.Index.const_slice_u8_sentinel_0_type),
         anyerror_void_error_union_type = @enumToInt(InternPool.Index.anyerror_void_error_union_type),
         generic_poison_type = @enumToInt(InternPool.Index.generic_poison_type),
+        inferred_alloc_const_type = @enumToInt(InternPool.Index.inferred_alloc_const_type),
+        inferred_alloc_mut_type = @enumToInt(InternPool.Index.inferred_alloc_mut_type),
         empty_struct_type = @enumToInt(InternPool.Index.empty_struct_type),
         undef = @enumToInt(InternPool.Index.undef),
         zero = @enumToInt(InternPool.Index.zero),
src/InternPool.zig
@@ -959,6 +959,8 @@ pub const Index = enum(u32) {
     const_slice_u8_sentinel_0_type,
     anyerror_void_error_union_type,
     generic_poison_type,
+    inferred_alloc_const_type,
+    inferred_alloc_mut_type,
     /// `@TypeOf(.{})`
     empty_struct_type,
 
@@ -1009,10 +1011,7 @@ pub const Index = enum(u32) {
 
     pub fn toType(i: Index) @import("type.zig").Type {
         assert(i != .none);
-        return .{
-            .ip_index = i,
-            .legacy = undefined,
-        };
+        return .{ .ip_index = i };
     }
 
     pub fn toValue(i: Index) @import("value.zig").Value {
@@ -1195,6 +1194,10 @@ pub const static_keys = [_]Key{
 
     // generic_poison_type
     .{ .simple_type = .generic_poison },
+    // inferred_alloc_const_type
+    .{ .simple_type = .inferred_alloc_const },
+    // inferred_alloc_mut_type
+    .{ .simple_type = .inferred_alloc_mut },
 
     // empty_struct_type
     .{ .anon_struct_type = .{
@@ -1568,6 +1571,12 @@ pub const SimpleType = enum(u32) {
     type_info,
 
     generic_poison,
+    /// TODO: remove this from `SimpleType`; instead make it only a special `Index` tag like
+    /// `var_args_param_type`.
+    inferred_alloc_const,
+    /// TODO: remove this from `SimpleType`; instead make it only a special `Index` tag like
+    /// `var_args_param_type`.
+    inferred_alloc_mut,
 };
 
 pub const SimpleValue = enum(u32) {
src/Module.zig
@@ -6818,7 +6818,7 @@ pub fn singleConstPtrType(mod: *Module, child_type: Type) Allocator.Error!Type {
 }
 
 pub fn adjustPtrTypeChild(mod: *Module, ptr_ty: Type, new_child: Type) Allocator.Error!Type {
-    const info = ptr_ty.ptrInfoIp(mod.intern_pool);
+    const info = Type.ptrInfoIp(mod.intern_pool, ptr_ty.toIntern());
     return mod.ptrType(.{
         .elem_type = new_child.toIntern(),
 
src/print_air.zig
@@ -366,13 +366,7 @@ const Writer = struct {
     }
 
     fn writeType(w: *Writer, s: anytype, ty: Type) !void {
-        switch (ty.ip_index) {
-            .none => switch (ty.tag()) {
-                .inferred_alloc_const => try s.writeAll("(inferred_alloc_const)"),
-                .inferred_alloc_mut => try s.writeAll("(inferred_alloc_mut)"),
-            },
-            else => try ty.print(s, w.module),
-        }
+        return ty.print(s, w.module);
     }
 
     fn writeTy(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
src/Sema.zig
@@ -904,10 +904,10 @@ fn analyzeBodyInner(
         const air_inst: Air.Inst.Ref = switch (tags[inst]) {
             // zig fmt: off
             .alloc                        => try sema.zirAlloc(block, inst),
-            .alloc_inferred               => try sema.zirAllocInferred(block, inst, Type.initTag(.inferred_alloc_const)),
-            .alloc_inferred_mut           => try sema.zirAllocInferred(block, inst, Type.initTag(.inferred_alloc_mut)),
-            .alloc_inferred_comptime      => try sema.zirAllocInferredComptime(inst, Type.initTag(.inferred_alloc_const)),
-            .alloc_inferred_comptime_mut  => try sema.zirAllocInferredComptime(inst, Type.initTag(.inferred_alloc_mut)),
+            .alloc_inferred               => try sema.zirAllocInferred(block, inst, .{ .ip_index = .inferred_alloc_const_type }),
+            .alloc_inferred_mut           => try sema.zirAllocInferred(block, inst, .{ .ip_index = .inferred_alloc_mut_type }),
+            .alloc_inferred_comptime      => try sema.zirAllocInferredComptime(inst, .{ .ip_index = .inferred_alloc_const_type }),
+            .alloc_inferred_comptime_mut  => try sema.zirAllocInferredComptime(inst, .{ .ip_index = .inferred_alloc_mut_type }),
             .alloc_mut                    => try sema.zirAllocMut(block, inst),
             .alloc_comptime_mut           => try sema.zirAllocComptime(block, inst),
             .make_ptr_const               => try sema.zirMakePtrConst(block, inst),
@@ -3471,9 +3471,9 @@ fn zirAllocExtended(
     } else 0;
 
     const inferred_alloc_ty = if (small.is_const)
-        Type.initTag(.inferred_alloc_const)
+        Type{ .ip_index = .inferred_alloc_const_type }
     else
-        Type.initTag(.inferred_alloc_mut);
+        Type{ .ip_index = .inferred_alloc_mut_type };
 
     if (block.is_comptime or small.is_comptime) {
         if (small.has_type) {
@@ -3707,9 +3707,10 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
     assert(sema.air_instructions.items(.tag)[ptr_inst] == .constant);
     const value_index = sema.air_instructions.items(.data)[ptr_inst].ty_pl.payload;
     const ptr_val = sema.air_values.items[value_index];
-    const var_is_mut = switch (sema.typeOf(ptr).tag()) {
-        .inferred_alloc_const => false,
-        .inferred_alloc_mut => true,
+    const var_is_mut = switch (sema.typeOf(ptr).toIntern()) {
+        .inferred_alloc_const_type => false,
+        .inferred_alloc_mut_type => true,
+        else => unreachable,
     };
     const target = sema.mod.getTarget();
 
@@ -7451,7 +7452,7 @@ fn instantiateGenericCall(
                 };
                 arg_val.hashUncoerced(arg_ty, &hasher, mod);
                 if (is_anytype) {
-                    arg_ty.hashWithHasher(&hasher, mod);
+                    std.hash.autoHash(&hasher, arg_ty.toIntern());
                     generic_args[i] = .{
                         .ty = arg_ty,
                         .val = arg_val,
@@ -7465,7 +7466,7 @@ fn instantiateGenericCall(
                     };
                 }
             } else if (is_anytype) {
-                arg_ty.hashWithHasher(&hasher, mod);
+                std.hash.autoHash(&hasher, arg_ty.toIntern());
                 generic_args[i] = .{
                     .ty = arg_ty,
                     .val = Value.generic_poison,
@@ -8233,7 +8234,7 @@ fn zirEnumLiteral(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
     const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
     const duped_name = try sema.arena.dupe(u8, inst_data.get(sema.code));
     return sema.addConstant(
-        .{ .ip_index = .enum_literal_type, .legacy = undefined },
+        .{ .ip_index = .enum_literal_type },
         try Value.Tag.enum_literal.create(sema.arena, duped_name),
     );
 }
@@ -13278,9 +13279,12 @@ fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins
         const rhs_val = maybe_rhs_val orelse unreachable;
         const rem = lhs_val.floatRem(rhs_val, resolved_type, sema.arena, mod) catch unreachable;
         if (!rem.compareAllWithZero(.eq, mod)) {
-            return sema.fail(block, src, "ambiguous coercion of division operands '{s}' and '{s}'; non-zero remainder '{}'", .{
-                @tagName(lhs_ty.tag()), @tagName(rhs_ty.tag()), rem.fmtValue(resolved_type, sema.mod),
-            });
+            return sema.fail(
+                block,
+                src,
+                "ambiguous coercion of division operands '{}' and '{}'; non-zero remainder '{}'",
+                .{ lhs_ty.fmt(mod), rhs_ty.fmt(mod), rem.fmtValue(resolved_type, mod) },
+            );
         }
     }
 
@@ -13386,7 +13390,12 @@ fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins
 
     const air_tag = if (is_int) blk: {
         if (lhs_ty.isSignedInt(mod) or rhs_ty.isSignedInt(mod)) {
-            return sema.fail(block, src, "division with '{s}' and '{s}': signed integers must use @divTrunc, @divFloor, or @divExact", .{ @tagName(lhs_ty.tag()), @tagName(rhs_ty.tag()) });
+            return sema.fail(
+                block,
+                src,
+                "division with '{}' and '{}': signed integers must use @divTrunc, @divFloor, or @divExact",
+                .{ lhs_ty.fmt(mod), rhs_ty.fmt(mod) },
+            );
         }
         break :blk Air.Inst.Tag.div_trunc;
     } else switch (block.float_mode) {
@@ -23367,7 +23376,7 @@ fn validateRunTimeType(
     };
 }
 
-const TypeSet = std.HashMapUnmanaged(Type, void, Type.HashContext64, std.hash_map.default_max_load_percentage);
+const TypeSet = std.AutoHashMapUnmanaged(InternPool.Index, void);
 
 fn explainWhyTypeIsComptime(
     sema: *Sema,
@@ -23453,7 +23462,7 @@ fn explainWhyTypeIsComptimeInner(
         },
 
         .Struct => {
-            if ((try type_set.getOrPutContext(sema.gpa, ty, .{ .mod = mod })).found_existing) return;
+            if ((try type_set.getOrPut(sema.gpa, ty.toIntern())).found_existing) return;
 
             if (mod.typeToStruct(ty)) |struct_obj| {
                 for (struct_obj.fields.values(), 0..) |field, i| {
@@ -23472,7 +23481,7 @@ fn explainWhyTypeIsComptimeInner(
         },
 
         .Union => {
-            if ((try type_set.getOrPutContext(sema.gpa, ty, .{ .mod = mod })).found_existing) return;
+            if ((try type_set.getOrPut(sema.gpa, ty.toIntern())).found_existing) return;
 
             if (mod.typeToUnion(ty)) |union_obj| {
                 for (union_obj.fields.values(), 0..) |field, i| {
@@ -27459,8 +27468,8 @@ fn obtainBitCastedVectorPtr(sema: *Sema, ptr: Air.Inst.Ref) ?Air.Inst.Ref {
         // different behavior depending on whether the types were inferred.
         // Something seems wrong here.
         if (prev_ptr_ty.ip_index == .none) {
-            if (prev_ptr_ty.tag() == .inferred_alloc_mut) return null;
-            if (prev_ptr_ty.tag() == .inferred_alloc_const) return null;
+            if (prev_ptr_ty.ip_index == .inferred_alloc_mut_type) return null;
+            if (prev_ptr_ty.ip_index == .inferred_alloc_const_type) return null;
         }
 
         const prev_ptr_child_ty = prev_ptr_ty.childType(mod);
@@ -31677,6 +31686,9 @@ pub fn resolveTypeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool {
                 .enum_literal,
                 .type_info,
                 => true,
+
+                .inferred_alloc_const => unreachable,
+                .inferred_alloc_mut => unreachable,
             },
             .struct_type => |struct_type| {
                 const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse return false;
@@ -31931,6 +31943,8 @@ pub fn resolveTypeFields(sema: *Sema, ty: Type) CompileError!Type {
         .bool_false => unreachable,
         .empty_struct => unreachable,
         .generic_poison => unreachable,
+        .inferred_alloc_const_type => unreachable,
+        .inferred_alloc_mut_type => unreachable,
 
         .type_info_type => return sema.getBuiltinType("Type"),
         .extern_options_type => return sema.getBuiltinType("ExternOptions"),
@@ -33032,16 +33046,9 @@ 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;
-
-    switch (ty.ip_index) {
-        .empty_struct_type => return Value.empty_struct,
-
-        .none => switch (ty.tag()) {
-            .inferred_alloc_const => unreachable,
-            .inferred_alloc_mut => unreachable,
-        },
-
-        else => return switch (mod.intern_pool.indexToKey(ty.ip_index)) {
+    return switch (ty.ip_index) {
+        .empty_struct_type => Value.empty_struct,
+        else => switch (mod.intern_pool.indexToKey(ty.ip_index)) {
             .int_type => |int_type| {
                 if (int_type.bits == 0) {
                     return try mod.intValue(ty, 0);
@@ -33123,6 +33130,8 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
                 .undefined => Value.undef,
 
                 .generic_poison => return error.GenericPoison,
+                .inferred_alloc_const => unreachable,
+                .inferred_alloc_mut => unreachable,
             },
             .struct_type => |struct_type| {
                 const resolved_ty = try sema.resolveTypeFields(ty);
@@ -33245,7 +33254,7 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
             .enum_tag => unreachable,
             .aggregate => unreachable,
         },
-    }
+    };
 }
 
 /// Returns the type of the AIR instruction.
@@ -33563,16 +33572,15 @@ fn usizeCast(sema: *Sema, block: *Block, src: LazySrcLoc, int: u64) CompileError
 /// This logic must be kept in sync with `Type.isPtrLikeOptional`.
 fn typePtrOrOptionalPtrTy(sema: *Sema, ty: Type) !?Type {
     const mod = sema.mod;
-
-    if (ty.ip_index != .none) switch (mod.intern_pool.indexToKey(ty.ip_index)) {
+    return switch (mod.intern_pool.indexToKey(ty.ip_index)) {
         .ptr_type => |ptr_type| switch (ptr_type.size) {
-            .Slice => return null,
-            .C => return ptr_type.elem_type.toType(),
-            .One, .Many => return ty,
+            .Slice => null,
+            .C => ptr_type.elem_type.toType(),
+            .One, .Many => ty,
         },
         .opt_type => |opt_child| switch (mod.intern_pool.indexToKey(opt_child)) {
             .ptr_type => |ptr_type| switch (ptr_type.size) {
-                .Slice, .C => return null,
+                .Slice, .C => null,
                 .Many, .One => {
                     if (ptr_type.is_allowzero) return null;
 
@@ -33585,15 +33593,10 @@ fn typePtrOrOptionalPtrTy(sema: *Sema, ty: Type) !?Type {
                     return payload_ty;
                 },
             },
-            else => return null,
+            else => null,
         },
-        else => return null,
+        else => null,
     };
-
-    switch (ty.tag()) {
-        .inferred_alloc_const => unreachable,
-        .inferred_alloc_mut => unreachable,
-    }
 }
 
 /// `generic_poison` will return false.
@@ -33677,6 +33680,9 @@ pub fn typeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool {
                 .enum_literal,
                 .type_info,
                 => true,
+
+                .inferred_alloc_const => unreachable,
+                .inferred_alloc_mut => unreachable,
             },
             .struct_type => |struct_type| {
                 const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse return false;
src/type.zig
@@ -11,115 +11,99 @@ const TypedValue = @import("TypedValue.zig");
 const Sema = @import("Sema.zig");
 const InternPool = @import("InternPool.zig");
 
-const file_struct = @This();
-
+/// Both types and values are canonically represented by a single 32-bit integer
+/// which is an index into an `InternPool` data structure.
+/// This struct abstracts around this storage by providing methods only
+/// applicable to types rather than values in general.
 pub const Type = struct {
-    /// We are migrating towards using this for every Type object. However, many
-    /// types are still represented the legacy way. This is indicated by using
-    /// InternPool.Index.none.
     ip_index: InternPool.Index,
 
-    /// This is the raw data, with no bookkeeping, no memory awareness, no de-duplication.
-    /// This union takes advantage of the fact that the first page of memory
-    /// is unmapped, giving us 4096 possible enum tags that have no payload.
-    legacy: extern union {
-        /// If the tag value is less than Tag.no_payload_count, then no pointer
-        /// dereference is needed.
-        tag_if_small_enough: Tag,
-        ptr_otherwise: *Payload,
-    },
-
     pub fn zigTypeTag(ty: Type, mod: *const Module) std.builtin.TypeId {
         return ty.zigTypeTagOrPoison(mod) catch unreachable;
     }
 
     pub fn zigTypeTagOrPoison(ty: Type, mod: *const Module) error{GenericPoison}!std.builtin.TypeId {
-        switch (ty.ip_index) {
-            .none => switch (ty.tag()) {
-                .inferred_alloc_const,
-                .inferred_alloc_mut,
-                => return .Pointer,
-            },
-            else => return switch (mod.intern_pool.indexToKey(ty.ip_index)) {
-                .int_type => .Int,
-                .ptr_type => .Pointer,
-                .array_type => .Array,
-                .vector_type => .Vector,
-                .opt_type => .Optional,
-                .error_union_type => .ErrorUnion,
-                .error_set_type, .inferred_error_set_type => .ErrorSet,
-                .struct_type, .anon_struct_type => .Struct,
-                .union_type => .Union,
-                .opaque_type => .Opaque,
-                .enum_type => .Enum,
-                .func_type => .Fn,
-                .anyframe_type => .AnyFrame,
-                .simple_type => |s| switch (s) {
-                    .f16,
-                    .f32,
-                    .f64,
-                    .f80,
-                    .f128,
-                    .c_longdouble,
-                    => .Float,
+        return switch (mod.intern_pool.indexToKey(ty.ip_index)) {
+            .int_type => .Int,
+            .ptr_type => .Pointer,
+            .array_type => .Array,
+            .vector_type => .Vector,
+            .opt_type => .Optional,
+            .error_union_type => .ErrorUnion,
+            .error_set_type, .inferred_error_set_type => .ErrorSet,
+            .struct_type, .anon_struct_type => .Struct,
+            .union_type => .Union,
+            .opaque_type => .Opaque,
+            .enum_type => .Enum,
+            .func_type => .Fn,
+            .anyframe_type => .AnyFrame,
+            .simple_type => |s| switch (s) {
+                .f16,
+                .f32,
+                .f64,
+                .f80,
+                .f128,
+                .c_longdouble,
+                => .Float,
 
-                    .usize,
-                    .isize,
-                    .c_char,
-                    .c_short,
-                    .c_ushort,
-                    .c_int,
-                    .c_uint,
-                    .c_long,
-                    .c_ulong,
-                    .c_longlong,
-                    .c_ulonglong,
-                    => .Int,
-
-                    .anyopaque => .Opaque,
-                    .bool => .Bool,
-                    .void => .Void,
-                    .type => .Type,
-                    .anyerror => .ErrorSet,
-                    .comptime_int => .ComptimeInt,
-                    .comptime_float => .ComptimeFloat,
-                    .noreturn => .NoReturn,
-                    .null => .Null,
-                    .undefined => .Undefined,
-                    .enum_literal => .EnumLiteral,
+                .usize,
+                .isize,
+                .c_char,
+                .c_short,
+                .c_ushort,
+                .c_int,
+                .c_uint,
+                .c_long,
+                .c_ulong,
+                .c_longlong,
+                .c_ulonglong,
+                => .Int,
+
+                .anyopaque => .Opaque,
+                .bool => .Bool,
+                .void => .Void,
+                .type => .Type,
+                .anyerror => .ErrorSet,
+                .comptime_int => .ComptimeInt,
+                .comptime_float => .ComptimeFloat,
+                .noreturn => .NoReturn,
+                .null => .Null,
+                .undefined => .Undefined,
+                .enum_literal => .EnumLiteral,
 
-                    .atomic_order,
-                    .atomic_rmw_op,
-                    .calling_convention,
-                    .address_space,
-                    .float_mode,
-                    .reduce_op,
-                    .call_modifier,
-                    => .Enum,
+                .atomic_order,
+                .atomic_rmw_op,
+                .calling_convention,
+                .address_space,
+                .float_mode,
+                .reduce_op,
+                .call_modifier,
+                => .Enum,
 
-                    .prefetch_options,
-                    .export_options,
-                    .extern_options,
-                    => .Struct,
+                .prefetch_options,
+                .export_options,
+                .extern_options,
+                => .Struct,
 
-                    .type_info => .Union,
+                .type_info => .Union,
 
-                    .generic_poison => return error.GenericPoison,
-                },
+                .generic_poison => return error.GenericPoison,
 
-                // values, not types
-                .undef => unreachable,
-                .un => unreachable,
-                .extern_func => unreachable,
-                .int => unreachable,
-                .float => unreachable,
-                .ptr => unreachable,
-                .opt => unreachable,
-                .enum_tag => unreachable,
-                .simple_value => unreachable,
-                .aggregate => unreachable,
+                .inferred_alloc_const, .inferred_alloc_mut => return .Pointer,
             },
-        }
+
+            // values, not types
+            .undef => unreachable,
+            .un => unreachable,
+            .extern_func => unreachable,
+            .int => unreachable,
+            .float => unreachable,
+            .ptr => unreachable,
+            .opt => unreachable,
+            .enum_tag => unreachable,
+            .simple_value => unreachable,
+            .aggregate => unreachable,
+        };
     }
 
     pub fn baseZigTypeTag(self: Type, mod: *Module) std.builtin.TypeId {
@@ -171,68 +155,6 @@ pub const Type = struct {
         };
     }
 
-    pub fn initTag(comptime small_tag: Tag) Type {
-        comptime assert(@enumToInt(small_tag) < Tag.no_payload_count);
-        return Type{
-            .ip_index = .none,
-            .legacy = .{ .tag_if_small_enough = small_tag },
-        };
-    }
-
-    pub fn initPayload(payload: *Payload) Type {
-        assert(@enumToInt(payload.tag) >= Tag.no_payload_count);
-        return Type{
-            .ip_index = .none,
-            .legacy = .{ .ptr_otherwise = payload },
-        };
-    }
-
-    pub fn tag(ty: Type) Tag {
-        assert(ty.ip_index == .none);
-        if (@enumToInt(ty.legacy.tag_if_small_enough) < Tag.no_payload_count) {
-            return ty.legacy.tag_if_small_enough;
-        } else {
-            return ty.legacy.ptr_otherwise.tag;
-        }
-    }
-
-    /// Prefer `castTag` to this.
-    pub fn cast(self: Type, comptime T: type) ?*T {
-        if (self.ip_index != .none) {
-            return null;
-        }
-        if (@hasField(T, "base_tag")) {
-            return self.castTag(T.base_tag);
-        }
-        if (@enumToInt(self.legacy.tag_if_small_enough) < Tag.no_payload_count) {
-            return null;
-        }
-        inline for (@typeInfo(Tag).Enum.fields) |field| {
-            if (field.value < Tag.no_payload_count)
-                continue;
-            const t = @intToEnum(Tag, field.value);
-            if (self.legacy.ptr_otherwise.tag == t) {
-                if (T == t.Type()) {
-                    return @fieldParentPtr(T, "base", self.legacy.ptr_otherwise);
-                }
-                return null;
-            }
-        }
-        unreachable;
-    }
-
-    pub fn castTag(self: Type, comptime t: Tag) ?*t.Type() {
-        if (self.ip_index != .none) return null;
-
-        if (@enumToInt(self.legacy.tag_if_small_enough) < Tag.no_payload_count)
-            return null;
-
-        if (self.legacy.ptr_otherwise.tag == t)
-            return @fieldParentPtr(t.Type(), "base", self.legacy.ptr_otherwise);
-
-        return null;
-    }
-
     /// If it is a function pointer, returns the function type. Otherwise returns null.
     pub fn castPtrToFn(ty: Type, mod: *const Module) ?Type {
         if (ty.zigTypeTag(mod) != .Pointer) return null;
@@ -260,8 +182,8 @@ pub const Type = struct {
         };
     }
 
-    pub fn ptrInfoIp(ty: Type, ip: InternPool) InternPool.Key.PtrType {
-        return switch (ip.indexToKey(ty.ip_index)) {
+    pub fn ptrInfoIp(ip: InternPool, ty: InternPool.Index) InternPool.Key.PtrType {
+        return switch (ip.indexToKey(ty)) {
             .ptr_type => |p| p,
             .opt_type => |child| switch (ip.indexToKey(child)) {
                 .ptr_type => |p| p,
@@ -272,135 +194,28 @@ pub const Type = struct {
     }
 
     pub fn ptrInfo(ty: Type, mod: *const Module) Payload.Pointer.Data {
-        return Payload.Pointer.Data.fromKey(ptrInfoIp(ty, mod.intern_pool));
-    }
-
-    pub fn eql(a: Type, b: Type, mod: *Module) bool {
-        if (a.ip_index != .none or b.ip_index != .none) {
-            // The InternPool data structure hashes based on Key to make interned objects
-            // unique. An Index can be treated simply as u32 value for the
-            // purpose of Type/Value hashing and equality.
-            return a.ip_index == b.ip_index;
-        }
-        // As a shortcut, if the small tags / addresses match, we're done.
-        if (a.legacy.tag_if_small_enough == b.legacy.tag_if_small_enough) return true;
-
-        switch (a.tag()) {
-            .inferred_alloc_const,
-            .inferred_alloc_mut,
-            => {
-                if (b.zigTypeTag(mod) != .Pointer) return false;
-
-                const info_a = a.ptrInfo(mod);
-                const info_b = b.ptrInfo(mod);
-                if (!info_a.pointee_type.eql(info_b.pointee_type, mod))
-                    return false;
-                if (info_a.@"align" != info_b.@"align")
-                    return false;
-                if (info_a.@"addrspace" != info_b.@"addrspace")
-                    return false;
-                if (info_a.bit_offset != info_b.bit_offset)
-                    return false;
-                if (info_a.host_size != info_b.host_size)
-                    return false;
-                if (info_a.vector_index != info_b.vector_index)
-                    return false;
-                if (info_a.@"allowzero" != info_b.@"allowzero")
-                    return false;
-                if (info_a.mutable != info_b.mutable)
-                    return false;
-                if (info_a.@"volatile" != info_b.@"volatile")
-                    return false;
-                if (info_a.size != info_b.size)
-                    return false;
-
-                const sentinel_a = info_a.sentinel;
-                const sentinel_b = info_b.sentinel;
-                if (sentinel_a) |sa| {
-                    if (sentinel_b) |sb| {
-                        if (!sa.eql(sb, info_a.pointee_type, mod))
-                            return false;
-                    } else {
-                        return false;
-                    }
-                } else {
-                    if (sentinel_b != null)
-                        return false;
-                }
-
-                return true;
-            },
-        }
+        return Payload.Pointer.Data.fromKey(ptrInfoIp(mod.intern_pool, ty.ip_index));
     }
 
-    pub fn hash(self: Type, mod: *Module) u64 {
-        var hasher = std.hash.Wyhash.init(0);
-        self.hashWithHasher(&hasher, mod);
-        return hasher.final();
+    pub fn eql(a: Type, b: Type, mod: *const Module) bool {
+        _ = mod; // TODO: remove this parameter
+        assert(a.ip_index != .none);
+        assert(b.ip_index != .none);
+        // The InternPool data structure hashes based on Key to make interned objects
+        // unique. An Index can be treated simply as u32 value for the
+        // purpose of Type/Value hashing and equality.
+        return a.ip_index == b.ip_index;
     }
 
-    pub fn hashWithHasher(ty: Type, hasher: *std.hash.Wyhash, mod: *Module) void {
-        if (ty.ip_index != .none) {
-            // The InternPool data structure hashes based on Key to make interned objects
-            // unique. An Index can be treated simply as u32 value for the
-            // purpose of Type/Value hashing and equality.
-            std.hash.autoHash(hasher, ty.ip_index);
-            return;
-        }
-        switch (ty.tag()) {
-            .inferred_alloc_const,
-            .inferred_alloc_mut,
-            => {
-                std.hash.autoHash(hasher, std.builtin.TypeId.Pointer);
-
-                const info = ty.ptrInfo(mod);
-                hashWithHasher(info.pointee_type, hasher, mod);
-                hashSentinel(info.sentinel, info.pointee_type, hasher, mod);
-                std.hash.autoHash(hasher, info.@"align");
-                std.hash.autoHash(hasher, info.@"addrspace");
-                std.hash.autoHash(hasher, info.bit_offset);
-                std.hash.autoHash(hasher, info.host_size);
-                std.hash.autoHash(hasher, info.vector_index);
-                std.hash.autoHash(hasher, info.@"allowzero");
-                std.hash.autoHash(hasher, info.mutable);
-                std.hash.autoHash(hasher, info.@"volatile");
-                std.hash.autoHash(hasher, info.size);
-            },
-        }
-    }
-
-    fn hashSentinel(opt_val: ?Value, ty: Type, hasher: *std.hash.Wyhash, mod: *Module) void {
-        if (opt_val) |s| {
-            std.hash.autoHash(hasher, true);
-            s.hash(ty, hasher, mod);
-        } else {
-            std.hash.autoHash(hasher, false);
-        }
+    pub fn hash(ty: Type, mod: *const Module) u32 {
+        _ = mod; // TODO: remove this parameter
+        assert(ty.ip_index != .none);
+        // The InternPool data structure hashes based on Key to make interned objects
+        // unique. An Index can be treated simply as u32 value for the
+        // purpose of Type/Value hashing and equality.
+        return std.hash.uint32(@enumToInt(ty.ip_index));
     }
 
-    pub const HashContext64 = struct {
-        mod: *Module,
-
-        pub fn hash(self: @This(), t: Type) u64 {
-            return t.hash(self.mod);
-        }
-        pub fn eql(self: @This(), a: Type, b: Type) bool {
-            return a.eql(b, self.mod);
-        }
-    };
-
-    pub const HashContext32 = struct {
-        mod: *Module,
-
-        pub fn hash(self: @This(), t: Type) u32 {
-            return @truncate(u32, t.hash(self.mod));
-        }
-        pub fn eql(self: @This(), a: Type, b: Type, b_index: usize) bool {
-            _ = b_index;
-            return a.eql(b, self.mod);
-        }
-    };
-
     pub fn format(ty: Type, comptime unused_fmt_string: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
         _ = ty;
         _ = unused_fmt_string;
@@ -460,214 +275,208 @@ pub const Type = struct {
 
     /// Prints a name suitable for `@typeName`.
     pub fn print(ty: Type, writer: anytype, mod: *Module) @TypeOf(writer).Error!void {
-        switch (ty.ip_index) {
-            .none => switch (ty.tag()) {
-                .inferred_alloc_const => unreachable,
-                .inferred_alloc_mut => unreachable,
+        switch (mod.intern_pool.indexToKey(ty.ip_index)) {
+            .int_type => |int_type| {
+                const sign_char: u8 = switch (int_type.signedness) {
+                    .signed => 'i',
+                    .unsigned => 'u',
+                };
+                return writer.print("{c}{d}", .{ sign_char, int_type.bits });
             },
-            else => switch (mod.intern_pool.indexToKey(ty.ip_index)) {
-                .int_type => |int_type| {
-                    const sign_char: u8 = switch (int_type.signedness) {
-                        .signed => 'i',
-                        .unsigned => 'u',
-                    };
-                    return writer.print("{c}{d}", .{ sign_char, int_type.bits });
-                },
-                .ptr_type => {
-                    const info = ty.ptrInfo(mod);
-
-                    if (info.sentinel) |s| switch (info.size) {
-                        .One, .C => unreachable,
-                        .Many => try writer.print("[*:{}]", .{s.fmtValue(info.pointee_type, mod)}),
-                        .Slice => try writer.print("[:{}]", .{s.fmtValue(info.pointee_type, mod)}),
-                    } else switch (info.size) {
-                        .One => try writer.writeAll("*"),
-                        .Many => try writer.writeAll("[*]"),
-                        .C => try writer.writeAll("[*c]"),
-                        .Slice => try writer.writeAll("[]"),
-                    }
-                    if (info.@"align" != 0 or info.host_size != 0 or info.vector_index != .none) {
-                        if (info.@"align" != 0) {
-                            try writer.print("align({d}", .{info.@"align"});
-                        } else {
-                            const alignment = info.pointee_type.abiAlignment(mod);
-                            try writer.print("align({d}", .{alignment});
-                        }
+            .ptr_type => {
+                const info = ty.ptrInfo(mod);
 
-                        if (info.bit_offset != 0 or info.host_size != 0) {
-                            try writer.print(":{d}:{d}", .{ info.bit_offset, info.host_size });
-                        }
-                        if (info.vector_index == .runtime) {
-                            try writer.writeAll(":?");
-                        } else if (info.vector_index != .none) {
-                            try writer.print(":{d}", .{@enumToInt(info.vector_index)});
-                        }
-                        try writer.writeAll(") ");
-                    }
-                    if (info.@"addrspace" != .generic) {
-                        try writer.print("addrspace(.{s}) ", .{@tagName(info.@"addrspace")});
+                if (info.sentinel) |s| switch (info.size) {
+                    .One, .C => unreachable,
+                    .Many => try writer.print("[*:{}]", .{s.fmtValue(info.pointee_type, mod)}),
+                    .Slice => try writer.print("[:{}]", .{s.fmtValue(info.pointee_type, mod)}),
+                } else switch (info.size) {
+                    .One => try writer.writeAll("*"),
+                    .Many => try writer.writeAll("[*]"),
+                    .C => try writer.writeAll("[*c]"),
+                    .Slice => try writer.writeAll("[]"),
+                }
+                if (info.@"align" != 0 or info.host_size != 0 or info.vector_index != .none) {
+                    if (info.@"align" != 0) {
+                        try writer.print("align({d}", .{info.@"align"});
+                    } else {
+                        const alignment = info.pointee_type.abiAlignment(mod);
+                        try writer.print("align({d}", .{alignment});
                     }
-                    if (!info.mutable) try writer.writeAll("const ");
-                    if (info.@"volatile") try writer.writeAll("volatile ");
-                    if (info.@"allowzero" and info.size != .C) try writer.writeAll("allowzero ");
 
-                    try print(info.pointee_type, writer, mod);
-                    return;
-                },
-                .array_type => |array_type| {
-                    if (array_type.sentinel == .none) {
-                        try writer.print("[{d}]", .{array_type.len});
-                        try print(array_type.child.toType(), writer, mod);
-                    } else {
-                        try writer.print("[{d}:{}]", .{
-                            array_type.len,
-                            array_type.sentinel.toValue().fmtValue(array_type.child.toType(), mod),
-                        });
-                        try print(array_type.child.toType(), writer, mod);
+                    if (info.bit_offset != 0 or info.host_size != 0) {
+                        try writer.print(":{d}:{d}", .{ info.bit_offset, info.host_size });
                     }
-                    return;
-                },
-                .vector_type => |vector_type| {
-                    try writer.print("@Vector({d}, ", .{vector_type.len});
-                    try print(vector_type.child.toType(), writer, mod);
-                    try writer.writeAll(")");
-                    return;
-                },
-                .opt_type => |child| {
-                    try writer.writeByte('?');
-                    return print(child.toType(), writer, mod);
-                },
-                .error_union_type => |error_union_type| {
-                    try print(error_union_type.error_set_type.toType(), writer, mod);
-                    try writer.writeByte('!');
-                    try print(error_union_type.payload_type.toType(), writer, mod);
-                    return;
-                },
-                .inferred_error_set_type => |index| {
-                    const ies = mod.inferredErrorSetPtr(index);
-                    const func = ies.func;
+                    if (info.vector_index == .runtime) {
+                        try writer.writeAll(":?");
+                    } else if (info.vector_index != .none) {
+                        try writer.print(":{d}", .{@enumToInt(info.vector_index)});
+                    }
+                    try writer.writeAll(") ");
+                }
+                if (info.@"addrspace" != .generic) {
+                    try writer.print("addrspace(.{s}) ", .{@tagName(info.@"addrspace")});
+                }
+                if (!info.mutable) try writer.writeAll("const ");
+                if (info.@"volatile") try writer.writeAll("volatile ");
+                if (info.@"allowzero" and info.size != .C) try writer.writeAll("allowzero ");
 
-                    try writer.writeAll("@typeInfo(@typeInfo(@TypeOf(");
-                    const owner_decl = mod.declPtr(func.owner_decl);
-                    try owner_decl.renderFullyQualifiedName(mod, writer);
-                    try writer.writeAll(")).Fn.return_type.?).ErrorUnion.error_set");
-                },
-                .error_set_type => |error_set_type| {
-                    const names = error_set_type.names;
-                    try writer.writeAll("error{");
-                    for (names, 0..) |name, i| {
-                        if (i != 0) try writer.writeByte(',');
-                        try writer.writeAll(mod.intern_pool.stringToSlice(name));
+                try print(info.pointee_type, writer, mod);
+                return;
+            },
+            .array_type => |array_type| {
+                if (array_type.sentinel == .none) {
+                    try writer.print("[{d}]", .{array_type.len});
+                    try print(array_type.child.toType(), writer, mod);
+                } else {
+                    try writer.print("[{d}:{}]", .{
+                        array_type.len,
+                        array_type.sentinel.toValue().fmtValue(array_type.child.toType(), mod),
+                    });
+                    try print(array_type.child.toType(), writer, mod);
+                }
+                return;
+            },
+            .vector_type => |vector_type| {
+                try writer.print("@Vector({d}, ", .{vector_type.len});
+                try print(vector_type.child.toType(), writer, mod);
+                try writer.writeAll(")");
+                return;
+            },
+            .opt_type => |child| {
+                try writer.writeByte('?');
+                return print(child.toType(), writer, mod);
+            },
+            .error_union_type => |error_union_type| {
+                try print(error_union_type.error_set_type.toType(), writer, mod);
+                try writer.writeByte('!');
+                try print(error_union_type.payload_type.toType(), writer, mod);
+                return;
+            },
+            .inferred_error_set_type => |index| {
+                const ies = mod.inferredErrorSetPtr(index);
+                const func = ies.func;
+
+                try writer.writeAll("@typeInfo(@typeInfo(@TypeOf(");
+                const owner_decl = mod.declPtr(func.owner_decl);
+                try owner_decl.renderFullyQualifiedName(mod, writer);
+                try writer.writeAll(")).Fn.return_type.?).ErrorUnion.error_set");
+            },
+            .error_set_type => |error_set_type| {
+                const names = error_set_type.names;
+                try writer.writeAll("error{");
+                for (names, 0..) |name, i| {
+                    if (i != 0) try writer.writeByte(',');
+                    try writer.writeAll(mod.intern_pool.stringToSlice(name));
+                }
+                try writer.writeAll("}");
+            },
+            .simple_type => |s| return writer.writeAll(@tagName(s)),
+            .struct_type => |struct_type| {
+                if (mod.structPtrUnwrap(struct_type.index)) |struct_obj| {
+                    const decl = mod.declPtr(struct_obj.owner_decl);
+                    try decl.renderFullyQualifiedName(mod, writer);
+                } else if (struct_type.namespace.unwrap()) |namespace_index| {
+                    const namespace = mod.namespacePtr(namespace_index);
+                    try namespace.renderFullyQualifiedName(mod, "", writer);
+                } else {
+                    try writer.writeAll("@TypeOf(.{})");
+                }
+            },
+            .anon_struct_type => |anon_struct| {
+                try writer.writeAll("struct{");
+                for (anon_struct.types, anon_struct.values, 0..) |field_ty, val, i| {
+                    if (i != 0) try writer.writeAll(", ");
+                    if (val != .none) {
+                        try writer.writeAll("comptime ");
                     }
-                    try writer.writeAll("}");
-                },
-                .simple_type => |s| return writer.writeAll(@tagName(s)),
-                .struct_type => |struct_type| {
-                    if (mod.structPtrUnwrap(struct_type.index)) |struct_obj| {
-                        const decl = mod.declPtr(struct_obj.owner_decl);
-                        try decl.renderFullyQualifiedName(mod, writer);
-                    } else if (struct_type.namespace.unwrap()) |namespace_index| {
-                        const namespace = mod.namespacePtr(namespace_index);
-                        try namespace.renderFullyQualifiedName(mod, "", writer);
-                    } else {
-                        try writer.writeAll("@TypeOf(.{})");
+                    if (anon_struct.names.len != 0) {
+                        const name = mod.intern_pool.stringToSlice(anon_struct.names[i]);
+                        try writer.writeAll(name);
+                        try writer.writeAll(": ");
                     }
-                },
-                .anon_struct_type => |anon_struct| {
-                    try writer.writeAll("struct{");
-                    for (anon_struct.types, anon_struct.values, 0..) |field_ty, val, i| {
-                        if (i != 0) try writer.writeAll(", ");
-                        if (val != .none) {
-                            try writer.writeAll("comptime ");
-                        }
-                        if (anon_struct.names.len != 0) {
-                            const name = mod.intern_pool.stringToSlice(anon_struct.names[i]);
-                            try writer.writeAll(name);
-                            try writer.writeAll(": ");
-                        }
 
-                        try print(field_ty.toType(), writer, mod);
+                    try print(field_ty.toType(), writer, mod);
 
-                        if (val != .none) {
-                            try writer.print(" = {}", .{val.toValue().fmtValue(field_ty.toType(), mod)});
-                        }
+                    if (val != .none) {
+                        try writer.print(" = {}", .{val.toValue().fmtValue(field_ty.toType(), mod)});
                     }
-                    try writer.writeAll("}");
-                },
+                }
+                try writer.writeAll("}");
+            },
 
-                .union_type => |union_type| {
-                    const union_obj = mod.unionPtr(union_type.index);
-                    const decl = mod.declPtr(union_obj.owner_decl);
-                    try decl.renderFullyQualifiedName(mod, writer);
-                },
-                .opaque_type => |opaque_type| {
-                    const decl = mod.declPtr(opaque_type.decl);
-                    try decl.renderFullyQualifiedName(mod, writer);
-                },
-                .enum_type => |enum_type| {
-                    const decl = mod.declPtr(enum_type.decl);
-                    try decl.renderFullyQualifiedName(mod, writer);
-                },
-                .func_type => |fn_info| {
-                    if (fn_info.is_noinline) {
-                        try writer.writeAll("noinline ");
-                    }
-                    try writer.writeAll("fn(");
-                    for (fn_info.param_types, 0..) |param_ty, i| {
-                        if (i != 0) try writer.writeAll(", ");
-                        if (std.math.cast(u5, i)) |index| {
-                            if (fn_info.paramIsComptime(index)) {
-                                try writer.writeAll("comptime ");
-                            }
-                            if (fn_info.paramIsNoalias(index)) {
-                                try writer.writeAll("noalias ");
-                            }
-                        }
-                        if (param_ty == .generic_poison_type) {
-                            try writer.writeAll("anytype");
-                        } else {
-                            try print(param_ty.toType(), writer, mod);
+            .union_type => |union_type| {
+                const union_obj = mod.unionPtr(union_type.index);
+                const decl = mod.declPtr(union_obj.owner_decl);
+                try decl.renderFullyQualifiedName(mod, writer);
+            },
+            .opaque_type => |opaque_type| {
+                const decl = mod.declPtr(opaque_type.decl);
+                try decl.renderFullyQualifiedName(mod, writer);
+            },
+            .enum_type => |enum_type| {
+                const decl = mod.declPtr(enum_type.decl);
+                try decl.renderFullyQualifiedName(mod, writer);
+            },
+            .func_type => |fn_info| {
+                if (fn_info.is_noinline) {
+                    try writer.writeAll("noinline ");
+                }
+                try writer.writeAll("fn(");
+                for (fn_info.param_types, 0..) |param_ty, i| {
+                    if (i != 0) try writer.writeAll(", ");
+                    if (std.math.cast(u5, i)) |index| {
+                        if (fn_info.paramIsComptime(index)) {
+                            try writer.writeAll("comptime ");
                         }
-                    }
-                    if (fn_info.is_var_args) {
-                        if (fn_info.param_types.len != 0) {
-                            try writer.writeAll(", ");
+                        if (fn_info.paramIsNoalias(index)) {
+                            try writer.writeAll("noalias ");
                         }
-                        try writer.writeAll("...");
-                    }
-                    try writer.writeAll(") ");
-                    if (fn_info.alignment.toByteUnitsOptional()) |a| {
-                        try writer.print("align({d}) ", .{a});
-                    }
-                    if (fn_info.cc != .Unspecified) {
-                        try writer.writeAll("callconv(.");
-                        try writer.writeAll(@tagName(fn_info.cc));
-                        try writer.writeAll(") ");
                     }
-                    if (fn_info.return_type == .generic_poison_type) {
+                    if (param_ty == .generic_poison_type) {
                         try writer.writeAll("anytype");
                     } else {
-                        try print(fn_info.return_type.toType(), writer, mod);
+                        try print(param_ty.toType(), writer, mod);
                     }
-                },
-                .anyframe_type => |child| {
-                    if (child == .none) return writer.writeAll("anyframe");
-                    try writer.writeAll("anyframe->");
-                    return print(child.toType(), writer, mod);
-                },
-
-                // values, not types
-                .undef => unreachable,
-                .un => unreachable,
-                .simple_value => unreachable,
-                .extern_func => unreachable,
-                .int => unreachable,
-                .float => unreachable,
-                .ptr => unreachable,
-                .opt => unreachable,
-                .enum_tag => unreachable,
-                .aggregate => unreachable,
+                }
+                if (fn_info.is_var_args) {
+                    if (fn_info.param_types.len != 0) {
+                        try writer.writeAll(", ");
+                    }
+                    try writer.writeAll("...");
+                }
+                try writer.writeAll(") ");
+                if (fn_info.alignment.toByteUnitsOptional()) |a| {
+                    try writer.print("align({d}) ", .{a});
+                }
+                if (fn_info.cc != .Unspecified) {
+                    try writer.writeAll("callconv(.");
+                    try writer.writeAll(@tagName(fn_info.cc));
+                    try writer.writeAll(") ");
+                }
+                if (fn_info.return_type == .generic_poison_type) {
+                    try writer.writeAll("anytype");
+                } else {
+                    try print(fn_info.return_type.toType(), writer, mod);
+                }
+            },
+            .anyframe_type => |child| {
+                if (child == .none) return writer.writeAll("anyframe");
+                try writer.writeAll("anyframe->");
+                return print(child.toType(), writer, mod);
             },
+
+            // values, not types
+            .undef => unreachable,
+            .un => unreachable,
+            .simple_value => unreachable,
+            .extern_func => unreachable,
+            .int => unreachable,
+            .float => unreachable,
+            .ptr => unreachable,
+            .opt => unreachable,
+            .enum_tag => unreachable,
+            .aggregate => unreachable,
         }
     }
 
@@ -699,15 +508,10 @@ pub const Type = struct {
         ignore_comptime_only: bool,
         strat: AbiAlignmentAdvancedStrat,
     ) RuntimeBitsError!bool {
-        switch (ty.ip_index) {
+        return switch (ty.ip_index) {
             // False because it is a comptime-only type.
-            .empty_struct_type => return false,
-
-            .none => switch (ty.tag()) {
-                .inferred_alloc_const => unreachable,
-                .inferred_alloc_mut => unreachable,
-            },
-            else => return switch (mod.intern_pool.indexToKey(ty.ip_index)) {
+            .empty_struct_type => false,
+            else => switch (mod.intern_pool.indexToKey(ty.ip_index)) {
                 .int_type => |int_type| int_type.bits != 0,
                 .ptr_type => |ptr_type| {
                     // Pointers to zero-bit types still have a runtime address; however, pointers
@@ -802,6 +606,8 @@ pub const Type = struct {
                     => false,
 
                     .generic_poison => unreachable,
+                    .inferred_alloc_const => unreachable,
+                    .inferred_alloc_mut => unreachable,
                 },
                 .struct_type => |struct_type| {
                     const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse {
@@ -880,7 +686,7 @@ pub const Type = struct {
                 .enum_tag => unreachable,
                 .aggregate => unreachable,
             },
-        }
+        };
     }
 
     /// true if and only if the type has a well-defined memory layout
@@ -950,6 +756,9 @@ pub const Type = struct {
                 .type_info,
                 .generic_poison,
                 => false,
+
+                .inferred_alloc_const => unreachable,
+                .inferred_alloc_mut => unreachable,
             },
             .struct_type => |struct_type| {
                 const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse {
@@ -1167,10 +976,7 @@ pub const Type = struct {
                     .f80 => switch (target.c_type_bit_size(.longdouble)) {
                         80 => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.longdouble) },
                         else => {
-                            const u80_ty: Type = .{
-                                .ip_index = .u80_type,
-                                .legacy = undefined,
-                            };
+                            const u80_ty: Type = .{ .ip_index = .u80_type };
                             return AbiAlignmentAdvanced{ .scalar = abiAlignment(u80_ty, mod) };
                         },
                     },
@@ -1194,6 +1000,8 @@ pub const Type = struct {
 
                     .noreturn => unreachable,
                     .generic_poison => unreachable,
+                    .inferred_alloc_const => unreachable,
+                    .inferred_alloc_mut => unreachable,
                 },
                 .struct_type => |struct_type| {
                     const struct_obj = mod.structPtrUnwrap(struct_type.index) orelse
@@ -1562,10 +1370,7 @@ pub const Type = struct {
                     .f80 => switch (target.c_type_bit_size(.longdouble)) {
                         80 => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.longdouble) },
                         else => {
-                            const u80_ty: Type = .{
-                                .ip_index = .u80_type,
-                                .legacy = undefined,
-                            };
+                            const u80_ty: Type = .{ .ip_index = .u80_type };
                             return AbiSizeAdvanced{ .scalar = abiSize(u80_ty, mod) };
                         },
                     },
@@ -1605,6 +1410,8 @@ pub const Type = struct {
                     .type_info => unreachable,
                     .noreturn => unreachable,
                     .generic_poison => unreachable,
+                    .inferred_alloc_const => unreachable,
+                    .inferred_alloc_mut => unreachable,
                 },
                 .struct_type => |struct_type| switch (ty.containerLayout(mod)) {
                     .Packed => {
@@ -1835,6 +1642,8 @@ pub const Type = struct {
                 .undefined => unreachable,
                 .enum_literal => unreachable,
                 .generic_poison => unreachable,
+                .inferred_alloc_const => unreachable,
+                .inferred_alloc_mut => unreachable,
 
                 .atomic_order => unreachable, // missing call to resolveTypeFields
                 .atomic_rmw_op => unreachable, // missing call to resolveTypeFields
@@ -1927,17 +1736,13 @@ pub const Type = struct {
     }
 
     pub fn isSinglePointer(ty: Type, mod: *const Module) bool {
-        switch (ty.ip_index) {
-            .none => return switch (ty.tag()) {
-                .inferred_alloc_const,
-                .inferred_alloc_mut,
-                => true,
-            },
-            else => return switch (mod.intern_pool.indexToKey(ty.ip_index)) {
+        return switch (ty.ip_index) {
+            .inferred_alloc_const_type, .inferred_alloc_mut_type => true,
+            else => switch (mod.intern_pool.indexToKey(ty.ip_index)) {
                 .ptr_type => |ptr_info| ptr_info.size == .One,
                 else => false,
             },
-        }
+        };
     }
 
     /// Asserts `ty` is a pointer.
@@ -1948,11 +1753,7 @@ pub const Type = struct {
     /// Returns `null` if `ty` is not a pointer.
     pub fn ptrSizeOrNull(ty: Type, mod: *const Module) ?std.builtin.Type.Pointer.Size {
         return switch (ty.ip_index) {
-            .none => switch (ty.tag()) {
-                .inferred_alloc_const,
-                .inferred_alloc_mut,
-                => .One,
-            },
+            .inferred_alloc_const_type, .inferred_alloc_mut_type => .One,
             else => switch (mod.intern_pool.indexToKey(ty.ip_index)) {
                 .ptr_type => |ptr_info| ptr_info.size,
                 else => null,
@@ -2625,10 +2426,6 @@ pub const Type = struct {
         while (true) switch (ty.ip_index) {
             .empty_struct_type => return Value.empty_struct,
 
-            .none => switch (ty.tag()) {
-                .inferred_alloc_const => unreachable,
-                .inferred_alloc_mut => unreachable,
-            },
             else => switch (mod.intern_pool.indexToKey(ty.ip_index)) {
                 .int_type => |int_type| {
                     if (int_type.bits == 0) {
@@ -2710,6 +2507,8 @@ pub const Type = struct {
                     .undefined => return Value.undef,
 
                     .generic_poison => unreachable,
+                    .inferred_alloc_const => unreachable,
+                    .inferred_alloc_mut => unreachable,
                 },
                 .struct_type => |struct_type| {
                     if (mod.structPtrUnwrap(struct_type.index)) |s| {
@@ -2888,6 +2687,9 @@ pub const Type = struct {
                     .enum_literal,
                     .type_info,
                     => true,
+
+                    .inferred_alloc_const => unreachable,
+                    .inferred_alloc_mut => unreachable,
                 },
                 .struct_type => |struct_type| {
                     // A struct with no fields is not comptime-only.
@@ -3343,61 +3145,56 @@ pub const Type = struct {
 
     /// Supports structs and unions.
     pub fn structFieldOffset(ty: Type, index: usize, mod: *Module) u64 {
-        switch (ty.ip_index) {
-            .none => switch (ty.tag()) {
-                else => unreachable,
-            },
-            else => switch (mod.intern_pool.indexToKey(ty.ip_index)) {
-                .struct_type => |struct_type| {
-                    const struct_obj = mod.structPtrUnwrap(struct_type.index).?;
-                    assert(struct_obj.haveLayout());
-                    assert(struct_obj.layout != .Packed);
-                    var it = ty.iterateStructOffsets(mod);
-                    while (it.next()) |field_offset| {
-                        if (index == field_offset.field)
-                            return field_offset.offset;
-                    }
-
-                    return std.mem.alignForwardGeneric(u64, it.offset, @max(it.big_align, 1));
-                },
+        switch (mod.intern_pool.indexToKey(ty.ip_index)) {
+            .struct_type => |struct_type| {
+                const struct_obj = mod.structPtrUnwrap(struct_type.index).?;
+                assert(struct_obj.haveLayout());
+                assert(struct_obj.layout != .Packed);
+                var it = ty.iterateStructOffsets(mod);
+                while (it.next()) |field_offset| {
+                    if (index == field_offset.field)
+                        return field_offset.offset;
+                }
 
-                .anon_struct_type => |tuple| {
-                    var offset: u64 = 0;
-                    var big_align: u32 = 0;
+                return std.mem.alignForwardGeneric(u64, it.offset, @max(it.big_align, 1));
+            },
 
-                    for (tuple.types, tuple.values, 0..) |field_ty, field_val, i| {
-                        if (field_val != .none or !field_ty.toType().hasRuntimeBits(mod)) {
-                            // comptime field
-                            if (i == index) return offset;
-                            continue;
-                        }
+            .anon_struct_type => |tuple| {
+                var offset: u64 = 0;
+                var big_align: u32 = 0;
 
-                        const field_align = field_ty.toType().abiAlignment(mod);
-                        big_align = @max(big_align, field_align);
-                        offset = std.mem.alignForwardGeneric(u64, offset, field_align);
+                for (tuple.types, tuple.values, 0..) |field_ty, field_val, i| {
+                    if (field_val != .none or !field_ty.toType().hasRuntimeBits(mod)) {
+                        // comptime field
                         if (i == index) return offset;
-                        offset += field_ty.toType().abiSize(mod);
+                        continue;
                     }
-                    offset = std.mem.alignForwardGeneric(u64, offset, @max(big_align, 1));
-                    return offset;
-                },
 
-                .union_type => |union_type| {
-                    if (!union_type.hasTag())
-                        return 0;
-                    const union_obj = mod.unionPtr(union_type.index);
-                    const layout = union_obj.getLayout(mod, true);
-                    if (layout.tag_align >= layout.payload_align) {
-                        // {Tag, Payload}
-                        return std.mem.alignForwardGeneric(u64, layout.tag_size, layout.payload_align);
-                    } else {
-                        // {Payload, Tag}
-                        return 0;
-                    }
-                },
+                    const field_align = field_ty.toType().abiAlignment(mod);
+                    big_align = @max(big_align, field_align);
+                    offset = std.mem.alignForwardGeneric(u64, offset, field_align);
+                    if (i == index) return offset;
+                    offset += field_ty.toType().abiSize(mod);
+                }
+                offset = std.mem.alignForwardGeneric(u64, offset, @max(big_align, 1));
+                return offset;
+            },
 
-                else => unreachable,
+            .union_type => |union_type| {
+                if (!union_type.hasTag())
+                    return 0;
+                const union_obj = mod.unionPtr(union_type.index);
+                const layout = union_obj.getLayout(mod, true);
+                if (layout.tag_align >= layout.payload_align) {
+                    // {Tag, Payload}
+                    return std.mem.alignForwardGeneric(u64, layout.tag_size, layout.payload_align);
+                } else {
+                    // {Payload, Tag}
+                    return 0;
+                }
             },
+
+            else => unreachable,
         }
     }
 
@@ -3445,25 +3242,6 @@ pub const Type = struct {
         return ty.ip_index == .generic_poison_type;
     }
 
-    /// This enum does not directly correspond to `std.builtin.TypeId` because
-    /// it has extra enum tags in it, as a way of using less memory. For example,
-    /// even though Zig recognizes `*align(10) i32` and `*i32` both as Pointer types
-    /// but with different alignment values, in this data structure they are represented
-    /// with different enum tags, because the the former requires more payload data than the latter.
-    /// See `zigTypeTag` for the function that corresponds to `std.builtin.TypeId`.
-    pub const Tag = enum(usize) {
-        /// This is a special value that tracks a set of types that have been stored
-        /// to an inferred allocation. It does not support most of the normal type queries.
-        /// However it does respond to `isConstPtr`, `ptrSize`, `zigTypeTag`, etc.
-        inferred_alloc_mut,
-        /// Same as `inferred_alloc_mut` but the local is `var` not `const`.
-        inferred_alloc_const, // See last_no_payload_tag below.
-        // After this, the tag requires a payload.
-
-        pub const last_no_payload_tag = Tag.inferred_alloc_const;
-        pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
-    };
-
     pub fn isTuple(ty: Type, mod: *Module) bool {
         return switch (ty.ip_index) {
             .none => false,
@@ -3511,14 +3289,9 @@ pub const Type = struct {
         };
     }
 
-    /// The sub-types are named after what fields they contain.
     pub const Payload = struct {
-        tag: Tag,
-
         /// TODO: remove this data structure since we have `InternPool.Key.PtrType`.
         pub const Pointer = struct {
-            data: Data,
-
             pub const Data = struct {
                 pointee_type: Type,
                 sentinel: ?Value = null,
@@ -3568,64 +3341,60 @@ pub const Type = struct {
         };
     };
 
-    pub const @"u1": Type = .{ .ip_index = .u1_type, .legacy = undefined };
-    pub const @"u8": Type = .{ .ip_index = .u8_type, .legacy = undefined };
-    pub const @"u16": Type = .{ .ip_index = .u16_type, .legacy = undefined };
-    pub const @"u29": Type = .{ .ip_index = .u29_type, .legacy = undefined };
-    pub const @"u32": Type = .{ .ip_index = .u32_type, .legacy = undefined };
-    pub const @"u64": Type = .{ .ip_index = .u64_type, .legacy = undefined };
-    pub const @"u128": Type = .{ .ip_index = .u128_type, .legacy = undefined };
-
-    pub const @"i8": Type = .{ .ip_index = .i8_type, .legacy = undefined };
-    pub const @"i16": Type = .{ .ip_index = .i16_type, .legacy = undefined };
-    pub const @"i32": Type = .{ .ip_index = .i32_type, .legacy = undefined };
-    pub const @"i64": Type = .{ .ip_index = .i64_type, .legacy = undefined };
-    pub const @"i128": Type = .{ .ip_index = .i128_type, .legacy = undefined };
-
-    pub const @"f16": Type = .{ .ip_index = .f16_type, .legacy = undefined };
-    pub const @"f32": Type = .{ .ip_index = .f32_type, .legacy = undefined };
-    pub const @"f64": Type = .{ .ip_index = .f64_type, .legacy = undefined };
-    pub const @"f80": Type = .{ .ip_index = .f80_type, .legacy = undefined };
-    pub const @"f128": Type = .{ .ip_index = .f128_type, .legacy = undefined };
-
-    pub const @"bool": Type = .{ .ip_index = .bool_type, .legacy = undefined };
-    pub const @"usize": Type = .{ .ip_index = .usize_type, .legacy = undefined };
-    pub const @"isize": Type = .{ .ip_index = .isize_type, .legacy = undefined };
-    pub const @"comptime_int": Type = .{ .ip_index = .comptime_int_type, .legacy = undefined };
-    pub const @"comptime_float": Type = .{ .ip_index = .comptime_float_type, .legacy = undefined };
-    pub const @"void": Type = .{ .ip_index = .void_type, .legacy = undefined };
-    pub const @"type": Type = .{ .ip_index = .type_type, .legacy = undefined };
-    pub const @"anyerror": Type = .{ .ip_index = .anyerror_type, .legacy = undefined };
-    pub const @"anyopaque": Type = .{ .ip_index = .anyopaque_type, .legacy = undefined };
-    pub const @"anyframe": Type = .{ .ip_index = .anyframe_type, .legacy = undefined };
-    pub const @"null": Type = .{ .ip_index = .null_type, .legacy = undefined };
-    pub const @"undefined": Type = .{ .ip_index = .undefined_type, .legacy = undefined };
-    pub const @"noreturn": Type = .{ .ip_index = .noreturn_type, .legacy = undefined };
-
-    pub const @"c_char": Type = .{ .ip_index = .c_char_type, .legacy = undefined };
-    pub const @"c_short": Type = .{ .ip_index = .c_short_type, .legacy = undefined };
-    pub const @"c_ushort": Type = .{ .ip_index = .c_ushort_type, .legacy = undefined };
-    pub const @"c_int": Type = .{ .ip_index = .c_int_type, .legacy = undefined };
-    pub const @"c_uint": Type = .{ .ip_index = .c_uint_type, .legacy = undefined };
-    pub const @"c_long": Type = .{ .ip_index = .c_long_type, .legacy = undefined };
-    pub const @"c_ulong": Type = .{ .ip_index = .c_ulong_type, .legacy = undefined };
-    pub const @"c_longlong": Type = .{ .ip_index = .c_longlong_type, .legacy = undefined };
-    pub const @"c_ulonglong": Type = .{ .ip_index = .c_ulonglong_type, .legacy = undefined };
-    pub const @"c_longdouble": Type = .{ .ip_index = .c_longdouble_type, .legacy = undefined };
-
-    pub const const_slice_u8: Type = .{ .ip_index = .const_slice_u8_type, .legacy = undefined };
-    pub const manyptr_u8: Type = .{ .ip_index = .manyptr_u8_type, .legacy = undefined };
+    pub const @"u1": Type = .{ .ip_index = .u1_type };
+    pub const @"u8": Type = .{ .ip_index = .u8_type };
+    pub const @"u16": Type = .{ .ip_index = .u16_type };
+    pub const @"u29": Type = .{ .ip_index = .u29_type };
+    pub const @"u32": Type = .{ .ip_index = .u32_type };
+    pub const @"u64": Type = .{ .ip_index = .u64_type };
+    pub const @"u128": Type = .{ .ip_index = .u128_type };
+
+    pub const @"i8": Type = .{ .ip_index = .i8_type };
+    pub const @"i16": Type = .{ .ip_index = .i16_type };
+    pub const @"i32": Type = .{ .ip_index = .i32_type };
+    pub const @"i64": Type = .{ .ip_index = .i64_type };
+    pub const @"i128": Type = .{ .ip_index = .i128_type };
+
+    pub const @"f16": Type = .{ .ip_index = .f16_type };
+    pub const @"f32": Type = .{ .ip_index = .f32_type };
+    pub const @"f64": Type = .{ .ip_index = .f64_type };
+    pub const @"f80": Type = .{ .ip_index = .f80_type };
+    pub const @"f128": Type = .{ .ip_index = .f128_type };
+
+    pub const @"bool": Type = .{ .ip_index = .bool_type };
+    pub const @"usize": Type = .{ .ip_index = .usize_type };
+    pub const @"isize": Type = .{ .ip_index = .isize_type };
+    pub const @"comptime_int": Type = .{ .ip_index = .comptime_int_type };
+    pub const @"comptime_float": Type = .{ .ip_index = .comptime_float_type };
+    pub const @"void": Type = .{ .ip_index = .void_type };
+    pub const @"type": Type = .{ .ip_index = .type_type };
+    pub const @"anyerror": Type = .{ .ip_index = .anyerror_type };
+    pub const @"anyopaque": Type = .{ .ip_index = .anyopaque_type };
+    pub const @"anyframe": Type = .{ .ip_index = .anyframe_type };
+    pub const @"null": Type = .{ .ip_index = .null_type };
+    pub const @"undefined": Type = .{ .ip_index = .undefined_type };
+    pub const @"noreturn": Type = .{ .ip_index = .noreturn_type };
+
+    pub const @"c_char": Type = .{ .ip_index = .c_char_type };
+    pub const @"c_short": Type = .{ .ip_index = .c_short_type };
+    pub const @"c_ushort": Type = .{ .ip_index = .c_ushort_type };
+    pub const @"c_int": Type = .{ .ip_index = .c_int_type };
+    pub const @"c_uint": Type = .{ .ip_index = .c_uint_type };
+    pub const @"c_long": Type = .{ .ip_index = .c_long_type };
+    pub const @"c_ulong": Type = .{ .ip_index = .c_ulong_type };
+    pub const @"c_longlong": Type = .{ .ip_index = .c_longlong_type };
+    pub const @"c_ulonglong": Type = .{ .ip_index = .c_ulonglong_type };
+    pub const @"c_longdouble": Type = .{ .ip_index = .c_longdouble_type };
+
+    pub const const_slice_u8: Type = .{ .ip_index = .const_slice_u8_type };
+    pub const manyptr_u8: Type = .{ .ip_index = .manyptr_u8_type };
     pub const single_const_pointer_to_comptime_int: Type = .{
         .ip_index = .single_const_pointer_to_comptime_int_type,
-        .legacy = undefined,
-    };
-    pub const const_slice_u8_sentinel_0: Type = .{
-        .ip_index = .const_slice_u8_sentinel_0_type,
-        .legacy = undefined,
     };
-    pub const empty_struct_literal: Type = .{ .ip_index = .empty_struct_type, .legacy = undefined };
+    pub const const_slice_u8_sentinel_0: Type = .{ .ip_index = .const_slice_u8_sentinel_0_type };
+    pub const empty_struct_literal: Type = .{ .ip_index = .empty_struct_type };
 
-    pub const generic_poison: Type = .{ .ip_index = .generic_poison_type, .legacy = undefined };
+    pub const generic_poison: Type = .{ .ip_index = .generic_poison_type };
 
     pub const err_int = Type.u16;
 
@@ -3709,33 +3478,4 @@ pub const Type = struct {
     /// This is only used for comptime asserts. Bump this number when you make a change
     /// to packed struct layout to find out all the places in the codebase you need to edit!
     pub const packed_struct_layout_version = 2;
-
-    /// This function is used in the debugger pretty formatters in tools/ to fetch the
-    /// Tag to Payload mapping to facilitate fancy debug printing for this type.
-    fn dbHelper(self: *Type, tag_to_payload_map: *map: {
-        const tags = @typeInfo(Tag).Enum.fields;
-        var fields: [tags.len]std.builtin.Type.StructField = undefined;
-        for (&fields, tags) |*field, t| field.* = .{
-            .name = t.name,
-            .type = *if (t.value < Tag.no_payload_count) void else @field(Tag, t.name).Type(),
-            .default_value = null,
-            .is_comptime = false,
-            .alignment = 0,
-        };
-        break :map @Type(.{ .Struct = .{
-            .layout = .Extern,
-            .fields = &fields,
-            .decls = &.{},
-            .is_tuple = false,
-        } });
-    }) void {
-        _ = self;
-        _ = tag_to_payload_map;
-    }
-
-    comptime {
-        if (builtin.mode == .Debug) {
-            _ = &dbHelper;
-        }
-    }
 };
src/value.zig
@@ -2159,9 +2159,7 @@ pub const Value = struct {
             .Null,
             => {},
 
-            .Type => {
-                return val.toType().hashWithHasher(hasher, mod);
-            },
+            .Type => unreachable, // handled via ip_index check above
             .Float => {
                 // For hash/eql purposes, we treat floats as their IEEE integer representation.
                 switch (ty.floatBits(mod.getTarget())) {
@@ -2310,9 +2308,7 @@ pub const Value = struct {
             .Null,
             .Struct, // It sure would be nice to do something clever with structs.
             => |zig_type_tag| std.hash.autoHash(hasher, zig_type_tag),
-            .Type => {
-                val.toType().hashWithHasher(hasher, mod);
-            },
+            .Type => unreachable, // handled above with the ip_index check
             .Float, .ComptimeFloat => std.hash.autoHash(hasher, @bitCast(u128, val.toFloat(f128, mod))),
             .Bool, .Int, .ComptimeInt, .Pointer, .Fn => switch (val.tag()) {
                 .slice => {
src/Zir.zig
@@ -2112,6 +2112,8 @@ pub const Inst = struct {
         const_slice_u8_sentinel_0_type = @enumToInt(InternPool.Index.const_slice_u8_sentinel_0_type),
         anyerror_void_error_union_type = @enumToInt(InternPool.Index.anyerror_void_error_union_type),
         generic_poison_type = @enumToInt(InternPool.Index.generic_poison_type),
+        inferred_alloc_const_type = @enumToInt(InternPool.Index.inferred_alloc_const_type),
+        inferred_alloc_mut_type = @enumToInt(InternPool.Index.inferred_alloc_mut_type),
         empty_struct_type = @enumToInt(InternPool.Index.empty_struct_type),
         undef = @enumToInt(InternPool.Index.undef),
         zero = @enumToInt(InternPool.Index.zero),