Commit 115c089562

Jacob Young <jacobly0@users.noreply.github.com>
2023-05-20 15:35:11
Value: add `intern` and `unintern` to facilitate code conversion
This allows some code (like struct initializers) to use interned types while other code (such as comptime mutation) continues to use legacy types. With these changes, an `zig build-obj empty.zig` gets to a crash on missing interned error union types.
1 parent be78a12
src/codegen/c.zig
@@ -566,7 +566,7 @@ pub const DeclGen = struct {
                 try writer.writeAll("){ .ptr = ");
             }
 
-            try dg.renderValue(writer, ty.slicePtrFieldType(mod), val.slicePtr(), .Initializer);
+            try dg.renderValue(writer, ty.slicePtrFieldType(mod), val.slicePtr(mod), .Initializer);
 
             const len_val = try mod.intValue(Type.usize, val.sliceLen(mod));
 
src/codegen/llvm.zig
@@ -3363,125 +3363,223 @@ pub const DeclGen = struct {
                 },
                 else => switch (mod.intern_pool.indexToKey(tv.val.ip_index)) {
                     .int => |int| return lowerIntAsPtr(dg, int),
+                    .ptr => |ptr| {
+                        const ptr_val = switch (ptr.addr) {
+                            .@"var" => |@"var"| ptr: {
+                                const decl = dg.module.declPtr(@"var".owner_decl);
+                                dg.module.markDeclAlive(decl);
+
+                                const llvm_wanted_addrspace = toLlvmAddressSpace(decl.@"addrspace", target);
+                                const llvm_actual_addrspace = toLlvmGlobalAddressSpace(decl.@"addrspace", target);
+
+                                const val = try dg.resolveGlobalDecl(@"var".owner_decl);
+                                const addrspace_casted_ptr = if (llvm_actual_addrspace != llvm_wanted_addrspace)
+                                    val.constAddrSpaceCast(dg.context.pointerType(llvm_wanted_addrspace))
+                                else
+                                    val;
+                                break :ptr addrspace_casted_ptr;
+                            },
+                            .decl => |decl| try lowerDeclRefValue(dg, tv, decl),
+                            .mut_decl => |mut_decl| try lowerDeclRefValue(dg, tv, mut_decl.decl),
+                            .int => |int| lowerIntAsPtr(dg, mod.intern_pool.indexToKey(int).int),
+                        };
+                        switch (ptr.len) {
+                            .none => return ptr_val,
+                            else => {
+                                const fields: [2]*llvm.Value = .{
+                                    ptr_val,
+                                    try dg.lowerValue(.{ .ty = Type.usize, .val = ptr.len.toValue() }),
+                                };
+                                return dg.context.constStruct(&fields, fields.len, .False);
+                            },
+                        }
+                    },
                     else => unreachable,
                 },
             },
-            .Array => switch (tv.val.tag()) {
-                .bytes => {
-                    const bytes = tv.val.castTag(.bytes).?.data;
-                    return dg.context.constString(
-                        bytes.ptr,
-                        @intCast(c_uint, tv.ty.arrayLenIncludingSentinel(mod)),
-                        .True, // Don't null terminate. Bytes has the sentinel, if any.
-                    );
-                },
-                .str_lit => {
-                    const str_lit = tv.val.castTag(.str_lit).?.data;
-                    const bytes = dg.module.string_literal_bytes.items[str_lit.index..][0..str_lit.len];
-                    if (tv.ty.sentinel(mod)) |sent_val| {
-                        const byte = @intCast(u8, sent_val.toUnsignedInt(mod));
-                        if (byte == 0 and bytes.len > 0) {
+            .Array => switch (tv.val.ip_index) {
+                .none => switch (tv.val.tag()) {
+                    .bytes => {
+                        const bytes = tv.val.castTag(.bytes).?.data;
+                        return dg.context.constString(
+                            bytes.ptr,
+                            @intCast(c_uint, tv.ty.arrayLenIncludingSentinel(mod)),
+                            .True, // Don't null terminate. Bytes has the sentinel, if any.
+                        );
+                    },
+                    .str_lit => {
+                        const str_lit = tv.val.castTag(.str_lit).?.data;
+                        const bytes = dg.module.string_literal_bytes.items[str_lit.index..][0..str_lit.len];
+                        if (tv.ty.sentinel(mod)) |sent_val| {
+                            const byte = @intCast(u8, sent_val.toUnsignedInt(mod));
+                            if (byte == 0 and bytes.len > 0) {
+                                return dg.context.constString(
+                                    bytes.ptr,
+                                    @intCast(c_uint, bytes.len),
+                                    .False, // Yes, null terminate.
+                                );
+                            }
+                            var array = std.ArrayList(u8).init(dg.gpa);
+                            defer array.deinit();
+                            try array.ensureUnusedCapacity(bytes.len + 1);
+                            array.appendSliceAssumeCapacity(bytes);
+                            array.appendAssumeCapacity(byte);
+                            return dg.context.constString(
+                                array.items.ptr,
+                                @intCast(c_uint, array.items.len),
+                                .True, // Don't null terminate.
+                            );
+                        } else {
                             return dg.context.constString(
                                 bytes.ptr,
                                 @intCast(c_uint, bytes.len),
-                                .False, // Yes, null terminate.
+                                .True, // Don't null terminate. `bytes` has the sentinel, if any.
                             );
                         }
-                        var array = std.ArrayList(u8).init(dg.gpa);
-                        defer array.deinit();
-                        try array.ensureUnusedCapacity(bytes.len + 1);
-                        array.appendSliceAssumeCapacity(bytes);
-                        array.appendAssumeCapacity(byte);
-                        return dg.context.constString(
-                            array.items.ptr,
-                            @intCast(c_uint, array.items.len),
-                            .True, // Don't null terminate.
-                        );
-                    } else {
-                        return dg.context.constString(
-                            bytes.ptr,
-                            @intCast(c_uint, bytes.len),
-                            .True, // Don't null terminate. `bytes` has the sentinel, if any.
-                        );
-                    }
-                },
-                .aggregate => {
-                    const elem_vals = tv.val.castTag(.aggregate).?.data;
-                    const elem_ty = tv.ty.childType(mod);
-                    const gpa = dg.gpa;
-                    const len = @intCast(usize, tv.ty.arrayLenIncludingSentinel(mod));
-                    const llvm_elems = try gpa.alloc(*llvm.Value, len);
-                    defer gpa.free(llvm_elems);
-                    var need_unnamed = false;
-                    for (elem_vals[0..len], 0..) |elem_val, i| {
-                        llvm_elems[i] = try dg.lowerValue(.{ .ty = elem_ty, .val = elem_val });
-                        need_unnamed = need_unnamed or dg.isUnnamedType(elem_ty, llvm_elems[i]);
-                    }
-                    if (need_unnamed) {
-                        return dg.context.constStruct(
-                            llvm_elems.ptr,
-                            @intCast(c_uint, llvm_elems.len),
-                            .True,
-                        );
-                    } else {
-                        const llvm_elem_ty = try dg.lowerType(elem_ty);
-                        return llvm_elem_ty.constArray(
-                            llvm_elems.ptr,
-                            @intCast(c_uint, llvm_elems.len),
-                        );
-                    }
-                },
-                .repeated => {
-                    const val = tv.val.castTag(.repeated).?.data;
-                    const elem_ty = tv.ty.childType(mod);
-                    const sentinel = tv.ty.sentinel(mod);
-                    const len = @intCast(usize, tv.ty.arrayLen(mod));
-                    const len_including_sent = len + @boolToInt(sentinel != null);
-                    const gpa = dg.gpa;
-                    const llvm_elems = try gpa.alloc(*llvm.Value, len_including_sent);
-                    defer gpa.free(llvm_elems);
-
-                    var need_unnamed = false;
-                    if (len != 0) {
-                        for (llvm_elems[0..len]) |*elem| {
-                            elem.* = try dg.lowerValue(.{ .ty = elem_ty, .val = val });
+                    },
+                    .aggregate => {
+                        const elem_vals = tv.val.castTag(.aggregate).?.data;
+                        const elem_ty = tv.ty.childType(mod);
+                        const gpa = dg.gpa;
+                        const len = @intCast(usize, tv.ty.arrayLenIncludingSentinel(mod));
+                        const llvm_elems = try gpa.alloc(*llvm.Value, len);
+                        defer gpa.free(llvm_elems);
+                        var need_unnamed = false;
+                        for (elem_vals[0..len], 0..) |elem_val, i| {
+                            llvm_elems[i] = try dg.lowerValue(.{ .ty = elem_ty, .val = elem_val });
+                            need_unnamed = need_unnamed or dg.isUnnamedType(elem_ty, llvm_elems[i]);
                         }
-                        need_unnamed = need_unnamed or dg.isUnnamedType(elem_ty, llvm_elems[0]);
-                    }
+                        if (need_unnamed) {
+                            return dg.context.constStruct(
+                                llvm_elems.ptr,
+                                @intCast(c_uint, llvm_elems.len),
+                                .True,
+                            );
+                        } else {
+                            const llvm_elem_ty = try dg.lowerType(elem_ty);
+                            return llvm_elem_ty.constArray(
+                                llvm_elems.ptr,
+                                @intCast(c_uint, llvm_elems.len),
+                            );
+                        }
+                    },
+                    .repeated => {
+                        const val = tv.val.castTag(.repeated).?.data;
+                        const elem_ty = tv.ty.childType(mod);
+                        const sentinel = tv.ty.sentinel(mod);
+                        const len = @intCast(usize, tv.ty.arrayLen(mod));
+                        const len_including_sent = len + @boolToInt(sentinel != null);
+                        const gpa = dg.gpa;
+                        const llvm_elems = try gpa.alloc(*llvm.Value, len_including_sent);
+                        defer gpa.free(llvm_elems);
 
-                    if (sentinel) |sent| {
-                        llvm_elems[len] = try dg.lowerValue(.{ .ty = elem_ty, .val = sent });
-                        need_unnamed = need_unnamed or dg.isUnnamedType(elem_ty, llvm_elems[len]);
-                    }
+                        var need_unnamed = false;
+                        if (len != 0) {
+                            for (llvm_elems[0..len]) |*elem| {
+                                elem.* = try dg.lowerValue(.{ .ty = elem_ty, .val = val });
+                            }
+                            need_unnamed = need_unnamed or dg.isUnnamedType(elem_ty, llvm_elems[0]);
+                        }
 
-                    if (need_unnamed) {
-                        return dg.context.constStruct(
-                            llvm_elems.ptr,
-                            @intCast(c_uint, llvm_elems.len),
-                            .True,
-                        );
-                    } else {
-                        const llvm_elem_ty = try dg.lowerType(elem_ty);
-                        return llvm_elem_ty.constArray(
-                            llvm_elems.ptr,
-                            @intCast(c_uint, llvm_elems.len),
-                        );
-                    }
+                        if (sentinel) |sent| {
+                            llvm_elems[len] = try dg.lowerValue(.{ .ty = elem_ty, .val = sent });
+                            need_unnamed = need_unnamed or dg.isUnnamedType(elem_ty, llvm_elems[len]);
+                        }
+
+                        if (need_unnamed) {
+                            return dg.context.constStruct(
+                                llvm_elems.ptr,
+                                @intCast(c_uint, llvm_elems.len),
+                                .True,
+                            );
+                        } else {
+                            const llvm_elem_ty = try dg.lowerType(elem_ty);
+                            return llvm_elem_ty.constArray(
+                                llvm_elems.ptr,
+                                @intCast(c_uint, llvm_elems.len),
+                            );
+                        }
+                    },
+                    .empty_array_sentinel => {
+                        const elem_ty = tv.ty.childType(mod);
+                        const sent_val = tv.ty.sentinel(mod).?;
+                        const sentinel = try dg.lowerValue(.{ .ty = elem_ty, .val = sent_val });
+                        const llvm_elems: [1]*llvm.Value = .{sentinel};
+                        const need_unnamed = dg.isUnnamedType(elem_ty, llvm_elems[0]);
+                        if (need_unnamed) {
+                            return dg.context.constStruct(&llvm_elems, llvm_elems.len, .True);
+                        } else {
+                            const llvm_elem_ty = try dg.lowerType(elem_ty);
+                            return llvm_elem_ty.constArray(&llvm_elems, llvm_elems.len);
+                        }
+                    },
+                    else => unreachable,
                 },
-                .empty_array_sentinel => {
-                    const elem_ty = tv.ty.childType(mod);
-                    const sent_val = tv.ty.sentinel(mod).?;
-                    const sentinel = try dg.lowerValue(.{ .ty = elem_ty, .val = sent_val });
-                    const llvm_elems: [1]*llvm.Value = .{sentinel};
-                    const need_unnamed = dg.isUnnamedType(elem_ty, llvm_elems[0]);
-                    if (need_unnamed) {
-                        return dg.context.constStruct(&llvm_elems, llvm_elems.len, .True);
-                    } else {
-                        const llvm_elem_ty = try dg.lowerType(elem_ty);
-                        return llvm_elem_ty.constArray(&llvm_elems, llvm_elems.len);
-                    }
+                else => switch (mod.intern_pool.indexToKey(tv.val.ip_index)) {
+                    .aggregate => |aggregate| switch (aggregate.storage) {
+                        .elems => |elem_vals| {
+                            const elem_ty = tv.ty.childType(mod);
+                            const gpa = dg.gpa;
+                            const llvm_elems = try gpa.alloc(*llvm.Value, elem_vals.len);
+                            defer gpa.free(llvm_elems);
+                            var need_unnamed = false;
+                            for (elem_vals, 0..) |elem_val, i| {
+                                llvm_elems[i] = try dg.lowerValue(.{ .ty = elem_ty, .val = elem_val.toValue() });
+                                need_unnamed = need_unnamed or dg.isUnnamedType(elem_ty, llvm_elems[i]);
+                            }
+                            if (need_unnamed) {
+                                return dg.context.constStruct(
+                                    llvm_elems.ptr,
+                                    @intCast(c_uint, llvm_elems.len),
+                                    .True,
+                                );
+                            } else {
+                                const llvm_elem_ty = try dg.lowerType(elem_ty);
+                                return llvm_elem_ty.constArray(
+                                    llvm_elems.ptr,
+                                    @intCast(c_uint, llvm_elems.len),
+                                );
+                            }
+                        },
+                        .repeated_elem => |val| {
+                            const elem_ty = tv.ty.childType(mod);
+                            const sentinel = tv.ty.sentinel(mod);
+                            const len = @intCast(usize, tv.ty.arrayLen(mod));
+                            const len_including_sent = len + @boolToInt(sentinel != null);
+                            const gpa = dg.gpa;
+                            const llvm_elems = try gpa.alloc(*llvm.Value, len_including_sent);
+                            defer gpa.free(llvm_elems);
+
+                            var need_unnamed = false;
+                            if (len != 0) {
+                                for (llvm_elems[0..len]) |*elem| {
+                                    elem.* = try dg.lowerValue(.{ .ty = elem_ty, .val = val.toValue() });
+                                }
+                                need_unnamed = need_unnamed or dg.isUnnamedType(elem_ty, llvm_elems[0]);
+                            }
+
+                            if (sentinel) |sent| {
+                                llvm_elems[len] = try dg.lowerValue(.{ .ty = elem_ty, .val = sent });
+                                need_unnamed = need_unnamed or dg.isUnnamedType(elem_ty, llvm_elems[len]);
+                            }
+
+                            if (need_unnamed) {
+                                return dg.context.constStruct(
+                                    llvm_elems.ptr,
+                                    @intCast(c_uint, llvm_elems.len),
+                                    .True,
+                                );
+                            } else {
+                                const llvm_elem_ty = try dg.lowerType(elem_ty);
+                                return llvm_elem_ty.constArray(
+                                    llvm_elems.ptr,
+                                    @intCast(c_uint, llvm_elems.len),
+                                );
+                            }
+                        },
+                    },
+                    else => unreachable,
                 },
-                else => unreachable,
             },
             .Optional => {
                 comptime assert(optional_layout_version == 3);
@@ -3494,15 +3592,22 @@ pub const DeclGen = struct {
                     return non_null_bit;
                 }
                 const llvm_ty = try dg.lowerType(tv.ty);
-                if (tv.ty.optionalReprIsPayload(mod)) {
-                    if (tv.val.castTag(.opt_payload)) |payload| {
-                        return dg.lowerValue(.{ .ty = payload_ty, .val = payload.data });
-                    } else if (is_pl) {
-                        return dg.lowerValue(.{ .ty = payload_ty, .val = tv.val });
-                    } else {
-                        return llvm_ty.constNull();
-                    }
-                }
+                if (tv.ty.optionalReprIsPayload(mod)) return switch (tv.val.ip_index) {
+                    .none => if (tv.val.castTag(.opt_payload)) |payload|
+                        try dg.lowerValue(.{ .ty = payload_ty, .val = payload.data })
+                    else if (is_pl)
+                        try dg.lowerValue(.{ .ty = payload_ty, .val = tv.val })
+                    else
+                        llvm_ty.constNull(),
+                    .null_value => llvm_ty.constNull(),
+                    else => switch (mod.intern_pool.indexToKey(tv.val.ip_index)) {
+                        .opt => |opt| switch (opt.val) {
+                            .none => llvm_ty.constNull(),
+                            else => dg.lowerValue(.{ .ty = payload_ty, .val = opt.val.toValue() }),
+                        },
+                        else => unreachable,
+                    },
+                };
                 assert(payload_ty.zigTypeTag(mod) != .Fn);
 
                 const llvm_field_count = llvm_ty.countStructElementTypes();
@@ -3589,7 +3694,6 @@ pub const DeclGen = struct {
             },
             .Struct => {
                 const llvm_struct_ty = try dg.lowerType(tv.ty);
-                const field_vals = tv.val.castTag(.aggregate).?.data;
                 const gpa = dg.gpa;
 
                 const struct_type = switch (mod.intern_pool.indexToKey(tv.ty.ip_index)) {
@@ -3623,7 +3727,7 @@ pub const DeclGen = struct {
 
                             const field_llvm_val = try dg.lowerValue(.{
                                 .ty = field_ty.toType(),
-                                .val = field_vals[i],
+                                .val = try tv.val.fieldValue(field_ty.toType(), mod, i),
                             });
 
                             need_unnamed = need_unnamed or dg.isUnnamedType(field_ty.toType(), field_llvm_val);
@@ -3669,13 +3773,12 @@ pub const DeclGen = struct {
                     comptime assert(Type.packed_struct_layout_version == 2);
                     var running_int: *llvm.Value = int_llvm_ty.constNull();
                     var running_bits: u16 = 0;
-                    for (field_vals, 0..) |field_val, i| {
-                        const field = fields[i];
+                    for (fields, 0..) |field, i| {
                         if (!field.ty.hasRuntimeBitsIgnoreComptime(mod)) continue;
 
                         const non_int_val = try dg.lowerValue(.{
                             .ty = field.ty,
-                            .val = field_val,
+                            .val = try tv.val.fieldValue(field.ty, mod, i),
                         });
                         const ty_bit_size = @intCast(u16, field.ty.bitSize(mod));
                         const small_int_ty = dg.context.intType(ty_bit_size);
@@ -3722,7 +3825,7 @@ pub const DeclGen = struct {
 
                     const field_llvm_val = try dg.lowerValue(.{
                         .ty = field.ty,
-                        .val = field_vals[field_and_index.index],
+                        .val = try tv.val.fieldValue(field.ty, mod, field_and_index.index),
                     });
 
                     need_unnamed = need_unnamed or dg.isUnnamedType(field.ty, field_llvm_val);
@@ -3756,7 +3859,13 @@ pub const DeclGen = struct {
             },
             .Union => {
                 const llvm_union_ty = try dg.lowerType(tv.ty);
-                const tag_and_val = tv.val.castTag(.@"union").?.data;
+                const tag_and_val: Value.Payload.Union.Data = switch (tv.val.ip_index) {
+                    .none => tv.val.castTag(.@"union").?.data,
+                    else => switch (mod.intern_pool.indexToKey(tv.val.ip_index)) {
+                        .un => |un| .{ .tag = un.tag.toValue(), .val = un.val.toValue() },
+                        else => unreachable,
+                    },
+                };
 
                 const layout = tv.ty.unionGetLayout(mod);
 
src/InternPool.zig
@@ -100,6 +100,16 @@ pub const MapIndex = enum(u32) {
     }
 };
 
+pub const RuntimeIndex = enum(u32) {
+    zero = 0,
+    comptime_field_ptr = std.math.maxInt(u32),
+    _,
+
+    pub fn increment(ri: *RuntimeIndex) void {
+        ri.* = @intToEnum(RuntimeIndex, @enumToInt(ri.*) + 1);
+    }
+};
+
 /// An index into `string_bytes`.
 pub const NullTerminatedString = enum(u32) {
     _,
@@ -478,11 +488,27 @@ pub const Key = union(enum) {
     };
 
     pub const Ptr = struct {
+        /// This is the pointer type, not the element type.
         ty: Index,
+        /// The value of the address that the pointer points to.
         addr: Addr,
+        /// This could be `none` if size is not a slice.
+        len: Index = .none,
 
         pub const Addr = union(enum) {
+            @"var": struct {
+                init: Index,
+                owner_decl: Module.Decl.Index,
+                lib_name: OptionalNullTerminatedString,
+                is_const: bool,
+                is_threadlocal: bool,
+                is_weak_linkage: bool,
+            },
             decl: Module.Decl.Index,
+            mut_decl: struct {
+                decl: Module.Decl.Index,
+                runtime_index: RuntimeIndex,
+            },
             int: Index,
         };
     };
@@ -577,7 +603,9 @@ pub const Key = union(enum) {
                 // This is sound due to pointer provenance rules.
                 std.hash.autoHash(hasher, @as(@typeInfo(Key.Ptr.Addr).Union.tag_type.?, ptr.addr));
                 switch (ptr.addr) {
+                    .@"var" => |@"var"| std.hash.autoHash(hasher, @"var".owner_decl),
                     .decl => |decl| std.hash.autoHash(hasher, decl),
+                    .mut_decl => |mut_decl| std.hash.autoHash(hasher, mut_decl),
                     .int => |int| std.hash.autoHash(hasher, int),
                 }
             },
@@ -697,7 +725,9 @@ pub const Key = union(enum) {
                 if (@as(AddrTag, a_info.addr) != @as(AddrTag, b_info.addr)) return false;
 
                 return switch (a_info.addr) {
+                    .@"var" => |a_var| a_var.owner_decl == b_info.addr.@"var".owner_decl,
                     .decl => |a_decl| a_decl == b_info.addr.decl,
+                    .mut_decl => |a_mut_decl| std.meta.eql(a_mut_decl, b_info.addr.mut_decl),
                     .int => |a_int| a_int == b_info.addr.int,
                 };
             },
@@ -1330,6 +1360,12 @@ pub const Tag = enum(u8) {
     /// A value that can be represented with only an enum tag.
     /// data is SimpleValue enum value.
     simple_value,
+    /// A pointer to a var.
+    /// data is extra index of PtrVal, which contains the type and address.
+    ptr_var,
+    /// A pointer to a decl that can be mutated at comptime.
+    /// data is extra index of PtrMutDecl, which contains the type and address.
+    ptr_mut_decl,
     /// A pointer to a decl.
     /// data is extra index of PtrDecl, which contains the type and address.
     ptr_decl,
@@ -1338,6 +1374,11 @@ pub const Tag = enum(u8) {
     /// Only pointer types are allowed to have this encoding. Optional types must use
     /// `opt_payload` or `opt_null`.
     ptr_int,
+    /// A slice.
+    /// data is extra index of PtrSlice, which contains the ptr and len values
+    /// In order to use this encoding, one must ensure that the `InternPool`
+    /// already contains the slice type corresponding to this payload.
+    ptr_slice,
     /// An optional value that is non-null.
     /// data is Index of the payload value.
     /// In order to use this encoding, one must ensure that the `InternPool`
@@ -1672,16 +1713,45 @@ pub const PackedU64 = packed struct(u64) {
     }
 };
 
+pub const PtrVar = struct {
+    ty: Index,
+    /// If flags.is_extern == true this is `none`.
+    init: Index,
+    owner_decl: Module.Decl.Index,
+    /// Library name if specified.
+    /// For example `extern "c" var stderrp = ...` would have 'c' as library name.
+    lib_name: OptionalNullTerminatedString,
+    flags: Flags,
+
+    pub const Flags = packed struct(u32) {
+        is_const: bool,
+        is_threadlocal: bool,
+        is_weak_linkage: bool,
+        unused: u29 = undefined,
+    };
+};
+
 pub const PtrDecl = struct {
     ty: Index,
     decl: Module.Decl.Index,
 };
 
+pub const PtrMutDecl = struct {
+    ty: Index,
+    decl: Module.Decl.Index,
+    runtime_index: RuntimeIndex,
+};
+
 pub const PtrInt = struct {
     ty: Index,
     addr: Index,
 };
 
+pub const PtrSlice = struct {
+    ptr: Index,
+    len: Index,
+};
+
 /// Trailing: Limb for every limbs_len
 pub const Int = struct {
     ty: Index,
@@ -1994,6 +2064,30 @@ pub fn indexToKey(ip: InternPool, index: Index) Key {
                 .val = payload_val,
             } };
         },
+        .ptr_var => {
+            const info = ip.extraData(PtrVar, data);
+            return .{ .ptr = .{
+                .ty = info.ty,
+                .addr = .{ .@"var" = .{
+                    .init = info.init,
+                    .owner_decl = info.owner_decl,
+                    .lib_name = info.lib_name,
+                    .is_const = info.flags.is_const,
+                    .is_threadlocal = info.flags.is_threadlocal,
+                    .is_weak_linkage = info.flags.is_weak_linkage,
+                } },
+            } };
+        },
+        .ptr_mut_decl => {
+            const info = ip.extraData(PtrMutDecl, data);
+            return .{ .ptr = .{
+                .ty = info.ty,
+                .addr = .{ .mut_decl = .{
+                    .decl = info.decl,
+                    .runtime_index = info.runtime_index,
+                } },
+            } };
+        },
         .ptr_decl => {
             const info = ip.extraData(PtrDecl, data);
             return .{ .ptr = .{
@@ -2008,6 +2102,18 @@ pub fn indexToKey(ip: InternPool, index: Index) Key {
                 .addr = .{ .int = info.addr },
             } };
         },
+        .ptr_slice => {
+            const info = ip.extraData(PtrSlice, data);
+            const ptr = ip.indexToKey(info.ptr).ptr;
+            var ptr_ty = ip.indexToKey(ptr.ty);
+            assert(ptr_ty.ptr_type.size == .Many);
+            ptr_ty.ptr_type.size = .Slice;
+            return .{ .ptr = .{
+                .ty = ip.getAssumeExists(ptr_ty),
+                .addr = ptr.addr,
+                .len = info.len,
+            } };
+        },
         .int_u8 => .{ .int = .{
             .ty = .u8_type,
             .storage = .{ .u64 = data },
@@ -2472,31 +2578,67 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
 
         .extern_func => @panic("TODO"),
 
-        .ptr => |ptr| switch (ptr.addr) {
-            .decl => |decl| {
-                assert(ptr.ty != .none);
-                ip.items.appendAssumeCapacity(.{
-                    .tag = .ptr_decl,
-                    .data = try ip.addExtra(gpa, PtrDecl{
-                        .ty = ptr.ty,
-                        .decl = decl,
+        .ptr => |ptr| switch (ip.items.items(.tag)[@enumToInt(ptr.ty)]) {
+            .type_pointer => {
+                assert(ptr.len == .none);
+                switch (ptr.addr) {
+                    .@"var" => |@"var"| ip.items.appendAssumeCapacity(.{
+                        .tag = .ptr_var,
+                        .data = try ip.addExtra(gpa, PtrVar{
+                            .ty = ptr.ty,
+                            .init = @"var".init,
+                            .owner_decl = @"var".owner_decl,
+                            .lib_name = @"var".lib_name,
+                            .flags = .{
+                                .is_const = @"var".is_const,
+                                .is_threadlocal = @"var".is_threadlocal,
+                                .is_weak_linkage = @"var".is_weak_linkage,
+                            },
+                        }),
                     }),
-                });
+                    .decl => |decl| ip.items.appendAssumeCapacity(.{
+                        .tag = .ptr_decl,
+                        .data = try ip.addExtra(gpa, PtrDecl{
+                            .ty = ptr.ty,
+                            .decl = decl,
+                        }),
+                    }),
+                    .mut_decl => |mut_decl| ip.items.appendAssumeCapacity(.{
+                        .tag = .ptr_mut_decl,
+                        .data = try ip.addExtra(gpa, PtrMutDecl{
+                            .ty = ptr.ty,
+                            .decl = mut_decl.decl,
+                            .runtime_index = mut_decl.runtime_index,
+                        }),
+                    }),
+                    .int => |int| ip.items.appendAssumeCapacity(.{
+                        .tag = .ptr_int,
+                        .data = try ip.addExtra(gpa, PtrInt{
+                            .ty = ptr.ty,
+                            .addr = int,
+                        }),
+                    }),
+                }
             },
-            .int => |int| {
-                assert(ptr.ty != .none);
+            .type_slice => {
+                assert(ptr.len != .none);
+                var new_key = key;
+                new_key.ptr.ty = @intToEnum(Index, ip.items.items(.data)[@enumToInt(ptr.ty)]);
+                new_key.ptr.len = .none;
+                const ptr_index = try get(ip, gpa, new_key);
+                try ip.items.ensureUnusedCapacity(gpa, 1);
                 ip.items.appendAssumeCapacity(.{
-                    .tag = .ptr_int,
-                    .data = try ip.addExtra(gpa, PtrInt{
-                        .ty = ptr.ty,
-                        .addr = int,
+                    .tag = .ptr_slice,
+                    .data = try ip.addExtra(gpa, PtrSlice{
+                        .ptr = ptr_index,
+                        .len = ptr.len,
                     }),
                 });
             },
+            else => unreachable,
         },
 
         .opt => |opt| {
-            assert(opt.ty != .none);
             assert(ip.isOptionalType(opt.ty));
             ip.items.appendAssumeCapacity(if (opt.val == .none) .{
                 .tag = .opt_null,
@@ -3087,11 +3229,15 @@ fn addExtraAssumeCapacity(ip: *InternPool, extra: anytype) u32 {
             Module.Namespace.OptionalIndex => @enumToInt(@field(extra, field.name)),
             MapIndex => @enumToInt(@field(extra, field.name)),
             OptionalMapIndex => @enumToInt(@field(extra, field.name)),
+            RuntimeIndex => @enumToInt(@field(extra, field.name)),
+            NullTerminatedString => @enumToInt(@field(extra, field.name)),
+            OptionalNullTerminatedString => @enumToInt(@field(extra, field.name)),
             i32 => @bitCast(u32, @field(extra, field.name)),
             Pointer.Flags => @bitCast(u32, @field(extra, field.name)),
             TypeFunction.Flags => @bitCast(u32, @field(extra, field.name)),
             Pointer.PackedOffset => @bitCast(u32, @field(extra, field.name)),
             Pointer.VectorIndex => @enumToInt(@field(extra, field.name)),
+            PtrVar.Flags => @bitCast(u32, @field(extra, field.name)),
             else => @compileError("bad field type: " ++ @typeName(field.type)),
         });
     }
@@ -3149,11 +3295,15 @@ fn extraDataTrail(ip: InternPool, comptime T: type, index: usize) struct { data:
             Module.Namespace.OptionalIndex => @intToEnum(Module.Namespace.OptionalIndex, int32),
             MapIndex => @intToEnum(MapIndex, int32),
             OptionalMapIndex => @intToEnum(OptionalMapIndex, int32),
+            RuntimeIndex => @intToEnum(RuntimeIndex, int32),
+            NullTerminatedString => @intToEnum(NullTerminatedString, int32),
+            OptionalNullTerminatedString => @intToEnum(OptionalNullTerminatedString, int32),
             i32 => @bitCast(i32, int32),
             Pointer.Flags => @bitCast(Pointer.Flags, int32),
             TypeFunction.Flags => @bitCast(TypeFunction.Flags, int32),
             Pointer.PackedOffset => @bitCast(Pointer.PackedOffset, int32),
             Pointer.VectorIndex => @intToEnum(Pointer.VectorIndex, int32),
+            PtrVar.Flags => @bitCast(PtrVar.Flags, int32),
             else => @compileError("bad field type: " ++ @typeName(field.type)),
         };
     }
@@ -3274,7 +3424,7 @@ pub fn childType(ip: InternPool, i: Index) Index {
     };
 }
 
-/// Given a slice type, returns the type of the pointer field.
+/// Given a slice type, returns the type of the ptr field.
 pub fn slicePtrType(ip: InternPool, i: Index) Index {
     switch (i) {
         .const_slice_u8_type => return .manyptr_const_u8_type,
@@ -3288,10 +3438,29 @@ pub fn slicePtrType(ip: InternPool, i: Index) Index {
     }
 }
 
+/// Given a slice value, returns the value of the ptr field.
+pub fn slicePtr(ip: InternPool, i: Index) Index {
+    const item = ip.items.get(@enumToInt(i));
+    switch (item.tag) {
+        .ptr_slice => return ip.extraData(PtrSlice, item.data).ptr,
+        else => unreachable, // not a slice value
+    }
+}
+
+/// Given a slice value, returns the value of the len field.
+pub fn sliceLen(ip: InternPool, i: Index) Index {
+    const item = ip.items.get(@enumToInt(i));
+    switch (item.tag) {
+        .ptr_slice => return ip.extraData(PtrSlice, item.data).len,
+        else => unreachable, // not a slice value
+    }
+}
+
 /// Given an existing value, returns the same value but with the supplied type.
 /// Only some combinations are allowed:
 /// * int <=> int
 /// * int <=> enum
+/// * ptr <=> ptr
 pub fn getCoerced(ip: *InternPool, gpa: Allocator, val: Index, new_ty: Index) Allocator.Error!Index {
     switch (ip.indexToKey(val)) {
         .int => |int| switch (ip.indexToKey(new_ty)) {
@@ -3305,6 +3474,13 @@ pub fn getCoerced(ip: *InternPool, gpa: Allocator, val: Index, new_ty: Index) Al
             // Assume new_ty is an integer type.
             return getCoercedInts(ip, gpa, ip.indexToKey(enum_tag.int).int, new_ty);
         },
+        .ptr => |ptr| switch (ip.indexToKey(new_ty)) {
+            .ptr_type => return ip.get(gpa, .{ .ptr = .{
+                .ty = new_ty,
+                .addr = ptr.addr,
+            } }),
+            else => unreachable,
+        },
         else => unreachable,
     }
 }
@@ -3380,6 +3556,15 @@ pub fn indexToInferredErrorSetType(ip: InternPool, val: Index) Module.Fn.Inferre
     return @intToEnum(Module.Fn.InferredErrorSet.Index, datas[@enumToInt(val)]).toOptional();
 }
 
+pub fn isPointerType(ip: InternPool, ty: Index) bool {
+    const tags = ip.items.items(.tag);
+    if (ty == .none) return false;
+    return switch (tags[@enumToInt(ty)]) {
+        .type_pointer, .type_slice => true,
+        else => false,
+    };
+}
+
 pub fn isOptionalType(ip: InternPool, ty: Index) bool {
     const tags = ip.items.items(.tag);
     if (ty == .none) return false;
@@ -3485,8 +3670,11 @@ fn dumpFallible(ip: InternPool, arena: Allocator) anyerror!void {
             .undef => 0,
             .simple_type => 0,
             .simple_value => 0,
+            .ptr_var => @sizeOf(PtrVar),
             .ptr_decl => @sizeOf(PtrDecl),
+            .ptr_mut_decl => @sizeOf(PtrMutDecl),
             .ptr_int => @sizeOf(PtrInt),
+            .ptr_slice => @sizeOf(PtrSlice),
             .opt_null => 0,
             .opt_payload => 0,
             .int_u8 => 0,
src/Module.zig
@@ -5762,7 +5762,7 @@ pub fn analyzeFnBody(mod: *Module, func: *Fn, arena: Allocator) SemaError!Air {
     // Crucially, this happens *after* we set the function state to success above,
     // so that dependencies on the function body will now be satisfied rather than
     // result in circular dependency errors.
-    sema.resolveFnTypes(fn_ty_info) catch |err| switch (err) {
+    sema.resolveFnTypes(mod.typeToFunc(fn_ty).?) catch |err| switch (err) {
         error.NeededSourceLocation => unreachable,
         error.GenericPoison => unreachable,
         error.ComptimeReturn => unreachable,
src/Sema.zig
@@ -13072,25 +13072,32 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
             // as zero-filling a byte array.
             if (lhs_len == 1) {
                 const elem_val = try lhs_sub_val.elemValue(mod, 0);
-                break :v try Value.Tag.repeated.create(sema.arena, elem_val);
+                break :v try mod.intern(.{ .aggregate = .{
+                    .ty = result_ty.ip_index,
+                    .storage = .{ .repeated_elem = elem_val.ip_index },
+                } });
             }
 
-            const element_vals = try sema.arena.alloc(Value, final_len_including_sent);
+            const element_vals = try sema.arena.alloc(InternPool.Index, final_len_including_sent);
             var elem_i: usize = 0;
             while (elem_i < result_len) {
                 var lhs_i: usize = 0;
                 while (lhs_i < lhs_len) : (lhs_i += 1) {
                     const elem_val = try lhs_sub_val.elemValue(mod, lhs_i);
-                    element_vals[elem_i] = elem_val;
+                    assert(elem_val.ip_index != .none);
+                    element_vals[elem_i] = elem_val.ip_index;
                     elem_i += 1;
                 }
             }
             if (lhs_info.sentinel) |sent_val| {
-                element_vals[result_len] = sent_val;
+                element_vals[result_len] = sent_val.ip_index;
             }
-            break :v try Value.Tag.aggregate.create(sema.arena, element_vals);
+            break :v try mod.intern(.{ .aggregate = .{
+                .ty = result_ty.ip_index,
+                .storage = .{ .elems = element_vals },
+            } });
         };
-        return sema.addConstantMaybeRef(block, result_ty, val, ptr_addrspace != null);
+        return sema.addConstantMaybeRef(block, result_ty, val.toValue(), ptr_addrspace != null);
     }
 
     try sema.requireRuntimeBlock(block, src, lhs_src);
@@ -18111,12 +18118,16 @@ fn finishStructInit(
     } else null;
 
     const runtime_index = opt_runtime_index orelse {
-        const values = try sema.arena.alloc(Value, field_inits.len);
-        for (field_inits, 0..) |field_init, i| {
-            values[i] = (sema.resolveMaybeUndefVal(field_init) catch unreachable).?;
-        }
-        const struct_val = try Value.Tag.aggregate.create(sema.arena, values);
-        return sema.addConstantMaybeRef(block, struct_ty, struct_val, is_ref);
+        const elems = try sema.arena.alloc(InternPool.Index, field_inits.len);
+        for (elems, field_inits, 0..) |*elem, field_init, field_i| {
+            elem.* = try (sema.resolveMaybeUndefVal(field_init) catch unreachable).?
+                .intern(struct_ty.structFieldType(field_i, mod), mod);
+        }
+        const struct_val = try mod.intern(.{ .aggregate = .{
+            .ty = struct_ty.ip_index,
+            .storage = .{ .elems = elems },
+        } });
+        return sema.addConstantMaybeRef(block, struct_ty, struct_val.toValue(), is_ref);
     };
 
     if (is_ref) {
@@ -18195,21 +18206,20 @@ fn zirStructInitAnon(
 
             const init = try sema.resolveInst(item.data.init);
             field_ty.* = sema.typeOf(init).ip_index;
-            if (types[i].toType().zigTypeTag(mod) == .Opaque) {
+            if (field_ty.toType().zigTypeTag(mod) == .Opaque) {
                 const msg = msg: {
                     const decl = sema.mod.declPtr(block.src_decl);
                     const field_src = mod.initSrc(src.node_offset.x, decl, i);
                     const msg = try sema.errMsg(block, field_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{});
                     errdefer msg.destroy(sema.gpa);
 
-                    try sema.addDeclaredHereNote(msg, types[i].toType());
+                    try sema.addDeclaredHereNote(msg, field_ty.toType());
                     break :msg msg;
                 };
                 return sema.failWithOwnedErrorMsg(msg);
             }
             if (try sema.resolveMaybeUndefVal(init)) |init_val| {
-                assert(init_val.ip_index != .none);
-                values[i] = init_val.ip_index;
+                values[i] = try init_val.intern(field_ty.toType(), mod);
             } else {
                 values[i] = .none;
                 runtime_index = i;
@@ -24891,9 +24901,7 @@ fn structFieldVal(
                 if ((try sema.typeHasOnePossibleValue(field.ty))) |opv| {
                     return sema.addConstant(field.ty, opv);
                 }
-
-                const field_values = struct_val.castTag(.aggregate).?.data;
-                return sema.addConstant(field.ty, field_values[field_index]);
+                return sema.addConstant(field.ty, try struct_val.fieldValue(field.ty, mod, field_index));
             }
 
             try sema.requireRuntimeBlock(block, src, null);
@@ -27925,7 +27933,24 @@ fn beginComptimePtrMutation(
                             ptr_elem_ty,
                             parent.decl_ref_mut,
                         ),
+                        .repeated => {
+                            const arena = parent.beginArena(sema.mod);
+                            defer parent.finishArena(sema.mod);
+
+                            const elems = try arena.alloc(Value, parent.ty.structFieldCount(mod));
+                            @memset(elems, val_ptr.castTag(.repeated).?.data);
+                            val_ptr.* = try Value.Tag.aggregate.create(arena, elems);
 
+                            return beginComptimePtrMutationInner(
+                                sema,
+                                block,
+                                src,
+                                parent.ty.structFieldType(field_index, mod),
+                                &elems[field_index],
+                                ptr_elem_ty,
+                                parent.decl_ref_mut,
+                            );
+                        },
                         .@"union" => {
                             // We need to set the active field of the union.
                             const union_tag_ty = field_ptr.container_ty.unionTagTypeHypothetical(mod);
@@ -28107,6 +28132,13 @@ fn beginComptimePtrMutationInner(
     const mod = sema.mod;
     const target = mod.getTarget();
     const coerce_ok = (try sema.coerceInMemoryAllowed(block, ptr_elem_ty, decl_ty, true, target, src, src)) == .ok;
+
+    const decl = mod.declPtr(decl_ref_mut.decl_index);
+    var decl_arena: std.heap.ArenaAllocator = undefined;
+    const allocator = decl.value_arena.?.acquire(mod.gpa, &decl_arena);
+    defer decl.value_arena.?.release(&decl_arena);
+    decl_val.* = try decl_val.unintern(allocator, mod);
+
     if (coerce_ok) {
         return ComptimePtrMutationKit{
             .decl_ref_mut = decl_ref_mut,
@@ -28412,6 +28444,27 @@ fn beginComptimePtrLoad(
         },
         else => switch (mod.intern_pool.indexToKey(ptr_val.ip_index)) {
             .int => return error.RuntimeLoad,
+            .ptr => |ptr| switch (ptr.addr) {
+                .@"var", .int => return error.RuntimeLoad,
+                .decl, .mut_decl => blk: {
+                    const decl_index = switch (ptr.addr) {
+                        .decl => |decl| decl,
+                        .mut_decl => |mut_decl| mut_decl.decl,
+                        else => unreachable,
+                    };
+                    const decl = mod.declPtr(decl_index);
+                    const decl_tv = try decl.typedValue();
+                    if (decl_tv.val.tagIsVariable()) return error.RuntimeLoad;
+
+                    const layout_defined = decl.ty.hasWellDefinedLayout(mod);
+                    break :blk ComptimePtrLoadKit{
+                        .parent = if (layout_defined) .{ .tv = decl_tv, .byte_offset = 0 } else null,
+                        .pointee = decl_tv,
+                        .is_mutable = false,
+                        .ty_without_well_defined_layout = if (!layout_defined) decl.ty else null,
+                    };
+                },
+            },
             else => unreachable,
         },
     };
@@ -29425,7 +29478,7 @@ fn analyzeSlicePtr(
     const result_ty = slice_ty.slicePtrFieldType(mod);
     if (try sema.resolveMaybeUndefVal(slice)) |val| {
         if (val.isUndef(mod)) return sema.addConstUndef(result_ty);
-        return sema.addConstant(result_ty, val.slicePtr());
+        return sema.addConstant(result_ty, val.slicePtr(mod));
     }
     try sema.requireRuntimeBlock(block, slice_src, null);
     return block.addTyOp(.slice_ptr, result_ty, slice);
src/value.zig
@@ -602,6 +602,73 @@ pub const Value = struct {
         return result;
     }
 
+    pub fn intern(val: Value, ty: Type, mod: *Module) Allocator.Error!InternPool.Index {
+        if (val.ip_index != .none) return val.ip_index;
+        switch (val.tag()) {
+            .slice => {
+                const pl = val.castTag(.slice).?.data;
+                const ptr = try pl.ptr.intern(ty.slicePtrFieldType(mod), mod);
+                return mod.intern(.{ .ptr = .{
+                    .ty = ty.ip_index,
+                    .addr = mod.intern_pool.indexToKey(ptr).ptr.addr,
+                    .len = try pl.len.intern(Type.usize, mod),
+                } });
+            },
+            .opt_payload => return mod.intern(.{ .opt = .{
+                .ty = ty.ip_index,
+                .val = try val.castTag(.opt_payload).?.data.intern(ty.childType(mod), mod),
+            } }),
+            .aggregate => {
+                const old_elems = val.castTag(.aggregate).?.data;
+                const new_elems = try mod.gpa.alloc(InternPool.Index, old_elems.len);
+                defer mod.gpa.free(new_elems);
+                const ty_key = mod.intern_pool.indexToKey(ty.ip_index);
+                for (new_elems, old_elems, 0..) |*new_elem, old_elem, field_i|
+                    new_elem.* = try old_elem.intern(switch (ty_key) {
+                        .struct_type => ty.structFieldType(field_i, mod),
+                        .anon_struct_type => |info| info.types[field_i].toType(),
+                        inline .array_type, .vector_type => |info| info.child.toType(),
+                        else => unreachable,
+                    }, mod);
+                return mod.intern(.{ .aggregate = .{
+                    .ty = ty.ip_index,
+                    .storage = .{ .elems = new_elems },
+                } });
+            },
+            .repeated => return mod.intern(.{ .aggregate = .{
+                .ty = ty.ip_index,
+                .storage = .{ .repeated_elem = try val.castTag(.repeated).?.data.intern(
+                    ty.structFieldType(0, mod),
+                    mod,
+                ) },
+            } }),
+            .@"union" => {
+                const pl = val.castTag(.@"union").?.data;
+                return mod.intern(.{ .un = .{
+                    .ty = ty.ip_index,
+                    .tag = try pl.tag.intern(ty.unionTagTypeHypothetical(mod), mod),
+                    .val = try pl.val.intern(ty.unionFieldType(pl.tag, mod), mod),
+                } });
+            },
+            else => unreachable,
+        }
+    }
+
+    pub fn unintern(val: Value, arena: Allocator, mod: *Module) Allocator.Error!Value {
+        if (val.ip_index == .none) return val;
+        switch (mod.intern_pool.indexToKey(val.ip_index)) {
+            .aggregate => |aggregate| switch (aggregate.storage) {
+                .elems => |old_elems| {
+                    const new_elems = try arena.alloc(Value, old_elems.len);
+                    for (new_elems, old_elems) |*new_elem, old_elem| new_elem.* = old_elem.toValue();
+                    return Tag.aggregate.create(arena, new_elems);
+                },
+                .repeated_elem => |elem| return Tag.repeated.create(arena, elem.toValue()),
+            },
+            else => return val,
+        }
+    }
+
     pub fn toIntern(val: Value) InternPool.Index {
         assert(val.ip_index != .none);
         return val.ip_index;
@@ -2002,11 +2069,11 @@ pub const Value = struct {
 
                     const ptr_ty = ty.slicePtrFieldType(mod);
                     const a_ptr = switch (a_ty.ptrSize(mod)) {
-                        .Slice => a.slicePtr(),
+                        .Slice => a.slicePtr(mod),
                         .One => a,
                         else => unreachable,
                     };
-                    return try eqlAdvanced(a_ptr, ptr_ty, b.slicePtr(), ptr_ty, mod, opt_sema);
+                    return try eqlAdvanced(a_ptr, ptr_ty, b.slicePtr(mod), ptr_ty, mod, opt_sema);
                 },
                 .Many, .C, .One => {},
             },
@@ -2429,7 +2496,8 @@ pub const Value = struct {
         }
     }
 
-    pub fn slicePtr(val: Value) Value {
+    pub fn slicePtr(val: Value, mod: *Module) Value {
+        if (val.ip_index != .none) return mod.intern_pool.slicePtr(val.ip_index).toValue();
         return switch (val.tag()) {
             .slice => val.castTag(.slice).?.data.ptr,
             // TODO this should require being a slice tag, and not allow decl_ref, field_ptr, etc.
@@ -2439,6 +2507,7 @@ pub const Value = struct {
     }
 
     pub fn sliceLen(val: Value, mod: *Module) u64 {
+        if (val.ip_index != .none) return mod.intern_pool.sliceLen(val.ip_index).toValue().toUnsignedInt(mod);
         return switch (val.tag()) {
             .slice => val.castTag(.slice).?.data.len.toUnsignedInt(mod),
             .decl_ref => {
@@ -2531,7 +2600,19 @@ pub const Value = struct {
 
                 else => unreachable,
             },
-            else => unreachable,
+            else => return switch (mod.intern_pool.indexToKey(val.ip_index)) {
+                .ptr => |ptr| switch (ptr.addr) {
+                    .@"var" => unreachable,
+                    .decl => |decl| mod.declPtr(decl).val.elemValue(mod, index),
+                    .mut_decl => |mut_decl| mod.declPtr(mut_decl.decl).val.elemValue(mod, index),
+                    .int => unreachable,
+                },
+                .aggregate => |aggregate| switch (aggregate.storage) {
+                    .elems => |elems| elems[index].toValue(),
+                    .repeated_elem => |elem| elem.toValue(),
+                },
+                else => unreachable,
+            },
         }
     }
 
@@ -2675,6 +2756,7 @@ pub const Value = struct {
     }
 
     pub fn unionTag(val: Value, mod: *Module) Value {
+        if (val.ip_index == .none) return val.castTag(.@"union").?.data.tag;
         return switch (mod.intern_pool.indexToKey(val.ip_index)) {
             .undef, .enum_tag => val,
             .un => |un| un.tag.toValue(),
@@ -2696,7 +2778,7 @@ pub const Value = struct {
             else => val,
         };
 
-        if (ptr_val.tag() == .elem_ptr) {
+        if (ptr_val.ip_index == .none and ptr_val.tag() == .elem_ptr) {
             const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;
             if (elem_ptr.elem_ty.eql(elem_ty, mod)) {
                 return Tag.elem_ptr.create(arena, .{
@@ -4809,10 +4891,12 @@ pub const Value = struct {
             pub const base_tag = Tag.@"union";
 
             base: Payload = .{ .tag = base_tag },
-            data: struct {
+            data: Data,
+
+            pub const Data = struct {
                 tag: Value,
                 val: Value,
-            },
+            };
         };
     };
 
@@ -4844,15 +4928,7 @@ pub const Value = struct {
         return if (x) one else zero;
     }
 
-    pub const RuntimeIndex = enum(u32) {
-        zero = 0,
-        comptime_field_ptr = std.math.maxInt(u32),
-        _,
-
-        pub fn increment(ri: *RuntimeIndex) void {
-            ri.* = @intToEnum(RuntimeIndex, @enumToInt(ri.*) + 1);
-        }
-    };
+    pub const RuntimeIndex = InternPool.RuntimeIndex;
 
     /// 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.