Commit 133da8692e

Andrew Kelley <andrew@ziglang.org>
2020-12-31 03:57:11
stage2: rework Type Payload layout
Add `Type.castTag` and note that it is preferable to call than `Type.cast`. This matches other abstractions in the codebase. Added a convenience function `Type.Tag.create` which really cleans up the callsites of creating `Type` objects. `Type` payloads can now share types. This is in preparation for another improvement that I want to do.
1 parent 2622575
src/astgen.zig
@@ -2723,7 +2723,7 @@ fn rlWrap(mod: *Module, scope: *Scope, rl: ResultLoc, result: *zir.Inst) InnerEr
             return mod.fail(scope, result.src, "TODO implement rlWrap .bitcasted_ptr", .{});
         },
         .inferred_ptr => |alloc| {
-            return mod.fail(scope, result.src, "TODO implement rlWrap .inferred_ptr", .{});
+            return addZIRBinOp(mod, scope, result.src, .store, &alloc.base, result);
         },
         .block_ptr => |block_ptr| {
             return mod.fail(scope, result.src, "TODO implement rlWrap .block_ptr", .{});
src/codegen.zig
@@ -3262,7 +3262,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
                         if (typed_value.val.isNull())
                             return MCValue{ .immediate = 0 };
 
-                        var buf: Type.Payload.PointerSimple = undefined;
+                        var buf: Type.Payload.ElemType = undefined;
                         return self.genTypedValue(src, .{
                             .ty = typed_value.ty.optionalChild(&buf),
                             .val = typed_value.val,
src/Compilation.zig
@@ -825,9 +825,11 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
 
             const root_scope = rs: {
                 if (mem.endsWith(u8, root_pkg.root_src_path, ".zig")) {
-                    const struct_payload = try gpa.create(Type.Payload.EmptyStruct);
                     const root_scope = try gpa.create(Module.Scope.File);
-                    struct_payload.* = .{ .scope = &root_scope.root_container };
+                    const struct_ty = try Type.Tag.empty_struct.create(
+                        gpa,
+                        &root_scope.root_container,
+                    );
                     root_scope.* = .{
                         // TODO this is duped so it can be freed in Container.deinit
                         .sub_file_path = try gpa.dupe(u8, root_pkg.root_src_path),
@@ -838,7 +840,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
                         .root_container = .{
                             .file_scope = root_scope,
                             .decls = .{},
-                            .ty = Type.initPayload(&struct_payload.base),
+                            .ty = struct_ty,
                         },
                     };
                     break :rs &root_scope.base;
src/Module.zig
@@ -562,7 +562,7 @@ pub const Scope = struct {
         pub fn deinit(self: *Container, gpa: *Allocator) void {
             self.decls.deinit(gpa);
             // TODO either Container of File should have an arena for sub_file_path and ty
-            gpa.destroy(self.ty.cast(Type.Payload.EmptyStruct).?);
+            gpa.destroy(self.ty.castTag(.empty_struct).?);
             gpa.free(self.file_scope.sub_file_path);
             self.* = undefined;
         }
@@ -2528,12 +2528,11 @@ pub fn analyzeImport(self: *Module, scope: *Scope, src: usize, target_string: []
     }
 
     // TODO Scope.Container arena for ty and sub_file_path
-    const struct_payload = try self.gpa.create(Type.Payload.EmptyStruct);
-    errdefer self.gpa.destroy(struct_payload);
     const file_scope = try self.gpa.create(Scope.File);
     errdefer self.gpa.destroy(file_scope);
+    const struct_ty = try Type.Tag.empty_struct.create(self.gpa, &file_scope.root_container);
+    errdefer self.gpa.destroy(struct_ty.castTag(.empty_struct).?);
 
-    struct_payload.* = .{ .scope = &file_scope.root_container };
     file_scope.* = .{
         .sub_file_path = resolved_path,
         .source = .{ .unloaded = {} },
@@ -2543,7 +2542,7 @@ pub fn analyzeImport(self: *Module, scope: *Scope, src: usize, target_string: []
         .root_container = .{
             .file_scope = file_scope,
             .decls = .{},
-            .ty = Type.initPayload(&struct_payload.base),
+            .ty = struct_ty,
         },
     };
     self.analyzeContainer(&file_scope.root_container) catch |err| switch (err) {
@@ -2564,7 +2563,7 @@ pub fn cmpNumeric(
     lhs: *Inst,
     rhs: *Inst,
     op: std.math.CompareOperator,
-) !*Inst {
+) InnerError!*Inst {
     assert(lhs.ty.isNumeric());
     assert(rhs.ty.isNumeric());
 
@@ -2738,15 +2737,14 @@ fn wrapOptional(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*In
 }
 
 fn makeIntType(self: *Module, scope: *Scope, signed: bool, bits: u16) !Type {
-    if (signed) {
-        const int_payload = try scope.arena().create(Type.Payload.IntSigned);
-        int_payload.* = .{ .bits = bits };
-        return Type.initPayload(&int_payload.base);
-    } else {
-        const int_payload = try scope.arena().create(Type.Payload.IntUnsigned);
-        int_payload.* = .{ .bits = bits };
-        return Type.initPayload(&int_payload.base);
-    }
+    const int_payload = try scope.arena().create(Type.Payload.Bits);
+    int_payload.* = .{
+        .base = .{
+            .tag = if (signed) .int_signed else .int_unsigned,
+        },
+        .data = bits,
+    };
+    return Type.initPayload(&int_payload.base);
 }
 
 pub fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Type {
@@ -2829,7 +2827,7 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst
 
     // T to ?T
     if (dest_type.zigTypeTag() == .Optional) {
-        var buf: Type.Payload.PointerSimple = undefined;
+        var buf: Type.Payload.ElemType = undefined;
         const child_type = dest_type.optionalChild(&buf);
         if (child_type.eql(inst.ty)) {
             return self.wrapOptional(scope, dest_type, inst);
@@ -3225,7 +3223,7 @@ pub fn simplePtrType(self: *Module, scope: *Scope, src: usize, elem_ty: Type, mu
     // TODO stage1 type inference bug
     const T = Type.Tag;
 
-    const type_payload = try scope.arena().create(Type.Payload.PointerSimple);
+    const type_payload = try scope.arena().create(Type.Payload.ElemType);
     type_payload.* = .{
         .base = .{
             .tag = switch (size) {
@@ -3235,7 +3233,7 @@ pub fn simplePtrType(self: *Module, scope: *Scope, src: usize, elem_ty: Type, mu
                 .Slice => if (mutable) T.mut_slice else T.const_slice,
             },
         },
-        .pointee_type = elem_ty,
+        .data = elem_ty,
     };
     return Type.initPayload(&type_payload.base);
 }
@@ -3257,8 +3255,7 @@ pub fn ptrType(
     assert(host_size == 0 or bit_offset < host_size * 8);
 
     // TODO check if type can be represented by simplePtrType
-    const type_payload = try scope.arena().create(Type.Payload.Pointer);
-    type_payload.* = .{
+    return Type.Tag.pointer.create(scope.arena(), .{
         .pointee_type = elem_ty,
         .sentinel = sentinel,
         .@"align" = @"align",
@@ -3268,95 +3265,73 @@ pub fn ptrType(
         .mutable = mutable,
         .@"volatile" = @"volatile",
         .size = size,
-    };
-    return Type.initPayload(&type_payload.base);
+    });
 }
 
 pub fn optionalType(self: *Module, scope: *Scope, child_type: Type) Allocator.Error!Type {
-    return Type.initPayload(switch (child_type.tag()) {
-        .single_const_pointer => blk: {
-            const payload = try scope.arena().create(Type.Payload.PointerSimple);
-            payload.* = .{
-                .base = .{ .tag = .optional_single_const_pointer },
-                .pointee_type = child_type.elemType(),
-            };
-            break :blk &payload.base;
-        },
-        .single_mut_pointer => blk: {
-            const payload = try scope.arena().create(Type.Payload.PointerSimple);
-            payload.* = .{
-                .base = .{ .tag = .optional_single_mut_pointer },
-                .pointee_type = child_type.elemType(),
-            };
-            break :blk &payload.base;
-        },
-        else => blk: {
-            const payload = try scope.arena().create(Type.Payload.Optional);
-            payload.* = .{
-                .child_type = child_type,
-            };
-            break :blk &payload.base;
-        },
-    });
+    switch (child_type.tag()) {
+        .single_const_pointer => return Type.Tag.optional_single_const_pointer.create(
+            scope.arena(),
+            child_type.elemType(),
+        ),
+        .single_mut_pointer => return Type.Tag.optional_single_mut_pointer.create(
+            scope.arena(),
+            child_type.elemType(),
+        ),
+        else => return Type.Tag.optional.create(scope.arena(), child_type),
+    }
 }
 
-pub fn arrayType(self: *Module, scope: *Scope, len: u64, sentinel: ?Value, elem_type: Type) Allocator.Error!Type {
+pub fn arrayType(
+    self: *Module,
+    scope: *Scope,
+    len: u64,
+    sentinel: ?Value,
+    elem_type: Type,
+) Allocator.Error!Type {
     if (elem_type.eql(Type.initTag(.u8))) {
         if (sentinel) |some| {
             if (some.eql(Value.initTag(.zero))) {
-                const payload = try scope.arena().create(Type.Payload.Array_u8_Sentinel0);
-                payload.* = .{
-                    .len = len,
-                };
-                return Type.initPayload(&payload.base);
+                return Type.Tag.array_u8_sentinel_0.create(scope.arena(), len);
             }
         } else {
-            const payload = try scope.arena().create(Type.Payload.Array_u8);
-            payload.* = .{
-                .len = len,
-            };
-            return Type.initPayload(&payload.base);
+            return Type.Tag.array_u8.create(scope.arena(), len);
         }
     }
 
     if (sentinel) |some| {
-        const payload = try scope.arena().create(Type.Payload.ArraySentinel);
-        payload.* = .{
+        return Type.Tag.array_sentinel.create(scope.arena(), .{
             .len = len,
             .sentinel = some,
             .elem_type = elem_type,
-        };
-        return Type.initPayload(&payload.base);
+        });
     }
 
-    const payload = try scope.arena().create(Type.Payload.Array);
-    payload.* = .{
+    return Type.Tag.array.create(scope.arena(), .{
         .len = len,
         .elem_type = elem_type,
-    };
-    return Type.initPayload(&payload.base);
+    });
 }
 
-pub fn errorUnionType(self: *Module, scope: *Scope, error_set: Type, payload: Type) Allocator.Error!Type {
+pub fn errorUnionType(
+    self: *Module,
+    scope: *Scope,
+    error_set: Type,
+    payload: Type,
+) Allocator.Error!Type {
     assert(error_set.zigTypeTag() == .ErrorSet);
     if (error_set.eql(Type.initTag(.anyerror)) and payload.eql(Type.initTag(.void))) {
         return Type.initTag(.anyerror_void_error_union);
     }
 
-    const result = try scope.arena().create(Type.Payload.ErrorUnion);
-    result.* = .{
+    return Type.Tag.error_union.create(scope.arena(), .{
         .error_set = error_set,
         .payload = payload,
-    };
-    return Type.initPayload(&result.base);
+    });
 }
 
 pub fn anyframeType(self: *Module, scope: *Scope, return_type: Type) Allocator.Error!Type {
-    const result = try scope.arena().create(Type.Payload.AnyFrame);
-    result.* = .{
-        .return_type = return_type,
-    };
-    return Type.initPayload(&result.base);
+    return Type.Tag.anyframe_T.create(scope.arena(), return_type);
 }
 
 pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void {
src/type.zig
@@ -112,18 +112,39 @@ pub const Type = extern union {
         }
     }
 
+    /// Prefer `castTag` to this.
     pub fn cast(self: Type, comptime T: type) ?*T {
-        if (self.tag_if_small_enough < Tag.no_payload_count)
+        if (@hasField(T, "base_tag")) {
+            return base.castTag(T.base_tag);
+        }
+        if (self.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.ptr_otherwise.tag == t) {
+                if (T == t.Type()) {
+                    return @fieldParentPtr(T, "base", self.ptr_otherwise);
+                }
+                return null;
+            }
+        }
+        unreachable;
+    }
 
-        const expected_tag = std.meta.fieldInfo(T, "base").default_value.?.tag;
-        if (self.ptr_otherwise.tag != expected_tag)
+    pub fn castTag(self: Type, comptime t: Tag) ?*t.Type() {
+        if (self.tag_if_small_enough < Tag.no_payload_count)
             return null;
 
-        return @fieldParentPtr(T, "base", self.ptr_otherwise);
+        if (self.ptr_otherwise.tag == t)
+            return @fieldParentPtr(t.Type(), "base", self.ptr_otherwise);
+
+        return null;
     }
 
-    pub fn castPointer(self: Type) ?*Payload.PointerSimple {
+    pub fn castPointer(self: Type) ?*Payload.ElemType {
         return switch (self.tag()) {
             .single_const_pointer,
             .single_mut_pointer,
@@ -135,7 +156,8 @@ pub const Type = extern union {
             .mut_slice,
             .optional_single_const_pointer,
             .optional_single_mut_pointer,
-            => @fieldParentPtr(Payload.PointerSimple, "base", self.ptr_otherwise),
+            => self.cast(Payload.ElemType),
+
             else => null,
         };
     }
@@ -165,7 +187,7 @@ pub const Type = extern union {
                 // Hot path for common case:
                 if (a.castPointer()) |a_payload| {
                     if (b.castPointer()) |b_payload| {
-                        return a.tag() == b.tag() and eql(a_payload.pointee_type, b_payload.pointee_type);
+                        return a.tag() == b.tag() and eql(a_payload.data, b_payload.data);
                     }
                 }
                 const is_slice_a = isSlice(a);
@@ -230,8 +252,8 @@ pub const Type = extern union {
                 return true;
             },
             .Optional => {
-                var buf_a: Payload.PointerSimple = undefined;
-                var buf_b: Payload.PointerSimple = undefined;
+                var buf_a: Payload.ElemType = undefined;
+                var buf_b: Payload.ElemType = undefined;
                 return a.optionalChild(&buf_a).eql(b.optionalChild(&buf_b));
             },
             .Float,
@@ -294,7 +316,7 @@ pub const Type = extern union {
                 }
             },
             .Optional => {
-                var buf: Payload.PointerSimple = undefined;
+                var buf: Payload.ElemType = undefined;
                 std.hash.autoHash(&hasher, self.optionalChild(&buf).hash());
             },
             .Float,
@@ -364,68 +386,64 @@ pub const Type = extern union {
             .@"anyframe",
             => unreachable,
 
-            .array_u8_sentinel_0 => return self.copyPayloadShallow(allocator, Payload.Array_u8_Sentinel0),
-            .array_u8 => return self.copyPayloadShallow(allocator, Payload.Array_u8),
+            .array_u8,
+            .array_u8_sentinel_0,
+            => return self.copyPayloadShallow(allocator, Payload.Len),
+
+            .single_const_pointer,
+            .single_mut_pointer,
+            .many_const_pointer,
+            .many_mut_pointer,
+            .c_const_pointer,
+            .c_mut_pointer,
+            .const_slice,
+            .mut_slice,
+            .optional,
+            .optional_single_mut_pointer,
+            .optional_single_const_pointer,
+            .anyframe_T,
+            => return self.copyPayloadShallow(allocator, Payload.ElemType),
+
+            .int_signed,
+            .int_unsigned,
+            => return self.copyPayloadShallow(allocator, Payload.Bits),
+
             .array => {
-                const payload = @fieldParentPtr(Payload.Array, "base", self.ptr_otherwise);
-                const new_payload = try allocator.create(Payload.Array);
-                new_payload.* = .{
-                    .base = payload.base,
+                const payload = self.castTag(.array).?.data;
+                return Tag.array.create(allocator, .{
                     .len = payload.len,
                     .elem_type = try payload.elem_type.copy(allocator),
-                };
-                return Type{ .ptr_otherwise = &new_payload.base };
+                });
             },
             .array_sentinel => {
-                const payload = @fieldParentPtr(Payload.ArraySentinel, "base", self.ptr_otherwise);
-                const new_payload = try allocator.create(Payload.ArraySentinel);
-                new_payload.* = .{
-                    .base = payload.base,
+                const payload = self.castTag(.array_sentinel).?.data;
+                return Tag.array_sentinel.create(allocator, .{
                     .len = payload.len,
                     .sentinel = try payload.sentinel.copy(allocator),
                     .elem_type = try payload.elem_type.copy(allocator),
-                };
-                return Type{ .ptr_otherwise = &new_payload.base };
+                });
             },
-            .int_signed => return self.copyPayloadShallow(allocator, Payload.IntSigned),
-            .int_unsigned => return self.copyPayloadShallow(allocator, Payload.IntUnsigned),
             .function => {
-                const payload = @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise);
-                const new_payload = try allocator.create(Payload.Function);
+                const payload = self.castTag(.function).?.data;
                 const param_types = try allocator.alloc(Type, payload.param_types.len);
                 for (payload.param_types) |param_type, i| {
                     param_types[i] = try param_type.copy(allocator);
                 }
-                new_payload.* = .{
-                    .base = payload.base,
+                return Tag.function.create(allocator, .{
                     .return_type = try payload.return_type.copy(allocator),
                     .param_types = param_types,
                     .cc = payload.cc,
-                };
-                return Type{ .ptr_otherwise = &new_payload.base };
+                });
             },
-            .optional => return self.copyPayloadSingleField(allocator, Payload.Optional, "child_type"),
-            .single_const_pointer,
-            .single_mut_pointer,
-            .many_const_pointer,
-            .many_mut_pointer,
-            .c_const_pointer,
-            .c_mut_pointer,
-            .const_slice,
-            .mut_slice,
-            .optional_single_mut_pointer,
-            .optional_single_const_pointer,
-            => return self.copyPayloadSingleField(allocator, Payload.PointerSimple, "pointee_type"),
-            .anyframe_T => return self.copyPayloadSingleField(allocator, Payload.AnyFrame, "return_type"),
-
             .pointer => {
-                const payload = @fieldParentPtr(Payload.Pointer, "base", self.ptr_otherwise);
-                const new_payload = try allocator.create(Payload.Pointer);
-                new_payload.* = .{
-                    .base = payload.base,
-
+                const payload = self.castTag(.pointer).?.data;
+                const sent: ?Value = if (payload.sentinel) |some|
+                    try some.copy(allocator)
+                else
+                    null;
+                return Tag.pointer.create(allocator, .{
                     .pointee_type = try payload.pointee_type.copy(allocator),
-                    .sentinel = if (payload.sentinel) |some| try some.copy(allocator) else null,
+                    .sentinel = sent,
                     .@"align" = payload.@"align",
                     .bit_offset = payload.bit_offset,
                     .host_size = payload.host_size,
@@ -433,41 +451,28 @@ pub const Type = extern union {
                     .mutable = payload.mutable,
                     .@"volatile" = payload.@"volatile",
                     .size = payload.size,
-                };
-                return Type{ .ptr_otherwise = &new_payload.base };
+                });
             },
             .error_union => {
-                const payload = @fieldParentPtr(Payload.ErrorUnion, "base", self.ptr_otherwise);
-                const new_payload = try allocator.create(Payload.ErrorUnion);
-                new_payload.* = .{
-                    .base = payload.base,
-
+                const payload = self.castTag(.error_union).?.data;
+                return Tag.error_union.create(allocator, .{
                     .error_set = try payload.error_set.copy(allocator),
                     .payload = try payload.payload.copy(allocator),
-                };
-                return Type{ .ptr_otherwise = &new_payload.base };
+                });
             },
-            .error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet),
-            .error_set_single => return self.copyPayloadShallow(allocator, Payload.ErrorSetSingle),
-            .empty_struct => return self.copyPayloadShallow(allocator, Payload.EmptyStruct),
+            .error_set => return self.copyPayloadShallow(allocator, Payload.Decl),
+            .error_set_single => return self.copyPayloadShallow(allocator, Payload.Name),
+            .empty_struct => return self.copyPayloadShallow(allocator, Payload.ContainerScope),
         }
     }
 
     fn copyPayloadShallow(self: Type, allocator: *Allocator, comptime T: type) error{OutOfMemory}!Type {
-        const payload = @fieldParentPtr(T, "base", self.ptr_otherwise);
+        const payload = self.cast(T).?;
         const new_payload = try allocator.create(T);
         new_payload.* = payload.*;
         return Type{ .ptr_otherwise = &new_payload.base };
     }
 
-    fn copyPayloadSingleField(self: Type, allocator: *Allocator, comptime T: type, comptime field_name: []const u8) error{OutOfMemory}!Type {
-        const payload = @fieldParentPtr(T, "base", self.ptr_otherwise);
-        const new_payload = try allocator.create(T);
-        new_payload.base = payload.base;
-        @field(new_payload, field_name) = try @field(payload, field_name).copy(allocator);
-        return Type{ .ptr_otherwise = &new_payload.base };
-    }
-
     pub fn format(
         self: Type,
         comptime fmt: []const u8,
@@ -527,7 +532,7 @@ pub const Type = extern union {
                 .fn_ccc_void_no_args => return out_stream.writeAll("fn() callconv(.C) void"),
                 .single_const_pointer_to_comptime_int => return out_stream.writeAll("*const comptime_int"),
                 .function => {
-                    const payload = @fieldParentPtr(Payload.Function, "base", ty.ptr_otherwise);
+                    const payload = ty.castTag(.function).?.data;
                     try out_stream.writeAll("fn(");
                     for (payload.param_types) |param_type, i| {
                         if (i != 0) try out_stream.writeAll(", ");
@@ -539,108 +544,108 @@ pub const Type = extern union {
                 },
 
                 .anyframe_T => {
-                    const payload = @fieldParentPtr(Payload.AnyFrame, "base", ty.ptr_otherwise);
+                    const return_type = ty.castTag(.anyframe_T).?.data;
                     try out_stream.print("anyframe->", .{});
-                    ty = payload.return_type;
+                    ty = return_type;
                     continue;
                 },
                 .array_u8 => {
-                    const payload = @fieldParentPtr(Payload.Array_u8, "base", ty.ptr_otherwise);
-                    return out_stream.print("[{}]u8", .{payload.len});
+                    const len = ty.castTag(.array_u8).?.data;
+                    return out_stream.print("[{}]u8", .{len});
                 },
                 .array_u8_sentinel_0 => {
-                    const payload = @fieldParentPtr(Payload.Array_u8_Sentinel0, "base", ty.ptr_otherwise);
-                    return out_stream.print("[{}:0]u8", .{payload.len});
+                    const len = ty.castTag(.array_u8_sentinel_0).?.data;
+                    return out_stream.print("[{}:0]u8", .{len});
                 },
                 .array => {
-                    const payload = @fieldParentPtr(Payload.Array, "base", ty.ptr_otherwise);
+                    const payload = ty.castTag(.array).?.data;
                     try out_stream.print("[{}]", .{payload.len});
                     ty = payload.elem_type;
                     continue;
                 },
                 .array_sentinel => {
-                    const payload = @fieldParentPtr(Payload.ArraySentinel, "base", ty.ptr_otherwise);
+                    const payload = ty.castTag(.array_sentinel).?.data;
                     try out_stream.print("[{}:{}]", .{ payload.len, payload.sentinel });
                     ty = payload.elem_type;
                     continue;
                 },
                 .single_const_pointer => {
-                    const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
+                    const pointee_type = ty.castTag(.single_const_pointer).?.data;
                     try out_stream.writeAll("*const ");
-                    ty = payload.pointee_type;
+                    ty = pointee_type;
                     continue;
                 },
                 .single_mut_pointer => {
-                    const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
+                    const pointee_type = ty.castTag(.single_mut_pointer).?.data;
                     try out_stream.writeAll("*");
-                    ty = payload.pointee_type;
+                    ty = pointee_type;
                     continue;
                 },
                 .many_const_pointer => {
-                    const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
+                    const pointee_type = ty.castTag(.many_const_pointer).?.data;
                     try out_stream.writeAll("[*]const ");
-                    ty = payload.pointee_type;
+                    ty = pointee_type;
                     continue;
                 },
                 .many_mut_pointer => {
-                    const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
+                    const pointee_type = ty.castTag(.many_mut_pointer).?.data;
                     try out_stream.writeAll("[*]");
-                    ty = payload.pointee_type;
+                    ty = pointee_type;
                     continue;
                 },
                 .c_const_pointer => {
-                    const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
+                    const pointee_type = ty.castTag(.c_const_pointer).?.data;
                     try out_stream.writeAll("[*c]const ");
-                    ty = payload.pointee_type;
+                    ty = pointee_type;
                     continue;
                 },
                 .c_mut_pointer => {
-                    const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
+                    const pointee_type = ty.castTag(.c_mut_pointer).?.data;
                     try out_stream.writeAll("[*c]");
-                    ty = payload.pointee_type;
+                    ty = pointee_type;
                     continue;
                 },
                 .const_slice => {
-                    const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
+                    const pointee_type = ty.castTag(.const_slice).?.data;
                     try out_stream.writeAll("[]const ");
-                    ty = payload.pointee_type;
+                    ty = pointee_type;
                     continue;
                 },
                 .mut_slice => {
-                    const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
+                    const pointee_type = ty.castTag(.mut_slice).?.data;
                     try out_stream.writeAll("[]");
-                    ty = payload.pointee_type;
+                    ty = pointee_type;
                     continue;
                 },
                 .int_signed => {
-                    const payload = @fieldParentPtr(Payload.IntSigned, "base", ty.ptr_otherwise);
-                    return out_stream.print("i{}", .{payload.bits});
+                    const bits = ty.castTag(.int_signed).?.data;
+                    return out_stream.print("i{d}", .{bits});
                 },
                 .int_unsigned => {
-                    const payload = @fieldParentPtr(Payload.IntUnsigned, "base", ty.ptr_otherwise);
-                    return out_stream.print("u{}", .{payload.bits});
+                    const bits = ty.castTag(.int_unsigned).?.data;
+                    return out_stream.print("u{d}", .{bits});
                 },
                 .optional => {
-                    const payload = @fieldParentPtr(Payload.Optional, "base", ty.ptr_otherwise);
+                    const child_type = ty.castTag(.optional).?.data;
                     try out_stream.writeByte('?');
-                    ty = payload.child_type;
+                    ty = child_type;
                     continue;
                 },
                 .optional_single_const_pointer => {
-                    const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
+                    const pointee_type = ty.castTag(.optional_single_const_pointer).?.data;
                     try out_stream.writeAll("?*const ");
-                    ty = payload.pointee_type;
+                    ty = pointee_type;
                     continue;
                 },
                 .optional_single_mut_pointer => {
-                    const payload = @fieldParentPtr(Payload.PointerSimple, "base", ty.ptr_otherwise);
+                    const pointee_type = ty.castTag(.optional_single_mut_pointer).?.data;
                     try out_stream.writeAll("?*");
-                    ty = payload.pointee_type;
+                    ty = pointee_type;
                     continue;
                 },
 
                 .pointer => {
-                    const payload = @fieldParentPtr(Payload.Pointer, "base", ty.ptr_otherwise);
+                    const payload = ty.castTag(.pointer).?.data;
                     if (payload.sentinel) |some| switch (payload.size) {
                         .One, .C => unreachable,
                         .Many => try out_stream.print("[*:{}]", .{some}),
@@ -652,10 +657,10 @@ pub const Type = extern union {
                         .Slice => try out_stream.writeAll("[]"),
                     }
                     if (payload.@"align" != 0) {
-                        try out_stream.print("align({}", .{payload.@"align"});
+                        try out_stream.print("align({d}", .{payload.@"align"});
 
                         if (payload.bit_offset != 0) {
-                            try out_stream.print(":{}:{}", .{ payload.bit_offset, payload.host_size });
+                            try out_stream.print(":{d}:{d}", .{ payload.bit_offset, payload.host_size });
                         }
                         try out_stream.writeAll(") ");
                     }
@@ -667,19 +672,19 @@ pub const Type = extern union {
                     continue;
                 },
                 .error_union => {
-                    const payload = @fieldParentPtr(Payload.ErrorUnion, "base", ty.ptr_otherwise);
+                    const payload = ty.castTag(.error_union).?.data;
                     try payload.error_set.format("", .{}, out_stream);
                     try out_stream.writeAll("!");
                     ty = payload.payload;
                     continue;
                 },
                 .error_set => {
-                    const payload = @fieldParentPtr(Payload.ErrorSet, "base", ty.ptr_otherwise);
-                    return out_stream.writeAll(std.mem.spanZ(payload.decl.name));
+                    const decl = ty.castTag(.error_set).?.data;
+                    return out_stream.writeAll(std.mem.spanZ(decl.name));
                 },
                 .error_set_single => {
-                    const payload = @fieldParentPtr(Payload.ErrorSetSingle, "base", ty.ptr_otherwise);
-                    return out_stream.print("error{{{}}}", .{payload.name});
+                    const name = ty.castTag(.error_set_single).?.data;
+                    return out_stream.print("error{{{s}}}", .{name});
                 },
             }
             unreachable;
@@ -784,11 +789,10 @@ pub const Type = extern union {
             .array => self.elemType().hasCodeGenBits() and self.arrayLen() != 0,
             .array_u8 => self.arrayLen() != 0,
             .array_sentinel, .single_const_pointer, .single_mut_pointer, .many_const_pointer, .many_mut_pointer, .c_const_pointer, .c_mut_pointer, .const_slice, .mut_slice, .pointer => self.elemType().hasCodeGenBits(),
-            .int_signed => self.cast(Payload.IntSigned).?.bits != 0,
-            .int_unsigned => self.cast(Payload.IntUnsigned).?.bits != 0,
+            .int_signed, .int_unsigned => self.cast(Payload.Bits).?.data != 0,
 
             .error_union => {
-                const payload = self.cast(Payload.ErrorUnion).?;
+                const payload = self.castTag(.error_union).?.data;
                 return payload.error_set.hasCodeGenBits() or payload.payload.hasCodeGenBits();
             },
 
@@ -855,7 +859,7 @@ pub const Type = extern union {
             => return @divExact(target.cpu.arch.ptrBitWidth(), 8),
 
             .pointer => {
-                const payload = @fieldParentPtr(Payload.Pointer, "base", self.ptr_otherwise);
+                const payload = self.castTag(.pointer).?.data;
 
                 if (payload.@"align" != 0) return payload.@"align";
                 return @divExact(target.cpu.arch.ptrBitWidth(), 8);
@@ -885,18 +889,12 @@ pub const Type = extern union {
             .array, .array_sentinel => return self.elemType().abiAlignment(target),
 
             .int_signed, .int_unsigned => {
-                const bits: u16 = if (self.cast(Payload.IntSigned)) |pl|
-                    pl.bits
-                else if (self.cast(Payload.IntUnsigned)) |pl|
-                    pl.bits
-                else
-                    unreachable;
-
+                const bits: u16 = self.cast(Payload.Bits).?.data;
                 return std.math.ceilPowerOfTwoPromote(u16, (bits + 7) / 8);
             },
 
             .optional => {
-                var buf: Payload.PointerSimple = undefined;
+                var buf: Payload.ElemType = undefined;
                 const child_type = self.optionalChild(&buf);
                 if (!child_type.hasCodeGenBits()) return 1;
 
@@ -907,7 +905,7 @@ pub const Type = extern union {
             },
 
             .error_union => {
-                const payload = self.cast(Payload.ErrorUnion).?;
+                const payload = self.castTag(.error_union).?.data;
                 if (!payload.error_set.hasCodeGenBits()) {
                     return payload.payload.abiAlignment(target);
                 } else if (!payload.payload.hasCodeGenBits()) {
@@ -955,16 +953,19 @@ pub const Type = extern union {
             .bool,
             => return 1,
 
-            .array_u8 => @fieldParentPtr(Payload.Array_u8_Sentinel0, "base", self.ptr_otherwise).len,
-            .array_u8_sentinel_0 => @fieldParentPtr(Payload.Array_u8_Sentinel0, "base", self.ptr_otherwise).len + 1,
+            .array_u8 => self.castTag(.array_u8).?.data,
+            .array_u8_sentinel_0 => self.castTag(.array_u8_sentinel_0).?.data + 1,
             .array => {
-                const payload = @fieldParentPtr(Payload.Array, "base", self.ptr_otherwise);
+                const payload = self.castTag(.array).?.data;
                 const elem_size = std.math.max(payload.elem_type.abiAlignment(target), payload.elem_type.abiSize(target));
                 return payload.len * elem_size;
             },
             .array_sentinel => {
-                const payload = @fieldParentPtr(Payload.ArraySentinel, "base", self.ptr_otherwise);
-                const elem_size = std.math.max(payload.elem_type.abiAlignment(target), payload.elem_type.abiSize(target));
+                const payload = self.castTag(.array_sentinel).?.data;
+                const elem_size = std.math.max(
+                    payload.elem_type.abiAlignment(target),
+                    payload.elem_type.abiSize(target),
+                );
                 return (payload.len + 1) * elem_size;
             },
             .i16, .u16 => return 2,
@@ -1022,18 +1023,12 @@ pub const Type = extern union {
             => return 2, // TODO revisit this when we have the concept of the error tag type
 
             .int_signed, .int_unsigned => {
-                const bits: u16 = if (self.cast(Payload.IntSigned)) |pl|
-                    pl.bits
-                else if (self.cast(Payload.IntUnsigned)) |pl|
-                    pl.bits
-                else
-                    unreachable;
-
+                const bits: u16 = self.cast(Payload.Bits).?.data;
                 return std.math.ceilPowerOfTwoPromote(u16, (bits + 7) / 8);
             },
 
             .optional => {
-                var buf: Payload.PointerSimple = undefined;
+                var buf: Payload.ElemType = undefined;
                 const child_type = self.optionalChild(&buf);
                 if (!child_type.hasCodeGenBits()) return 1;
 
@@ -1048,7 +1043,7 @@ pub const Type = extern union {
             },
 
             .error_union => {
-                const payload = self.cast(Payload.ErrorUnion).?;
+                const payload = self.castTag(.error_union).?.data;
                 if (!payload.error_set.hasCodeGenBits() and !payload.payload.hasCodeGenBits()) {
                     return 0;
                 } else if (!payload.error_set.hasCodeGenBits()) {
@@ -1132,7 +1127,7 @@ pub const Type = extern union {
             .single_const_pointer_to_comptime_int,
             => true,
 
-            .pointer => self.cast(Payload.Pointer).?.size == .One,
+            .pointer => self.castTag(.pointer).?.data.size == .One,
         };
     }
 
@@ -1214,7 +1209,7 @@ pub const Type = extern union {
             .single_const_pointer_to_comptime_int,
             => .One,
 
-            .pointer => self.cast(Payload.Pointer).?.size,
+            .pointer => self.castTag(.pointer).?.data.size,
         };
     }
 
@@ -1289,7 +1284,7 @@ pub const Type = extern union {
             .const_slice_u8,
             => true,
 
-            .pointer => self.cast(Payload.Pointer).?.size == .Slice,
+            .pointer => self.castTag(.pointer).?.data.size == .Slice,
         };
     }
 
@@ -1364,7 +1359,7 @@ pub const Type = extern union {
             .const_slice,
             => true,
 
-            .pointer => !self.cast(Payload.Pointer).?.mutable,
+            .pointer => !self.castTag(.pointer).?.data.mutable,
         };
     }
 
@@ -1438,7 +1433,7 @@ pub const Type = extern union {
             => false,
 
             .pointer => {
-                const payload = @fieldParentPtr(Payload.Pointer, "base", self.ptr_otherwise);
+                const payload = self.castTag(.pointer).?.data;
                 return payload.@"volatile";
             },
         };
@@ -1514,7 +1509,7 @@ pub const Type = extern union {
             => false,
 
             .pointer => {
-                const payload = @fieldParentPtr(Payload.Pointer, "base", self.ptr_otherwise);
+                const payload = self.castTag(.pointer).?.data;
                 return payload.@"allowzero";
             },
         };
@@ -1525,7 +1520,7 @@ pub const Type = extern union {
         switch (self.tag()) {
             .optional_single_const_pointer, .optional_single_mut_pointer => return true,
             .optional => {
-                var buf: Payload.PointerSimple = undefined;
+                var buf: Payload.ElemType = undefined;
                 const child_type = self.optionalChild(&buf);
                 // optionals of zero sized pointers behave like bools
                 if (!child_type.hasCodeGenBits()) return false;
@@ -1563,7 +1558,7 @@ pub const Type = extern union {
             => return false,
 
             .Optional => {
-                var buf: Payload.PointerSimple = undefined;
+                var buf: Payload.ElemType = undefined;
                 return ty.optionalChild(&buf).isValidVarType(is_extern);
             },
             .Pointer, .Array => ty = ty.elemType(),
@@ -1631,8 +1626,8 @@ pub const Type = extern union {
             .empty_struct,
             => unreachable,
 
-            .array => self.cast(Payload.Array).?.elem_type,
-            .array_sentinel => self.cast(Payload.ArraySentinel).?.elem_type,
+            .array => self.castTag(.array).?.data.elem_type,
+            .array_sentinel => self.castTag(.array_sentinel).?.data.elem_type,
             .single_const_pointer,
             .single_mut_pointer,
             .many_const_pointer,
@@ -1641,28 +1636,29 @@ pub const Type = extern union {
             .c_mut_pointer,
             .const_slice,
             .mut_slice,
-            => self.castPointer().?.pointee_type,
+            => self.castPointer().?.data,
             .array_u8, .array_u8_sentinel_0, .const_slice_u8 => Type.initTag(.u8),
             .single_const_pointer_to_comptime_int => Type.initTag(.comptime_int),
-            .pointer => self.cast(Payload.Pointer).?.pointee_type,
+            .pointer => self.castTag(.pointer).?.data.pointee_type,
         };
     }
 
     /// Asserts that the type is an optional.
-    pub fn optionalChild(self: Type, buf: *Payload.PointerSimple) Type {
+    /// Resulting `Type` will have inner memory referencing `buf`.
+    pub fn optionalChild(self: Type, buf: *Payload.ElemType) Type {
         return switch (self.tag()) {
-            .optional => self.cast(Payload.Optional).?.child_type,
+            .optional => self.castTag(.optional).?.data,
             .optional_single_mut_pointer => {
                 buf.* = .{
                     .base = .{ .tag = .single_mut_pointer },
-                    .pointee_type = self.castPointer().?.pointee_type,
+                    .data = self.castPointer().?.data,
                 };
                 return Type.initPayload(&buf.base);
             },
             .optional_single_const_pointer => {
                 buf.* = .{
                     .base = .{ .tag = .single_const_pointer },
-                    .pointee_type = self.castPointer().?.pointee_type,
+                    .data = self.castPointer().?.data,
                 };
                 return Type.initPayload(&buf.base);
             },
@@ -1673,23 +1669,16 @@ pub const Type = extern union {
     /// Asserts that the type is an optional.
     /// Same as `optionalChild` but allocates the buffer if needed.
     pub fn optionalChildAlloc(self: Type, allocator: *Allocator) !Type {
-        return switch (self.tag()) {
-            .optional => self.cast(Payload.Optional).?.child_type,
-            .optional_single_mut_pointer, .optional_single_const_pointer => {
-                const payload = try allocator.create(Payload.PointerSimple);
-                payload.* = .{
-                    .base = .{
-                        .tag = if (self.tag() == .optional_single_const_pointer)
-                            .single_const_pointer
-                        else
-                            .single_mut_pointer,
-                    },
-                    .pointee_type = self.castPointer().?.pointee_type,
-                };
-                return Type.initPayload(&payload.base);
+        switch (self.tag()) {
+            .optional => return self.castTag(.optional).?.data,
+            .optional_single_mut_pointer => {
+                return Tag.single_mut_pointer.create(allocator, self.castPointer().?.data);
+            },
+            .optional_single_const_pointer => {
+                return Tag.single_const_pointer.create(allocator, self.castPointer().?.data);
             },
             else => unreachable,
-        };
+        }
     }
 
     /// Asserts the type is an array or vector.
@@ -1759,10 +1748,10 @@ pub const Type = extern union {
             .empty_struct,
             => unreachable,
 
-            .array => self.cast(Payload.Array).?.len,
-            .array_sentinel => self.cast(Payload.ArraySentinel).?.len,
-            .array_u8 => self.cast(Payload.Array_u8).?.len,
-            .array_u8_sentinel_0 => self.cast(Payload.Array_u8_Sentinel0).?.len,
+            .array => self.castTag(.array).?.data.len,
+            .array_sentinel => self.castTag(.array_sentinel).?.data.len,
+            .array_u8 => self.castTag(.array_u8).?.data,
+            .array_u8_sentinel_0 => self.castTag(.array_u8_sentinel_0).?.data,
         };
     }
 
@@ -1836,8 +1825,8 @@ pub const Type = extern union {
             .array_u8,
             => return null,
 
-            .pointer => return self.cast(Payload.Pointer).?.sentinel,
-            .array_sentinel => return self.cast(Payload.ArraySentinel).?.sentinel,
+            .pointer => return self.castTag(.pointer).?.data.sentinel,
+            .array_sentinel => return self.castTag(.array_sentinel).?.data.sentinel,
             .array_u8_sentinel_0 => return Value.initTag(.zero),
         };
     }
@@ -2048,8 +2037,14 @@ pub const Type = extern union {
             .empty_struct,
             => unreachable,
 
-            .int_unsigned => .{ .signedness = .unsigned, .bits = self.cast(Payload.IntUnsigned).?.bits },
-            .int_signed => .{ .signedness = .signed, .bits = self.cast(Payload.IntSigned).?.bits },
+            .int_unsigned => .{
+                .signedness = .unsigned,
+                .bits = self.castTag(.int_unsigned).?.data,
+            },
+            .int_signed => .{
+                .signedness = .signed,
+                .bits = self.castTag(.int_signed).?.data,
+            },
             .u8 => .{ .signedness = .unsigned, .bits = 8 },
             .i8 => .{ .signedness = .signed, .bits = 8 },
             .u16 => .{ .signedness = .unsigned, .bits = 16 },
@@ -2178,7 +2173,7 @@ pub const Type = extern union {
             .fn_void_no_args => 0,
             .fn_naked_noreturn_no_args => 0,
             .fn_ccc_void_no_args => 0,
-            .function => @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise).param_types.len,
+            .function => self.castTag(.function).?.data.param_types.len,
 
             .f16,
             .f32,
@@ -2254,7 +2249,7 @@ pub const Type = extern union {
             .fn_naked_noreturn_no_args => return,
             .fn_ccc_void_no_args => return,
             .function => {
-                const payload = @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise);
+                const payload = self.castTag(.function).?.data;
                 std.mem.copy(Type, types, payload.param_types);
             },
 
@@ -2327,7 +2322,7 @@ pub const Type = extern union {
     pub fn fnParamType(self: Type, index: usize) Type {
         switch (self.tag()) {
             .function => {
-                const payload = @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise);
+                const payload = self.castTag(.function).?.data;
                 return payload.param_types[index];
             },
 
@@ -2410,7 +2405,7 @@ pub const Type = extern union {
             .fn_ccc_void_no_args,
             => Type.initTag(.void),
 
-            .function => @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise).return_type,
+            .function => self.castTag(.function).?.data.return_type,
 
             .f16,
             .f32,
@@ -2484,7 +2479,7 @@ pub const Type = extern union {
             .fn_void_no_args => .Unspecified,
             .fn_naked_noreturn_no_args => .Naked,
             .fn_ccc_void_no_args => .C,
-            .function => @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise).cc,
+            .function => self.castTag(.function).?.data.cc,
 
             .f16,
             .f32,
@@ -2760,15 +2755,8 @@ pub const Type = extern union {
             .@"null" => return Value.initTag(.null_value),
             .@"undefined" => return Value.initTag(.undef),
 
-            .int_unsigned => {
-                if (ty.cast(Payload.IntUnsigned).?.bits == 0) {
-                    return Value.initTag(.zero);
-                } else {
-                    return null;
-                }
-            },
-            .int_signed => {
-                if (ty.cast(Payload.IntSigned).?.bits == 0) {
+            .int_unsigned, .int_signed => {
+                if (ty.cast(Payload.Bits).?.data == 0) {
                     return Value.initTag(.zero);
                 } else {
                     return null;
@@ -2787,12 +2775,11 @@ pub const Type = extern union {
             .single_const_pointer,
             .single_mut_pointer,
             => {
-                const ptr = ty.castPointer().?;
-                ty = ptr.pointee_type;
+                ty = ty.castPointer().?.data;
                 continue;
             },
             .pointer => {
-                ty = ty.cast(Payload.Pointer).?.pointee_type;
+                ty = ty.castTag(.pointer).?.data.pointee_type;
                 continue;
             },
         };
@@ -2869,7 +2856,7 @@ pub const Type = extern union {
             .c_mut_pointer,
             => return true,
 
-            .pointer => self.cast(Payload.Pointer).?.size == .C,
+            .pointer => self.castTag(.pointer).?.data.size == .C,
         };
     }
 
@@ -2950,7 +2937,7 @@ pub const Type = extern union {
             .pointer,
             => unreachable,
 
-            .empty_struct => self.cast(Type.Payload.EmptyStruct).?.scope,
+            .empty_struct => self.castTag(.empty_struct).?.data,
         };
     }
 
@@ -3105,117 +3092,195 @@ pub const Type = extern union {
 
         pub const last_no_payload_tag = Tag.const_slice_u8;
         pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
-    };
 
-    pub const Payload = struct {
-        tag: Tag,
+        pub fn Type(comptime t: Tag) type {
+            return switch (t) {
+                .u8,
+                .i8,
+                .u16,
+                .i16,
+                .u32,
+                .i32,
+                .u64,
+                .i64,
+                .usize,
+                .isize,
+                .c_short,
+                .c_ushort,
+                .c_int,
+                .c_uint,
+                .c_long,
+                .c_ulong,
+                .c_longlong,
+                .c_ulonglong,
+                .c_longdouble,
+                .f16,
+                .f32,
+                .f64,
+                .f128,
+                .c_void,
+                .bool,
+                .void,
+                .type,
+                .anyerror,
+                .comptime_int,
+                .comptime_float,
+                .noreturn,
+                .enum_literal,
+                .@"null",
+                .@"undefined",
+                .fn_noreturn_no_args,
+                .fn_void_no_args,
+                .fn_naked_noreturn_no_args,
+                .fn_ccc_void_no_args,
+                .single_const_pointer_to_comptime_int,
+                .anyerror_void_error_union,
+                .@"anyframe",
+                .const_slice_u8,
+                => @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"),
+
+                .array_u8,
+                .array_u8_sentinel_0,
+                => Payload.Len,
+
+                .single_const_pointer,
+                .single_mut_pointer,
+                .many_const_pointer,
+                .many_mut_pointer,
+                .c_const_pointer,
+                .c_mut_pointer,
+                .const_slice,
+                .mut_slice,
+                .optional,
+                .optional_single_mut_pointer,
+                .optional_single_const_pointer,
+                .anyframe_T,
+                => Payload.ElemType,
+
+                .int_signed,
+                .int_unsigned,
+                => Payload.Bits,
+
+                .array => Payload.Array,
+                .array_sentinel => Payload.ArraySentinel,
+                .pointer => Payload.Pointer,
+                .function => Payload.Function,
+                .error_union => Payload.ErrorUnion,
+                .error_set => Payload.Decl,
+                .error_set_single => Payload.Name,
+                .empty_struct => Payload.ContainerScope,
+            };
+        }
 
-        pub const Array_u8_Sentinel0 = struct {
-            base: Payload = Payload{ .tag = .array_u8_sentinel_0 },
+        pub fn create(comptime t: Tag, ally: *Allocator, data: Data(t)) error{OutOfMemory}!Type {
+            const ptr = try ally.create(t.Type());
+            ptr.* = .{
+                .base = .{ .tag = t },
+                .data = data,
+            };
+            return Type{ .ptr_otherwise = &ptr.base };
+        }
 
-            len: u64,
-        };
+        pub fn Data(comptime t: Tag) type {
+            return std.meta.fieldInfo(t.Type(), "data").field_type;
+        }
+    };
 
-        pub const Array_u8 = struct {
-            base: Payload = Payload{ .tag = .array_u8 },
+    /// The sub-types are named after what fields they contain.
+    pub const Payload = struct {
+        tag: Tag,
 
-            len: u64,
+        pub const Len = struct {
+            base: Payload,
+            data: u64,
         };
 
         pub const Array = struct {
-            base: Payload = Payload{ .tag = .array },
+            pub const base_tag = Tag.array;
 
-            len: u64,
-            elem_type: Type,
+            base: Payload = Payload{ .tag = base_tag },
+            data: struct {
+                len: u64,
+                elem_type: Type,
+            },
         };
 
         pub const ArraySentinel = struct {
-            base: Payload = Payload{ .tag = .array_sentinel },
+            pub const base_tag = Tag.array_sentinel;
 
-            len: u64,
-            sentinel: Value,
-            elem_type: Type,
+            base: Payload = Payload{ .tag = base_tag },
+            data: struct {
+                len: u64,
+                sentinel: Value,
+                elem_type: Type,
+            },
         };
 
-        pub const PointerSimple = struct {
+        pub const ElemType = struct {
             base: Payload,
-
-            pointee_type: Type,
+            data: Type,
         };
 
-        pub const IntSigned = struct {
-            base: Payload = Payload{ .tag = .int_signed },
-
-            bits: u16,
-        };
-
-        pub const IntUnsigned = struct {
-            base: Payload = Payload{ .tag = .int_unsigned },
-
-            bits: u16,
+        pub const Bits = struct {
+            base: Payload,
+            data: u16,
         };
 
         pub const Function = struct {
-            base: Payload = Payload{ .tag = .function },
-
-            param_types: []Type,
-            return_type: Type,
-            cc: std.builtin.CallingConvention,
-        };
-
-        pub const Optional = struct {
-            base: Payload = Payload{ .tag = .optional },
+            pub const base_tag = Tag.function;
 
-            child_type: Type,
+            base: Payload = Payload{ .tag = base_tag },
+            data: struct {
+                param_types: []Type,
+                return_type: Type,
+                cc: std.builtin.CallingConvention,
+            },
         };
 
         pub const Pointer = struct {
-            base: Payload = .{ .tag = .pointer },
-
-            pointee_type: Type,
-            sentinel: ?Value,
-            /// If zero use pointee_type.AbiAlign()
-            @"align": u32,
-            bit_offset: u16,
-            host_size: u16,
-            @"allowzero": bool,
-            mutable: bool,
-            @"volatile": bool,
-            size: std.builtin.TypeInfo.Pointer.Size,
+            pub const base_tag = Tag.pointer;
+
+            base: Payload = Payload{ .tag = base_tag },
+            data: struct {
+                pointee_type: Type,
+                sentinel: ?Value,
+                /// If zero use pointee_type.AbiAlign()
+                @"align": u32,
+                bit_offset: u16,
+                host_size: u16,
+                @"allowzero": bool,
+                mutable: bool,
+                @"volatile": bool,
+                size: std.builtin.TypeInfo.Pointer.Size,
+            },
         };
 
         pub const ErrorUnion = struct {
-            base: Payload = .{ .tag = .error_union },
+            pub const base_tag = Tag.error_union;
 
-            error_set: Type,
-            payload: Type,
-        };
-
-        pub const AnyFrame = struct {
-            base: Payload = .{ .tag = .anyframe_T },
-
-            return_type: Type,
+            base: Payload = Payload{ .tag = base_tag },
+            data: struct {
+                error_set: Type,
+                payload: Type,
+            },
         };
 
-        pub const ErrorSet = struct {
-            base: Payload = .{ .tag = .error_set },
-
-            decl: *Module.Decl,
+        pub const Decl = struct {
+            base: Payload,
+            data: *Module.Decl,
         };
 
-        pub const ErrorSetSingle = struct {
-            base: Payload = .{ .tag = .error_set_single },
-
+        pub const Name = struct {
+            base: Payload,
             /// memory is owned by `Module`
-            name: []const u8,
+            data: []const u8,
         };
 
         /// Mostly used for namespace like structs with zero fields.
         /// Most commonly used for files.
-        pub const EmptyStruct = struct {
-            base: Payload = .{ .tag = .empty_struct },
-
-            scope: *Module.Scope.Container,
+        pub const ContainerScope = struct {
+            base: Payload,
+            data: *Module.Scope.Container,
         };
     };
 };
src/value.zig
@@ -440,21 +440,18 @@ pub const Value = extern union {
 
             .int_type => {
                 const payload = self.cast(Payload.IntType).?;
-                if (payload.signed) {
-                    const new = try allocator.create(Type.Payload.IntSigned);
-                    new.* = .{ .bits = payload.bits };
-                    return Type.initPayload(&new.base);
-                } else {
-                    const new = try allocator.create(Type.Payload.IntUnsigned);
-                    new.* = .{ .bits = payload.bits };
-                    return Type.initPayload(&new.base);
-                }
+                const new = try allocator.create(Type.Payload.Bits);
+                new.* = .{
+                    .base = .{
+                        .tag = if (payload.signed) .int_signed else .int_unsigned,
+                    },
+                    .data = payload.bits,
+                };
+                return Type.initPayload(&new.base);
             },
             .error_set => {
                 const payload = self.cast(Payload.ErrorSet).?;
-                const new = try allocator.create(Type.Payload.ErrorSet);
-                new.* = .{ .decl = payload.decl };
-                return Type.initPayload(&new.base);
+                return Type.Tag.error_set.create(allocator, payload.decl);
             },
 
             .undef,
@@ -1321,13 +1318,13 @@ pub const Value = extern union {
             },
             .int_type => {
                 const payload = self.cast(Payload.IntType).?;
-                if (payload.signed) {
-                    var new = Type.Payload.IntSigned{ .bits = payload.bits };
-                    return Type.initPayload(&new.base).hash();
-                } else {
-                    var new = Type.Payload.IntUnsigned{ .bits = payload.bits };
-                    return Type.initPayload(&new.base).hash();
-                }
+                var int_payload = Type.Payload.Bits{
+                    .base = .{
+                        .tag = if (payload.signed) .int_signed else .int_unsigned,
+                    },
+                    .data = payload.bits,
+                };
+                return Type.initPayload(&int_payload.base).hash();
             },
 
             .empty_struct_value,
src/zir.zig
@@ -2785,7 +2785,7 @@ const EmitZIR = struct {
                     }
                 },
                 .Optional => {
-                    var buf: Type.Payload.PointerSimple = undefined;
+                    var buf: Type.Payload.ElemType = undefined;
                     const inst = try self.arena.allocator.create(Inst.UnOp);
                     inst.* = .{
                         .base = .{
src/zir_sema.zig
@@ -480,14 +480,11 @@ fn analyzeInstStr(mod: *Module, scope: *Scope, str_inst: *zir.Inst.Str) InnerErr
     errdefer new_decl_arena.deinit();
     const arena_bytes = try new_decl_arena.allocator.dupe(u8, str_inst.positionals.bytes);
 
-    const ty_payload = try scope.arena().create(Type.Payload.Array_u8_Sentinel0);
-    ty_payload.* = .{ .len = arena_bytes.len };
-
     const bytes_payload = try scope.arena().create(Value.Payload.Bytes);
     bytes_payload.* = .{ .data = arena_bytes };
 
     const new_decl = try mod.createAnonymousDecl(scope, &new_decl_arena, .{
-        .ty = Type.initPayload(&ty_payload.base),
+        .ty = try Type.Tag.array_u8_sentinel_0.create(scope.arena(), arena_bytes.len),
         .val = Value.initPayload(&bytes_payload.base),
     });
     return mod.analyzeDeclRef(scope, str_inst.base.src, new_decl);
@@ -952,13 +949,12 @@ fn analyzeInstFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) Inne
         param_types[i] = resolved;
     }
 
-    const payload = try arena.create(Type.Payload.Function);
-    payload.* = .{
+    const fn_ty = try Type.Tag.function.create(arena, .{
         .cc = fntype.kw_args.cc,
         .return_type = return_type,
         .param_types = param_types,
-    };
-    return mod.constType(scope, fntype.base.src, Type.initPayload(&payload.base));
+    });
+    return mod.constType(scope, fntype.base.src, fn_ty);
 }
 
 fn analyzeInstPrimitive(mod: *Module, scope: *Scope, primitive: *zir.Inst.Primitive) InnerError!*Inst {
@@ -1062,11 +1058,10 @@ fn analyzeInstFieldPtr(mod: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr
                     const ref_payload = try scope.arena().create(Value.Payload.RefVal);
                     ref_payload.* = .{ .val = Value.initPayload(&error_payload.base) };
 
-                    const result_type = if (child_type.tag() == .anyerror) blk: {
-                        const result_payload = try scope.arena().create(Type.Payload.ErrorSetSingle);
-                        result_payload.* = .{ .name = entry.key };
-                        break :blk Type.initPayload(&result_payload.base);
-                    } else child_type;
+                    const result_type = if (child_type.tag() == .anyerror)
+                        try Type.Tag.error_set_single.create(scope.arena(), entry.key)
+                    else
+                        child_type;
 
                     return mod.constInst(scope, fieldptr.base.src, .{
                         .ty = try mod.simplePtrType(scope, fieldptr.base.src, result_type, false, .One),
@@ -1195,15 +1190,10 @@ fn analyzeInstElemPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) Inne
                 // @intCast here because it would have been impossible to construct a value that
                 // required a larger index.
                 const elem_ptr = try array_ptr_val.elemPtr(scope.arena(), @intCast(usize, index_u64));
-
-                const type_payload = try scope.arena().create(Type.Payload.PointerSimple);
-                type_payload.* = .{
-                    .base = .{ .tag = .single_const_pointer },
-                    .pointee_type = elem_ty.elemType().elemType(),
-                };
+                const pointee_type = elem_ty.elemType().elemType();
 
                 return mod.constInst(scope, inst.base.src, .{
-                    .ty = Type.initPayload(&type_payload.base),
+                    .ty = try Type.Tag.single_const_pointer.create(scope.arena(), pointee_type),
                     .val = elem_ptr,
                 });
             }