Commit 2d5bc01469

Jacob Young <jacobly0@users.noreply.github.com>
2023-05-27 03:22:34
behavior: get more test cases passing with llvm
1 parent c8b0d4d
src/codegen/llvm.zig
@@ -2003,7 +2003,7 @@ pub const Object = struct {
                                 mod.intern_pool.stringToSlice(tuple.names[i])
                             else
                                 try std.fmt.allocPrintZ(gpa, "{d}", .{i});
-                            defer gpa.free(field_name);
+                            defer if (tuple.names.len == 0) gpa.free(field_name);
 
                             try di_fields.append(gpa, dib.createMemberType(
                                 fwd_decl.toScope(),
@@ -2461,13 +2461,13 @@ pub const DeclGen = struct {
             if (decl.@"linksection") |section| global.setSection(section);
             assert(decl.has_tv);
             const init_val = if (decl.val.getVariable(mod)) |variable| init_val: {
-                break :init_val variable.init.toValue();
+                break :init_val variable.init;
             } else init_val: {
                 global.setGlobalConstant(.True);
-                break :init_val decl.val;
+                break :init_val decl.val.toIntern();
             };
-            if (init_val.toIntern() != .unreachable_value) {
-                const llvm_init = try dg.lowerValue(.{ .ty = decl.ty, .val = init_val });
+            if (init_val != .none) {
+                const llvm_init = try dg.lowerValue(.{ .ty = decl.ty, .val = init_val.toValue() });
                 if (global.globalGetValueType() == llvm_init.typeOf()) {
                     global.setInitializer(llvm_init);
                 } else {
@@ -2748,7 +2748,7 @@ pub const DeclGen = struct {
         if (std.debug.runtime_safety and false) check: {
             if (t.zigTypeTag(mod) == .Opaque) break :check;
             if (!t.hasRuntimeBits(mod)) break :check;
-            if (!llvm_ty.isSized().toBool(mod)) break :check;
+            if (!llvm_ty.isSized().toBool()) break :check;
 
             const zig_size = t.abiSize(mod);
             const llvm_size = dg.object.target_data.abiSizeOfType(llvm_ty);
@@ -3239,7 +3239,7 @@ pub const DeclGen = struct {
                 => unreachable, // non-runtime values
                 .false, .true => {
                     const llvm_type = try dg.lowerType(tv.ty);
-                    return if (tv.val.toBool(mod)) llvm_type.constAllOnes() else llvm_type.constNull();
+                    return if (tv.val.toBool()) llvm_type.constAllOnes() else llvm_type.constNull();
                 },
             },
             .variable,
@@ -3522,15 +3522,19 @@ pub const DeclGen = struct {
                     const elem_ty = vector_type.child.toType();
                     const llvm_elems = try dg.gpa.alloc(*llvm.Value, vector_type.len);
                     defer dg.gpa.free(llvm_elems);
+                    const llvm_i8 = dg.context.intType(8);
                     for (llvm_elems, 0..) |*llvm_elem, i| {
-                        llvm_elem.* = try dg.lowerValue(.{
-                            .ty = elem_ty,
-                            .val = switch (aggregate.storage) {
-                                .bytes => unreachable,
-                                .elems => |elems| elems[i],
-                                .repeated_elem => |elem| elem,
-                            }.toValue(),
-                        });
+                        llvm_elem.* = switch (aggregate.storage) {
+                            .bytes => |bytes| llvm_i8.constInt(bytes[i], .False),
+                            .elems => |elems| try dg.lowerValue(.{
+                                .ty = elem_ty,
+                                .val = elems[i].toValue(),
+                            }),
+                            .repeated_elem => |elem| try dg.lowerValue(.{
+                                .ty = elem_ty,
+                                .val = elem.toValue(),
+                            }),
+                        };
                     }
                     return llvm.constVector(
                         llvm_elems.ptr,
src/codegen/spirv.zig
@@ -654,7 +654,7 @@ pub const DeclGen = struct {
                     .@"unreachable",
                     .generic_poison,
                     => unreachable, // non-runtime values
-                    .false, .true => try self.addConstBool(val.toBool(mod)),
+                    .false, .true => try self.addConstBool(val.toBool()),
                 },
                 .variable,
                 .extern_func,
@@ -974,7 +974,6 @@ pub const DeclGen = struct {
     /// This function should only be called during function code generation.
     fn constant(self: *DeclGen, ty: Type, val: Value, repr: Repr) !IdRef {
         const mod = self.module;
-        const target = self.getTarget();
         const result_ty_ref = try self.resolveType(ty, repr);
 
         log.debug("constant: ty = {}, val = {}", .{ ty.fmt(self.module), val.fmtValue(ty, self.module) });
@@ -991,51 +990,8 @@ pub const DeclGen = struct {
                     return try self.spv.constInt(result_ty_ref, val.toUnsignedInt(mod));
                 }
             },
-            .Bool => switch (repr) {
-                .direct => return try self.spv.constBool(result_ty_ref, val.toBool(mod)),
-                .indirect => return try self.spv.constInt(result_ty_ref, @boolToInt(val.toBool(mod))),
-            },
-            .Float => return switch (ty.floatBits(target)) {
-                16 => try self.spv.resolveId(.{ .float = .{ .ty = result_ty_ref, .value = .{ .float16 = val.toFloat(f16, mod) } } }),
-                32 => try self.spv.resolveId(.{ .float = .{ .ty = result_ty_ref, .value = .{ .float32 = val.toFloat(f32, mod) } } }),
-                64 => try self.spv.resolveId(.{ .float = .{ .ty = result_ty_ref, .value = .{ .float64 = val.toFloat(f64, mod) } } }),
-                80, 128 => unreachable, // TODO
-                else => unreachable,
-            },
-            .ErrorSet => {
-                const value = switch (val.tag()) {
-                    .@"error" => blk: {
-                        const err_name = val.castTag(.@"error").?.data.name;
-                        const kv = try self.module.getErrorValue(err_name);
-                        break :blk @intCast(u16, kv.value);
-                    },
-                    .zero => 0,
-                    else => unreachable,
-                };
-
-                return try self.spv.constInt(result_ty_ref, value);
-            },
-            .ErrorUnion => {
-                const payload_ty = ty.errorUnionPayload();
-                const is_pl = val.errorUnionIsPayload();
-                const error_val = if (!is_pl) val else Value.initTag(.zero);
-
-                const eu_layout = self.errorUnionLayout(payload_ty);
-                if (!eu_layout.payload_has_bits) {
-                    return try self.constant(Type.anyerror, error_val, repr);
-                }
-
-                const payload_val = if (val.castTag(.eu_payload)) |pl| pl.data else Value.undef;
-
-                var members: [2]IdRef = undefined;
-                if (eu_layout.error_first) {
-                    members[0] = try self.constant(Type.anyerror, error_val, .indirect);
-                    members[1] = try self.constant(payload_ty, payload_val, .indirect);
-                } else {
-                    members[0] = try self.constant(payload_ty, payload_val, .indirect);
-                    members[1] = try self.constant(Type.anyerror, error_val, .indirect);
-                }
-                return try self.spv.constComposite(result_ty_ref, &members);
+            .Bool => {
+                @compileError("TODO merge conflict failure");
             },
             // TODO: We can handle most pointers here (decl refs etc), because now they emit an extra
             // OpVariable that is not really required.
src/codegen.zig
@@ -957,7 +957,7 @@ pub fn genTypedValue(
             }
         },
         .Bool => {
-            return GenResult.mcv(.{ .immediate = @boolToInt(typed_value.val.toBool(mod)) });
+            return GenResult.mcv(.{ .immediate = @boolToInt(typed_value.val.toBool()) });
         },
         .Optional => {
             if (typed_value.ty.isPtrLikeOptional(mod)) {
src/InternPool.zig
@@ -621,8 +621,7 @@ pub const Key = union(enum) {
 
     pub fn hashWithHasher(key: Key, hasher: *std.hash.Wyhash, ip: *const InternPool) void {
         const KeyTag = @typeInfo(Key).Union.tag_type.?;
-        const key_tag: KeyTag = key;
-        std.hash.autoHash(hasher, key_tag);
+        std.hash.autoHash(hasher, @as(KeyTag, key));
         switch (key) {
             inline .int_type,
             .ptr_type,
@@ -710,39 +709,58 @@ pub const Key = union(enum) {
 
             .aggregate => |aggregate| {
                 std.hash.autoHash(hasher, aggregate.ty);
-                switch (ip.indexToKey(aggregate.ty)) {
-                    .array_type => |array_type| if (array_type.child == .u8_type) {
-                        switch (aggregate.storage) {
-                            .bytes => |bytes| for (bytes) |byte| std.hash.autoHash(hasher, byte),
-                            .elems => |elems| {
-                                var buffer: Key.Int.Storage.BigIntSpace = undefined;
-                                for (elems) |elem| std.hash.autoHash(
+                const len = ip.aggregateTypeLen(aggregate.ty);
+                const child = switch (ip.indexToKey(aggregate.ty)) {
+                    .array_type => |array_type| array_type.child,
+                    .vector_type => |vector_type| vector_type.child,
+                    .anon_struct_type, .struct_type => .none,
+                    else => unreachable,
+                };
+
+                if (child == .u8_type) {
+                    switch (aggregate.storage) {
+                        .bytes => |bytes| for (bytes[0..@intCast(usize, len)]) |byte| {
+                            std.hash.autoHash(hasher, KeyTag.int);
+                            std.hash.autoHash(hasher, byte);
+                        },
+                        .elems => |elems| for (elems[0..@intCast(usize, len)]) |elem| {
+                            const elem_key = ip.indexToKey(elem);
+                            std.hash.autoHash(hasher, @as(KeyTag, elem_key));
+                            switch (elem_key) {
+                                .undef => {},
+                                .int => |int| std.hash.autoHash(
                                     hasher,
-                                    ip.indexToKey(elem).int.storage.toBigInt(&buffer).to(u8) catch
-                                        unreachable,
-                                );
-                            },
-                            .repeated_elem => |elem| {
-                                const len = ip.aggregateTypeLen(aggregate.ty);
-                                var buffer: Key.Int.Storage.BigIntSpace = undefined;
-                                const byte = ip.indexToKey(elem).int.storage.toBigInt(&buffer).to(u8) catch
-                                    unreachable;
-                                var i: u64 = 0;
-                                while (i < len) : (i += 1) std.hash.autoHash(hasher, byte);
-                            },
-                        }
-                        return;
-                    },
-                    else => {},
+                                    @intCast(u8, int.storage.u64),
+                                ),
+                                else => unreachable,
+                            }
+                        },
+                        .repeated_elem => |elem| {
+                            const elem_key = ip.indexToKey(elem);
+                            var remaining = len;
+                            while (remaining > 0) : (remaining -= 1) {
+                                std.hash.autoHash(hasher, @as(KeyTag, elem_key));
+                                switch (elem_key) {
+                                    .undef => {},
+                                    .int => |int| std.hash.autoHash(
+                                        hasher,
+                                        @intCast(u8, int.storage.u64),
+                                    ),
+                                    else => unreachable,
+                                }
+                            }
+                        },
+                    }
+                    return;
                 }
 
                 switch (aggregate.storage) {
                     .bytes => unreachable,
-                    .elems => |elems| for (elems) |elem| std.hash.autoHash(hasher, elem),
+                    .elems => |elems| for (elems[0..@intCast(usize, len)]) |elem|
+                        std.hash.autoHash(hasher, elem),
                     .repeated_elem => |elem| {
-                        const len = ip.aggregateTypeLen(aggregate.ty);
-                        var i: u64 = 0;
-                        while (i < len) : (i += 1) std.hash.autoHash(hasher, elem);
+                        var remaining = len;
+                        while (remaining > 0) : (remaining -= 1) std.hash.autoHash(hasher, elem);
                     },
                 }
             },
@@ -960,9 +978,10 @@ pub const Key = union(enum) {
                 const b_info = b.aggregate;
                 if (a_info.ty != b_info.ty) return false;
 
+                const len = ip.aggregateTypeLen(a_info.ty);
                 const StorageTag = @typeInfo(Key.Aggregate.Storage).Union.tag_type.?;
                 if (@as(StorageTag, a_info.storage) != @as(StorageTag, b_info.storage)) {
-                    for (0..@intCast(usize, ip.aggregateTypeLen(a_info.ty))) |elem_index| {
+                    for (0..@intCast(usize, len)) |elem_index| {
                         const a_elem = switch (a_info.storage) {
                             .bytes => |bytes| ip.getIfExists(.{ .int = .{
                                 .ty = .u8_type,
@@ -987,11 +1006,19 @@ pub const Key = union(enum) {
                 switch (a_info.storage) {
                     .bytes => |a_bytes| {
                         const b_bytes = b_info.storage.bytes;
-                        return std.mem.eql(u8, a_bytes, b_bytes);
+                        return std.mem.eql(
+                            u8,
+                            a_bytes[0..@intCast(usize, len)],
+                            b_bytes[0..@intCast(usize, len)],
+                        );
                     },
                     .elems => |a_elems| {
                         const b_elems = b_info.storage.elems;
-                        return std.mem.eql(Index, a_elems, b_elems);
+                        return std.mem.eql(
+                            Index,
+                            a_elems[0..@intCast(usize, len)],
+                            b_elems[0..@intCast(usize, len)],
+                        );
                     },
                     .repeated_elem => |a_elem| {
                         const b_elem = b_info.storage.repeated_elem;
@@ -2691,7 +2718,7 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
         },
         .bytes => {
             const extra = ip.extraData(Bytes, data);
-            const len = @intCast(u32, ip.aggregateTypeLen(extra.ty));
+            const len = @intCast(u32, ip.aggregateTypeLenIncludingSentinel(extra.ty));
             return .{ .aggregate = .{
                 .ty = extra.ty,
                 .storage = .{ .bytes = ip.string_bytes.items[@enumToInt(extra.bytes)..][0..len] },
@@ -2699,7 +2726,7 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
         },
         .aggregate => {
             const extra = ip.extraDataTrail(Aggregate, data);
-            const len = @intCast(u32, ip.aggregateTypeLen(extra.data.ty));
+            const len = @intCast(u32, ip.aggregateTypeLenIncludingSentinel(extra.data.ty));
             const fields = @ptrCast([]const Index, ip.extra.items[extra.end..][0..len]);
             return .{ .aggregate = .{
                 .ty = extra.data.ty,
@@ -3145,7 +3172,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
                             }),
                         }),
                         .int => |int| {
-                            assert(int != .none);
+                            assert(ip.typeOf(int) == .usize_type);
                             ip.items.appendAssumeCapacity(.{
                                 .tag = .ptr_int,
                                 .data = try ip.addExtra(gpa, PtrAddr{
@@ -3452,7 +3479,11 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
 
         .enum_tag => |enum_tag| {
             assert(ip.isEnumType(enum_tag.ty));
-            assert(ip.indexToKey(enum_tag.int) == .int);
+            switch (ip.indexToKey(enum_tag.ty)) {
+                .simple_type => assert(ip.isIntegerType(ip.typeOf(enum_tag.int))),
+                .enum_type => |enum_type| assert(ip.typeOf(enum_tag.int) == enum_type.tag_ty),
+                else => unreachable,
+            }
             ip.items.appendAssumeCapacity(.{
                 .tag = .enum_tag,
                 .data = try ip.addExtra(gpa, enum_tag),
@@ -3501,21 +3532,43 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
 
         .aggregate => |aggregate| {
             const ty_key = ip.indexToKey(aggregate.ty);
-            const aggregate_len = ip.aggregateTypeLen(aggregate.ty);
+            const len = ip.aggregateTypeLen(aggregate.ty);
+            const child = switch (ty_key) {
+                .array_type => |array_type| array_type.child,
+                .vector_type => |vector_type| vector_type.child,
+                .anon_struct_type, .struct_type => .none,
+                else => unreachable,
+            };
+            const sentinel = switch (ty_key) {
+                .array_type => |array_type| array_type.sentinel,
+                .vector_type, .anon_struct_type, .struct_type => .none,
+                else => unreachable,
+            };
+            const len_including_sentinel = len + @boolToInt(sentinel != .none);
             switch (aggregate.storage) {
                 .bytes => |bytes| {
-                    assert(ty_key.array_type.child == .u8_type);
-                    assert(bytes.len == aggregate_len);
+                    assert(child == .u8_type);
+                    if (bytes.len != len) {
+                        assert(bytes.len == len_including_sentinel);
+                        assert(bytes[len] == ip.indexToKey(sentinel).int.storage.u64);
+                        unreachable;
+                    }
                 },
                 .elems => |elems| {
-                    assert(elems.len == aggregate_len);
+                    if (elems.len != len) {
+                        assert(elems.len == len_including_sentinel);
+                        assert(elems[len] == sentinel);
+                        unreachable;
+                    }
+                },
+                .repeated_elem => |elem| {
+                    assert(sentinel == .none or elem == sentinel);
                 },
-                .repeated_elem => {},
             }
             switch (ty_key) {
-                inline .array_type, .vector_type => |seq_type| {
+                .array_type, .vector_type => {
                     for (aggregate.storage.values()) |elem| {
-                        assert(ip.typeOf(elem) == seq_type.child);
+                        assert(ip.typeOf(elem) == child);
                     }
                 },
                 .struct_type => |struct_type| {
@@ -3534,7 +3587,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
                 else => unreachable,
             }
 
-            if (aggregate_len == 0) {
+            if (len == 0) {
                 ip.items.appendAssumeCapacity(.{
                     .tag = .only_possible_value,
                     .data = @enumToInt(aggregate.ty),
@@ -3543,41 +3596,43 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
             }
 
             switch (ty_key) {
-                .anon_struct_type => |anon_struct_type| {
-                    if (switch (aggregate.storage) {
+                .anon_struct_type => |anon_struct_type| opv: {
+                    switch (aggregate.storage) {
                         .bytes => |bytes| for (anon_struct_type.values, bytes) |value, byte| {
                             if (value != ip.getIfExists(.{ .int = .{
                                 .ty = .u8_type,
                                 .storage = .{ .u64 = byte },
-                            } })) break false;
-                        } else true,
-                        .elems => |elems| std.mem.eql(Index, anon_struct_type.values, elems),
+                            } })) break :opv;
+                        },
+                        .elems => |elems| if (!std.mem.eql(
+                            Index,
+                            anon_struct_type.values,
+                            elems,
+                        )) break :opv,
                         .repeated_elem => |elem| for (anon_struct_type.values) |value| {
-                            if (value != elem) break false;
-                        } else true,
-                    }) {
-                        // This encoding works thanks to the fact that, as we just verified,
-                        // the type itself contains a slice of values that can be provided
-                        // in the aggregate fields.
-                        ip.items.appendAssumeCapacity(.{
-                            .tag = .only_possible_value,
-                            .data = @enumToInt(aggregate.ty),
-                        });
-                        return @intToEnum(Index, ip.items.len - 1);
+                            if (value != elem) break :opv;
+                        },
                     }
+                    // This encoding works thanks to the fact that, as we just verified,
+                    // the type itself contains a slice of values that can be provided
+                    // in the aggregate fields.
+                    ip.items.appendAssumeCapacity(.{
+                        .tag = .only_possible_value,
+                        .data = @enumToInt(aggregate.ty),
+                    });
+                    return @intToEnum(Index, ip.items.len - 1);
                 },
                 else => {},
             }
 
-            if (switch (aggregate.storage) {
-                .bytes => |bytes| for (bytes[1..]) |byte| {
-                    if (byte != bytes[0]) break false;
-                } else true,
-                .elems => |elems| for (elems[1..]) |elem| {
-                    if (elem != elems[0]) break false;
-                } else true,
-                .repeated_elem => true,
-            }) {
+            repeated: {
+                switch (aggregate.storage) {
+                    .bytes => |bytes| for (bytes[1..@intCast(usize, len)]) |byte|
+                        if (byte != bytes[0]) break :repeated,
+                    .elems => |elems| for (elems[1..@intCast(usize, len)]) |elem|
+                        if (elem != elems[0]) break :repeated,
+                    .repeated_elem => {},
+                }
                 const elem = switch (aggregate.storage) {
                     .bytes => |bytes| elem: {
                         _ = ip.map.pop();
@@ -3607,42 +3662,48 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
                 return @intToEnum(Index, ip.items.len - 1);
             }
 
-            switch (ty_key) {
-                .array_type => |array_type| if (array_type.child == .u8_type) {
-                    const len_including_sentinel = aggregate_len + @boolToInt(array_type.sentinel != .none);
-                    try ip.string_bytes.ensureUnusedCapacity(gpa, len_including_sentinel + 1);
-                    try ip.extra.ensureUnusedCapacity(gpa, @typeInfo(Bytes).Struct.fields.len);
-                    var buffer: Key.Int.Storage.BigIntSpace = undefined;
-                    switch (aggregate.storage) {
-                        .bytes => |bytes| ip.string_bytes.appendSliceAssumeCapacity(bytes),
-                        .elems => |elems| for (elems) |elem| ip.string_bytes.appendAssumeCapacity(
-                            ip.indexToKey(elem).int.storage.toBigInt(&buffer).to(u8) catch unreachable,
+            if (child == .u8_type) bytes: {
+                const string_bytes_index = ip.string_bytes.items.len;
+                try ip.string_bytes.ensureUnusedCapacity(gpa, len_including_sentinel + 1);
+                try ip.extra.ensureUnusedCapacity(gpa, @typeInfo(Bytes).Struct.fields.len);
+                switch (aggregate.storage) {
+                    .bytes => |bytes| ip.string_bytes.appendSliceAssumeCapacity(bytes),
+                    .elems => |elems| for (elems) |elem| switch (ip.indexToKey(elem)) {
+                        .undef => {
+                            ip.string_bytes.shrinkRetainingCapacity(string_bytes_index);
+                            break :bytes;
+                        },
+                        .int => |int| ip.string_bytes.appendAssumeCapacity(
+                            @intCast(u8, int.storage.u64),
                         ),
-                        .repeated_elem => |elem| @memset(
-                            ip.string_bytes.addManyAsSliceAssumeCapacity(aggregate_len),
-                            ip.indexToKey(elem).int.storage.toBigInt(&buffer).to(u8) catch unreachable,
+                        else => unreachable,
+                    },
+                    .repeated_elem => |elem| switch (ip.indexToKey(elem)) {
+                        .undef => break :bytes,
+                        .int => |int| @memset(
+                            ip.string_bytes.addManyAsSliceAssumeCapacity(len),
+                            @intCast(u8, int.storage.u64),
                         ),
-                    }
-                    if (array_type.sentinel != .none) ip.string_bytes.appendAssumeCapacity(
-                        ip.indexToKey(array_type.sentinel).int.storage.toBigInt(&buffer).to(u8) catch
-                            unreachable,
-                    );
-                    const bytes = try ip.getOrPutTrailingString(gpa, len_including_sentinel);
-                    ip.items.appendAssumeCapacity(.{
-                        .tag = .bytes,
-                        .data = ip.addExtraAssumeCapacity(Bytes{
-                            .ty = aggregate.ty,
-                            .bytes = bytes.toString(),
-                        }),
-                    });
-                    return @intToEnum(Index, ip.items.len - 1);
-                },
-                else => {},
+                        else => unreachable,
+                    },
+                }
+                if (sentinel != .none) ip.string_bytes.appendAssumeCapacity(
+                    @intCast(u8, ip.indexToKey(sentinel).int.storage.u64),
+                );
+                const bytes = try ip.getOrPutTrailingString(gpa, len_including_sentinel);
+                ip.items.appendAssumeCapacity(.{
+                    .tag = .bytes,
+                    .data = ip.addExtraAssumeCapacity(Bytes{
+                        .ty = aggregate.ty,
+                        .bytes = bytes.toString(),
+                    }),
+                });
+                return @intToEnum(Index, ip.items.len - 1);
             }
 
             try ip.extra.ensureUnusedCapacity(
                 gpa,
-                @typeInfo(Aggregate).Struct.fields.len + aggregate_len,
+                @typeInfo(Aggregate).Struct.fields.len + len_including_sentinel,
             );
             ip.items.appendAssumeCapacity(.{
                 .tag = .aggregate,
@@ -3651,6 +3712,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
                 }),
             });
             ip.extra.appendSliceAssumeCapacity(@ptrCast([]const u32, aggregate.storage.elems));
+            if (sentinel != .none) ip.extra.appendAssumeCapacity(@enumToInt(sentinel));
         },
 
         .un => |un| {
@@ -4183,10 +4245,12 @@ pub fn sliceLen(ip: InternPool, i: Index) Index {
 /// Given an existing value, returns the same value but with the supplied type.
 /// Only some combinations are allowed:
 /// * identity coercion
+/// * undef => any
 /// * int <=> int
 /// * int <=> enum
 /// * enum_literal => enum
 /// * ptr <=> ptr
+/// * int => ptr
 /// * null_value => opt
 /// * payload => opt
 /// * error set <=> error set
@@ -4194,68 +4258,93 @@ pub fn sliceLen(ip: InternPool, i: Index) Index {
 /// * error set => error union
 /// * payload => error union
 /// * fn <=> fn
+/// * array <=> array
+/// * array <=> vector
+/// * vector <=> vector
 pub fn getCoerced(ip: *InternPool, gpa: Allocator, val: Index, new_ty: Index) Allocator.Error!Index {
     const old_ty = ip.typeOf(val);
     if (old_ty == new_ty) return val;
-    switch (ip.indexToKey(val)) {
-        .extern_func => |extern_func| if (ip.isFunctionType(new_ty))
-            return ip.get(gpa, .{ .extern_func = .{
-                .ty = new_ty,
-                .decl = extern_func.decl,
-                .lib_name = extern_func.lib_name,
-            } }),
-        .func => |func| if (ip.isFunctionType(new_ty))
-            return ip.get(gpa, .{ .func = .{
-                .ty = new_ty,
-                .index = func.index,
-            } }),
-        .int => |int| if (ip.isIntegerType(new_ty))
-            return getCoercedInts(ip, gpa, int, new_ty)
-        else if (ip.isEnumType(new_ty))
-            return ip.get(gpa, .{ .enum_tag = .{
+    switch (val) {
+        .undef => return ip.get(gpa, .{ .undef = new_ty }),
+        .null_value => if (ip.isOptionalType(new_ty))
+            return ip.get(gpa, .{ .opt = .{
                 .ty = new_ty,
-                .int = val,
+                .val = .none,
             } }),
-        .enum_tag => |enum_tag| if (ip.isIntegerType(new_ty))
-            return getCoercedInts(ip, gpa, ip.indexToKey(enum_tag.int).int, new_ty),
-        .enum_literal => |enum_literal| switch (ip.indexToKey(new_ty)) {
-            .enum_type => |enum_type| {
-                const index = enum_type.nameIndex(ip, enum_literal).?;
+        else => switch (ip.indexToKey(val)) {
+            .undef => return ip.get(gpa, .{ .undef = new_ty }),
+            .extern_func => |extern_func| if (ip.isFunctionType(new_ty))
+                return ip.get(gpa, .{ .extern_func = .{
+                    .ty = new_ty,
+                    .decl = extern_func.decl,
+                    .lib_name = extern_func.lib_name,
+                } }),
+            .func => |func| if (ip.isFunctionType(new_ty))
+                return ip.get(gpa, .{ .func = .{
+                    .ty = new_ty,
+                    .index = func.index,
+                } }),
+            .int => |int| if (ip.isIntegerType(new_ty))
+                return getCoercedInts(ip, gpa, int, new_ty)
+            else if (ip.isEnumType(new_ty))
                 return ip.get(gpa, .{ .enum_tag = .{
                     .ty = new_ty,
-                    .int = if (enum_type.values.len != 0)
-                        enum_type.values[index]
-                    else
-                        try ip.get(gpa, .{ .int = .{
-                            .ty = enum_type.tag_ty,
-                            .storage = .{ .u64 = index },
-                        } }),
-                } });
+                    .int = val,
+                } })
+            else if (ip.isPointerType(new_ty))
+                return ip.get(gpa, .{ .ptr = .{
+                    .ty = new_ty,
+                    .addr = .{ .int = val },
+                } }),
+            .enum_tag => |enum_tag| if (ip.isIntegerType(new_ty))
+                return getCoercedInts(ip, gpa, ip.indexToKey(enum_tag.int).int, new_ty),
+            .enum_literal => |enum_literal| switch (ip.indexToKey(new_ty)) {
+                .enum_type => |enum_type| {
+                    const index = enum_type.nameIndex(ip, enum_literal).?;
+                    return ip.get(gpa, .{ .enum_tag = .{
+                        .ty = new_ty,
+                        .int = if (enum_type.values.len != 0)
+                            enum_type.values[index]
+                        else
+                            try ip.get(gpa, .{ .int = .{
+                                .ty = enum_type.tag_ty,
+                                .storage = .{ .u64 = index },
+                            } }),
+                    } });
+                },
+                else => {},
             },
-            else => {},
-        },
-        .ptr => |ptr| if (ip.isPointerType(new_ty))
-            return ip.get(gpa, .{ .ptr = .{
-                .ty = new_ty,
-                .addr = ptr.addr,
-                .len = ptr.len,
-            } }),
-        .err => |err| if (ip.isErrorSetType(new_ty))
-            return ip.get(gpa, .{ .err = .{
-                .ty = new_ty,
-                .name = err.name,
-            } })
-        else if (ip.isErrorUnionType(new_ty))
-            return ip.get(gpa, .{ .error_union = .{
-                .ty = new_ty,
-                .val = .{ .err_name = err.name },
-            } }),
-        .error_union => |error_union| if (ip.isErrorUnionType(new_ty))
-            return ip.get(gpa, .{ .error_union = .{
+            .ptr => |ptr| if (ip.isPointerType(new_ty))
+                return ip.get(gpa, .{ .ptr = .{
+                    .ty = new_ty,
+                    .addr = ptr.addr,
+                    .len = ptr.len,
+                } }),
+            .err => |err| if (ip.isErrorSetType(new_ty))
+                return ip.get(gpa, .{ .err = .{
+                    .ty = new_ty,
+                    .name = err.name,
+                } })
+            else if (ip.isErrorUnionType(new_ty))
+                return ip.get(gpa, .{ .error_union = .{
+                    .ty = new_ty,
+                    .val = .{ .err_name = err.name },
+                } }),
+            .error_union => |error_union| if (ip.isErrorUnionType(new_ty))
+                return ip.get(gpa, .{ .error_union = .{
+                    .ty = new_ty,
+                    .val = error_union.val,
+                } }),
+            .aggregate => |aggregate| return ip.get(gpa, .{ .aggregate = .{
                 .ty = new_ty,
-                .val = error_union.val,
+                .storage = switch (aggregate.storage) {
+                    .bytes => |bytes| .{ .bytes = bytes[0..@intCast(usize, ip.aggregateTypeLen(new_ty))] },
+                    .elems => |elems| .{ .elems = elems[0..@intCast(usize, ip.aggregateTypeLen(new_ty))] },
+                    .repeated_elem => |elem| .{ .repeated_elem = elem },
+                },
             } }),
-        else => {},
+            else => {},
+        },
     }
     switch (ip.indexToKey(new_ty)) {
         .opt_type => |child_type| switch (val) {
@@ -4527,7 +4616,7 @@ fn dumpFallible(ip: InternPool, arena: Allocator) anyerror!void {
 
             .type_function => b: {
                 const info = ip.extraData(TypeFunction, data);
-                break :b @sizeOf(TypeFunction) + (@sizeOf(u32) * info.params_len);
+                break :b @sizeOf(TypeFunction) + (@sizeOf(Index) * info.params_len);
             },
 
             .undef => 0,
@@ -4570,14 +4659,14 @@ fn dumpFallible(ip: InternPool, arena: Allocator) anyerror!void {
 
             .bytes => b: {
                 const info = ip.extraData(Bytes, data);
-                const len = @intCast(u32, ip.aggregateTypeLen(info.ty));
+                const len = @intCast(u32, ip.aggregateTypeLenIncludingSentinel(info.ty));
                 break :b @sizeOf(Bytes) + len +
                     @boolToInt(ip.string_bytes.items[@enumToInt(info.bytes) + len - 1] != 0);
             },
             .aggregate => b: {
                 const info = ip.extraData(Aggregate, data);
-                const fields_len = @intCast(u32, ip.aggregateTypeLen(info.ty));
-                break :b @sizeOf(Aggregate) + (@sizeOf(u32) * fields_len);
+                const fields_len = @intCast(u32, ip.aggregateTypeLenIncludingSentinel(info.ty));
+                break :b @sizeOf(Aggregate) + (@sizeOf(Index) * fields_len);
             },
             .repeated => @sizeOf(Repeated),
 
@@ -4889,6 +4978,16 @@ pub fn aggregateTypeLen(ip: InternPool, ty: Index) u64 {
     };
 }
 
+pub fn aggregateTypeLenIncludingSentinel(ip: InternPool, ty: Index) u64 {
+    return switch (ip.indexToKey(ty)) {
+        .struct_type => |struct_type| ip.structPtrConst(struct_type.index.unwrap() orelse return 0).fields.count(),
+        .anon_struct_type => |anon_struct_type| anon_struct_type.types.len,
+        .array_type => |array_type| array_type.len + @boolToInt(array_type.sentinel != .none),
+        .vector_type => |vector_type| vector_type.len,
+        else => unreachable,
+    };
+}
+
 pub fn isNoReturn(ip: InternPool, ty: Index) bool {
     return switch (ty) {
         .noreturn_type => true,
src/Module.zig
@@ -99,6 +99,7 @@ monomorphed_funcs: MonomorphedFuncsSet = .{},
 /// The set of all comptime function calls that have been cached so that future calls
 /// with the same parameters will get the same return value.
 memoized_calls: MemoizedCallSet = .{},
+memoized_call_args: MemoizedCall.Args = .{},
 /// Contains the values from `@setAlignStack`. A sparse table is used here
 /// instead of a field of `Fn` because usage of `@setAlignStack` is rare, while
 /// functions are many.
@@ -230,46 +231,30 @@ pub const MemoizedCallSet = std.HashMapUnmanaged(
 );
 
 pub const MemoizedCall = struct {
-    module: *Module,
+    args: *const Args,
+
+    pub const Args = std.ArrayListUnmanaged(InternPool.Index);
 
     pub const Key = struct {
         func: Fn.Index,
-        args: []TypedValue,
-    };
+        args_index: u32,
+        args_count: u32,
 
-    pub const Result = struct {
-        val: Value,
-        arena: std.heap.ArenaAllocator.State,
+        pub fn args(key: Key, ctx: MemoizedCall) []InternPool.Index {
+            return ctx.args.items[key.args_index..][0..key.args_count];
+        }
     };
 
-    pub fn eql(ctx: @This(), a: Key, b: Key) bool {
-        if (a.func != b.func) return false;
-
-        assert(a.args.len == b.args.len);
-        for (a.args, 0..) |a_arg, arg_i| {
-            const b_arg = b.args[arg_i];
-            if (!a_arg.eql(b_arg, ctx.module)) {
-                return false;
-            }
-        }
+    pub const Result = InternPool.Index;
 
-        return true;
+    pub fn eql(ctx: MemoizedCall, a: Key, b: Key) bool {
+        return a.func == b.func and mem.eql(InternPool.Index, a.args(ctx), b.args(ctx));
     }
 
-    /// Must match `Sema.GenericCallAdapter.hash`.
-    pub fn hash(ctx: @This(), key: Key) u64 {
+    pub fn hash(ctx: MemoizedCall, key: Key) u64 {
         var hasher = std.hash.Wyhash.init(0);
-
-        // The generic function Decl is guaranteed to be the first dependency
-        // of each of its instantiations.
         std.hash.autoHash(&hasher, key.func);
-
-        // This logic must be kept in sync with the logic in `analyzeCall` that
-        // computes the hash.
-        for (key.args) |arg| {
-            arg.hash(&hasher, ctx.module);
-        }
-
+        std.hash.autoHashStrat(&hasher, key.args(ctx), .Deep);
         return hasher.final();
     }
 };
@@ -883,6 +868,10 @@ pub const Decl = struct {
             return decl.ty.abiAlignment(mod);
         }
     }
+
+    pub fn intern(decl: *Decl, mod: *Module) Allocator.Error!void {
+        decl.val = (try decl.val.intern(decl.ty, mod)).toValue();
+    }
 };
 
 /// This state is attached to every Decl when Module emit_h is non-null.
@@ -3325,15 +3314,8 @@ pub fn deinit(mod: *Module) void {
     mod.test_functions.deinit(gpa);
     mod.align_stack_fns.deinit(gpa);
     mod.monomorphed_funcs.deinit(gpa);
-
-    {
-        var it = mod.memoized_calls.iterator();
-        while (it.next()) |entry| {
-            gpa.free(entry.key_ptr.args);
-            entry.value_ptr.arena.promote(gpa).deinit();
-        }
-        mod.memoized_calls.deinit(gpa);
-    }
+    mod.memoized_call_args.deinit(gpa);
+    mod.memoized_calls.deinit(gpa);
 
     mod.decls_free_list.deinit(gpa);
     mod.allocated_decls.deinit(gpa);
@@ -5894,6 +5876,7 @@ pub fn initNewAnonDecl(
     typed_value: TypedValue,
     name: [:0]u8,
 ) !void {
+    assert(typed_value.ty.toIntern() == mod.intern_pool.typeOf(typed_value.val.toIntern()));
     errdefer mod.gpa.free(name);
 
     const new_decl = mod.declPtr(new_decl_index);
@@ -6645,7 +6628,7 @@ pub fn markDeclAlive(mod: *Module, decl: *Decl) Allocator.Error!void {
     if (decl.alive) return;
     decl.alive = true;
 
-    decl.val = (try decl.val.intern(decl.ty, mod)).toValue();
+    try decl.intern(mod);
 
     // This is the first time we are marking this Decl alive. We must
     // therefore recurse into its value and mark any Decl it references
@@ -6749,15 +6732,19 @@ pub fn ptrType(mod: *Module, info: InternPool.Key.PtrType) Allocator.Error!Type
         }
     }
 
-    // Canonicalize host_size. If it matches the bit size of the pointee type,
-    // we change it to 0 here. If this causes an assertion trip, the pointee type
-    // needs to be resolved before calling this ptr() function.
-    if (info.host_size != 0) {
-        const elem_bit_size = info.elem_type.toType().bitSize(mod);
-        assert(info.bit_offset + elem_bit_size <= info.host_size * 8);
-        if (info.host_size * 8 == elem_bit_size) {
-            canon_info.host_size = 0;
-        }
+    switch (info.vector_index) {
+        // Canonicalize host_size. If it matches the bit size of the pointee type,
+        // we change it to 0 here. If this causes an assertion trip, the pointee type
+        // needs to be resolved before calling this ptr() function.
+        .none => if (info.host_size != 0) {
+            const elem_bit_size = info.elem_type.toType().bitSize(mod);
+            assert(info.bit_offset + elem_bit_size <= info.host_size * 8);
+            if (info.host_size * 8 == elem_bit_size) {
+                canon_info.host_size = 0;
+            }
+        },
+        .runtime => {},
+        _ => assert(@enumToInt(info.vector_index) < info.host_size),
     }
 
     return (try intern(mod, .{ .ptr_type = canon_info })).toType();
src/RangeSet.zig
@@ -1,18 +1,18 @@
 const std = @import("std");
+const assert = std.debug.assert;
 const Order = std.math.Order;
 
-const RangeSet = @This();
+const InternPool = @import("InternPool.zig");
 const Module = @import("Module.zig");
+const RangeSet = @This();
 const SwitchProngSrc = @import("Module.zig").SwitchProngSrc;
-const Type = @import("type.zig").Type;
-const Value = @import("value.zig").Value;
 
 ranges: std.ArrayList(Range),
 module: *Module,
 
 pub const Range = struct {
-    first: Value,
-    last: Value,
+    first: InternPool.Index,
+    last: InternPool.Index,
     src: SwitchProngSrc,
 };
 
@@ -29,18 +29,27 @@ pub fn deinit(self: *RangeSet) void {
 
 pub fn add(
     self: *RangeSet,
-    first: Value,
-    last: Value,
-    ty: Type,
+    first: InternPool.Index,
+    last: InternPool.Index,
     src: SwitchProngSrc,
 ) !?SwitchProngSrc {
+    const mod = self.module;
+    const ip = &mod.intern_pool;
+
+    const ty = ip.typeOf(first);
+    assert(ty == ip.typeOf(last));
+
     for (self.ranges.items) |range| {
-        if (last.compareScalar(.gte, range.first, ty, self.module) and
-            first.compareScalar(.lte, range.last, ty, self.module))
+        assert(ty == ip.typeOf(range.first));
+        assert(ty == ip.typeOf(range.last));
+
+        if (last.toValue().compareScalar(.gte, range.first.toValue(), ty.toType(), mod) and
+            first.toValue().compareScalar(.lte, range.last.toValue(), ty.toType(), mod))
         {
             return range.src; // They overlap.
         }
     }
+
     try self.ranges.append(.{
         .first = first,
         .last = last,
@@ -49,30 +58,29 @@ pub fn add(
     return null;
 }
 
-const LessThanContext = struct { ty: Type, module: *Module };
-
 /// Assumes a and b do not overlap
-fn lessThan(ctx: LessThanContext, a: Range, b: Range) bool {
-    return a.first.compareScalar(.lt, b.first, ctx.ty, ctx.module);
+fn lessThan(mod: *Module, a: Range, b: Range) bool {
+    const ty = mod.intern_pool.typeOf(a.first).toType();
+    return a.first.toValue().compareScalar(.lt, b.first.toValue(), ty, mod);
 }
 
-pub fn spans(self: *RangeSet, first: Value, last: Value, ty: Type) !bool {
+pub fn spans(self: *RangeSet, first: InternPool.Index, last: InternPool.Index) !bool {
+    const mod = self.module;
+    const ip = &mod.intern_pool;
+    assert(ip.typeOf(first) == ip.typeOf(last));
+
     if (self.ranges.items.len == 0)
         return false;
 
-    const mod = self.module;
-    std.mem.sort(Range, self.ranges.items, LessThanContext{
-        .ty = ty,
-        .module = mod,
-    }, lessThan);
+    std.mem.sort(Range, self.ranges.items, mod, lessThan);
 
-    if (!self.ranges.items[0].first.eql(first, ty, mod) or
-        !self.ranges.items[self.ranges.items.len - 1].last.eql(last, ty, mod))
+    if (self.ranges.items[0].first != first or
+        self.ranges.items[self.ranges.items.len - 1].last != last)
     {
         return false;
     }
 
-    var space: Value.BigIntSpace = undefined;
+    var space: InternPool.Key.Int.Storage.BigIntSpace = undefined;
 
     var counter = try std.math.big.int.Managed.init(self.ranges.allocator);
     defer counter.deinit();
@@ -83,10 +91,10 @@ pub fn spans(self: *RangeSet, first: Value, last: Value, ty: Type) !bool {
         const prev = self.ranges.items[i];
 
         // prev.last + 1 == cur.first
-        try counter.copy(prev.last.toBigInt(&space, mod));
+        try counter.copy(prev.last.toValue().toBigInt(&space, mod));
         try counter.addScalar(&counter, 1);
 
-        const cur_start_int = cur.first.toBigInt(&space, mod);
+        const cur_start_int = cur.first.toValue().toBigInt(&space, mod);
         if (!cur_start_int.eq(counter.toConst())) {
             return false;
         }
src/Sema.zig
@@ -1609,7 +1609,7 @@ fn analyzeBodyInner(
                     if (err == error.AnalysisFail and block.comptime_reason != null) try block.comptime_reason.?.explain(sema, sema.err);
                     return err;
                 };
-                const inline_body = if (cond.val.toBool(mod)) then_body else else_body;
+                const inline_body = if (cond.val.toBool()) then_body else else_body;
 
                 try sema.maybeErrorUnwrapCondbr(block, inline_body, extra.data.condition, cond_src);
                 const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse
@@ -1630,7 +1630,7 @@ fn analyzeBodyInner(
                     if (err == error.AnalysisFail and block.comptime_reason != null) try block.comptime_reason.?.explain(sema, sema.err);
                     return err;
                 };
-                const inline_body = if (cond.val.toBool(mod)) then_body else else_body;
+                const inline_body = if (cond.val.toBool()) then_body else else_body;
 
                 try sema.maybeErrorUnwrapCondbr(block, inline_body, extra.data.condition, cond_src);
                 const old_runtime_index = block.runtime_index;
@@ -1663,7 +1663,7 @@ fn analyzeBodyInner(
                     if (err == error.AnalysisFail and block.comptime_reason != null) try block.comptime_reason.?.explain(sema, sema.err);
                     return err;
                 };
-                if (is_non_err_val.toBool(mod)) {
+                if (is_non_err_val.toBool()) {
                     break :blk try sema.analyzeErrUnionPayload(block, src, err_union_ty, err_union, operand_src, false);
                 }
                 const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse
@@ -1689,7 +1689,7 @@ fn analyzeBodyInner(
                     if (err == error.AnalysisFail and block.comptime_reason != null) try block.comptime_reason.?.explain(sema, sema.err);
                     return err;
                 };
-                if (is_non_err_val.toBool(mod)) {
+                if (is_non_err_val.toBool()) {
                     break :blk try sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false);
                 }
                 const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse
@@ -1778,12 +1778,11 @@ fn resolveConstBool(
     zir_ref: Zir.Inst.Ref,
     reason: []const u8,
 ) !bool {
-    const mod = sema.mod;
     const air_inst = try sema.resolveInst(zir_ref);
     const wanted_type = Type.bool;
     const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src);
     const val = try sema.resolveConstValue(block, src, coerced_inst, reason);
-    return val.toBool(mod);
+    return val.toBool();
 }
 
 pub fn resolveConstString(
@@ -2488,7 +2487,7 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
                 defer anon_decl.deinit();
                 const decl_index = try anon_decl.finish(
                     pointee_ty,
-                    Value.undef,
+                    (try mod.intern(.{ .undef = pointee_ty.toIntern() })).toValue(),
                     alignment.toByteUnits(0),
                 );
                 sema.air_instructions.items(.data)[ptr_inst].inferred_alloc_comptime.decl_index = decl_index;
@@ -2611,7 +2610,7 @@ fn coerceResultPtr(
                     .@"addrspace" = addr_space,
                 });
                 if (try sema.resolveDefinedValue(block, src, new_ptr)) |ptr_val| {
-                    new_ptr = try sema.addConstant(ptr_operand_ty, ptr_val);
+                    new_ptr = try sema.addConstant(ptr_operand_ty, try mod.getCoerced(ptr_val, ptr_operand_ty));
                 } else {
                     new_ptr = try sema.bitCast(block, ptr_operand_ty, new_ptr, src, null);
                 }
@@ -3613,7 +3612,7 @@ fn makePtrConst(sema: *Sema, block: *Block, alloc: Air.Inst.Ref) CompileError!Ai
 
     // Detect if a comptime value simply needs to have its type changed.
     if (try sema.resolveMaybeUndefVal(alloc)) |val| {
-        return sema.addConstant(const_ptr_ty, val);
+        return sema.addConstant(const_ptr_ty, try mod.getCoerced(val, const_ptr_ty));
     }
 
     return block.addBitCast(const_ptr_ty, alloc);
@@ -3735,6 +3734,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
             try mod.declareDeclDependency(sema.owner_decl_index, decl_index);
 
             const decl = mod.declPtr(decl_index);
+            if (iac.is_const) try decl.intern(mod);
             const final_elem_ty = decl.ty;
             const final_ptr_ty = try mod.ptrType(.{
                 .elem_type = final_elem_ty.toIntern(),
@@ -3774,7 +3774,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
                 // Detect if the value is comptime-known. In such case, the
                 // last 3 AIR instructions of the block will look like this:
                 //
-                //   %a = interned
+                //   %a = inferred_alloc
                 //   %b = bitcast(%a)
                 //   %c = store(%b, %d)
                 //
@@ -3814,22 +3814,22 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
                     }
                 };
 
-                const const_inst = while (true) {
+                while (true) {
                     if (search_index == 0) break :ct;
                     search_index -= 1;
 
                     const candidate = block.instructions.items[search_index];
+                    if (candidate == ptr_inst) break;
                     switch (air_tags[candidate]) {
                         .dbg_stmt, .dbg_block_begin, .dbg_block_end => continue,
-                        .interned => break candidate,
                         else => break :ct,
                     }
-                };
+                }
 
                 const store_op = air_datas[store_inst].bin_op;
                 const store_val = (try sema.resolveMaybeUndefVal(store_op.rhs)) orelse break :ct;
                 if (store_op.lhs != Air.indexToRef(bitcast_inst)) break :ct;
-                if (air_datas[bitcast_inst].ty_op.operand != Air.indexToRef(const_inst)) break :ct;
+                if (air_datas[bitcast_inst].ty_op.operand != ptr) break :ct;
 
                 const new_decl_index = d: {
                     var anon_decl = try block.startAnonDecl();
@@ -3850,7 +3850,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
                 sema.air_instructions.set(ptr_inst, .{
                     .tag = .interned,
                     .data = .{ .interned = try mod.intern(.{ .ptr = .{
-                        .ty = final_elem_ty.toIntern(),
+                        .ty = final_ptr_ty.toIntern(),
                         .addr = .{ .decl = new_decl_index },
                     } }) },
                 });
@@ -4707,15 +4707,23 @@ fn zirValidateArrayInit(
         return;
     }
 
+    // If the array has one possible value, the value is always comptime-known.
+    if (try sema.typeHasOnePossibleValue(array_ty)) |array_opv| {
+        const array_init = try sema.addConstant(array_ty, array_opv);
+        try sema.storePtr2(block, init_src, array_ptr, init_src, array_init, init_src, .store);
+        return;
+    }
+
     var array_is_comptime = true;
     var first_block_index = block.instructions.items.len;
     var make_runtime = false;
 
     // Collect the comptime element values in case the array literal ends up
     // being comptime-known.
-    const array_len_s = try sema.usizeCast(block, init_src, array_ty.arrayLenIncludingSentinel(mod));
-    const element_vals = try sema.arena.alloc(InternPool.Index, array_len_s);
-    const opt_opv = try sema.typeHasOnePossibleValue(array_ty);
+    const element_vals = try sema.arena.alloc(
+        InternPool.Index,
+        try sema.usizeCast(block, init_src, array_len),
+    );
     const air_tags = sema.air_instructions.items(.tag);
     const air_datas = sema.air_instructions.items(.data);
 
@@ -4727,12 +4735,6 @@ fn zirValidateArrayInit(
                 element_vals[i] = opv.toIntern();
                 continue;
             }
-        } else {
-            // Array has one possible value, so value is always comptime-known
-            if (opt_opv) |opv| {
-                element_vals[i] = opv.toIntern();
-                continue;
-            }
         }
 
         const elem_ptr_air_ref = sema.inst_map.get(elem_ptr).?;
@@ -4814,11 +4816,6 @@ fn zirValidateArrayInit(
 
         // Our task is to delete all the `elem_ptr` and `store` instructions, and insert
         // instead a single `store` to the array_ptr with a comptime struct value.
-        // Also to populate the sentinel value, if any.
-        if (array_ty.sentinel(mod)) |sentinel_val| {
-            element_vals[instrs.len] = sentinel_val.toIntern();
-        }
-
         block.instructions.shrinkRetainingCapacity(first_block_index);
 
         var array_val = try mod.intern(.{ .aggregate = .{
@@ -6259,7 +6256,7 @@ fn popErrorReturnTrace(
     if (operand != .none) {
         is_non_error_inst = try sema.analyzeIsNonErr(block, src, operand);
         if (try sema.resolveDefinedValue(block, src, is_non_error_inst)) |cond_val|
-            is_non_error = cond_val.toBool(mod);
+            is_non_error = cond_val.toBool();
     } else is_non_error = true; // no operand means pop unconditionally
 
     if (is_non_error == true) {
@@ -6873,14 +6870,15 @@ fn analyzeCall(
 
         // If it's a comptime function call, we need to memoize it as long as no external
         // comptime memory is mutated.
-        var memoized_call_key: Module.MemoizedCall.Key = undefined;
+        var memoized_call_key = Module.MemoizedCall.Key{
+            .func = module_fn_index,
+            .args_index = @intCast(u32, mod.memoized_call_args.items.len),
+            .args_count = @intCast(u32, func_ty_info.param_types.len),
+        };
         var delete_memoized_call_key = false;
-        defer if (delete_memoized_call_key) gpa.free(memoized_call_key.args);
+        defer if (delete_memoized_call_key) mod.memoized_call_args.shrinkRetainingCapacity(memoized_call_key.args_index);
         if (is_comptime_call) {
-            memoized_call_key = .{
-                .func = module_fn_index,
-                .args = try gpa.alloc(TypedValue, func_ty_info.param_types.len),
-            };
+            try mod.memoized_call_args.ensureUnusedCapacity(gpa, memoized_call_key.args_count);
             delete_memoized_call_key = true;
         }
 
@@ -6916,8 +6914,7 @@ fn analyzeCall(
                 uncasted_args,
                 is_comptime_call,
                 &should_memoize,
-                memoized_call_key,
-                func_ty_info.param_types,
+                mod.typeToFunc(func_ty).?.param_types,
                 func,
                 &has_comptime_args,
             ) catch |err| switch (err) {
@@ -6934,8 +6931,7 @@ fn analyzeCall(
                         uncasted_args,
                         is_comptime_call,
                         &should_memoize,
-                        memoized_call_key,
-                        func_ty_info.param_types,
+                        mod.typeToFunc(func_ty).?.param_types,
                         func,
                         &has_comptime_args,
                     );
@@ -6988,9 +6984,19 @@ fn analyzeCall(
         // bug generating invalid LLVM IR.
         const res2: Air.Inst.Ref = res2: {
             if (should_memoize and is_comptime_call) {
-                if (mod.memoized_calls.getContext(memoized_call_key, .{ .module = mod })) |result| {
-                    break :res2 try sema.addConstant(fn_ret_ty, result.val);
+                const gop = try mod.memoized_calls.getOrPutContext(
+                    gpa,
+                    memoized_call_key,
+                    .{ .args = &mod.memoized_call_args },
+                );
+                if (gop.found_existing) {
+                    // We need to use the original memoized error set instead of fn_ret_ty.
+                    const result = gop.value_ptr.*;
+                    assert(result != .none); // recursive memoization?
+                    break :res2 try sema.addConstant(mod.intern_pool.typeOf(result).toType(), result.toValue());
                 }
+                gop.value_ptr.* = .none;
+                delete_memoized_call_key = false;
             }
 
             const new_func_resolved_ty = try mod.funcType(new_fn_info);
@@ -7049,26 +7055,10 @@ fn analyzeCall(
 
             if (should_memoize and is_comptime_call) {
                 const result_val = try sema.resolveConstMaybeUndefVal(block, .unneeded, result, "");
-
-                // TODO: check whether any external comptime memory was mutated by the
-                // comptime function call. If so, then do not memoize the call here.
-                // TODO: re-evaluate whether memoized_calls needs its own arena. I think
-                // it should be fine to use the Decl arena for the function.
-                {
-                    var arena_allocator = std.heap.ArenaAllocator.init(gpa);
-                    errdefer arena_allocator.deinit();
-                    const arena = arena_allocator.allocator();
-
-                    for (memoized_call_key.args) |*arg| {
-                        arg.* = try arg.*.copy(arena);
-                    }
-
-                    try mod.memoized_calls.putContext(gpa, memoized_call_key, .{
-                        .val = try result_val.copy(arena),
-                        .arena = arena_allocator.state,
-                    }, .{ .module = mod });
-                    delete_memoized_call_key = false;
-                }
+                mod.memoized_calls.getPtrContext(
+                    memoized_call_key,
+                    .{ .args = &mod.memoized_call_args },
+                ).?.* = try result_val.intern(fn_ret_ty, mod);
             }
 
             break :res2 result;
@@ -7214,11 +7204,11 @@ fn analyzeInlineCallArg(
     uncasted_args: []const Air.Inst.Ref,
     is_comptime_call: bool,
     should_memoize: *bool,
-    memoized_call_key: Module.MemoizedCall.Key,
     raw_param_types: []const InternPool.Index,
     func_inst: Air.Inst.Ref,
     has_comptime_args: *bool,
 ) !void {
+    const mod = sema.mod;
     const zir_tags = sema.code.instructions.items(.tag);
     switch (zir_tags[inst]) {
         .param_comptime, .param_anytype_comptime => has_comptime_args.* = true,
@@ -7276,11 +7266,8 @@ fn analyzeInlineCallArg(
                         try sema.resolveLazyValue(arg_val);
                     },
                 }
-                should_memoize.* = should_memoize.* and !arg_val.canMutateComptimeVarState(sema.mod);
-                memoized_call_key.args[arg_i.*] = .{
-                    .ty = param_ty.toType(),
-                    .val = arg_val,
-                };
+                should_memoize.* = should_memoize.* and !arg_val.canMutateComptimeVarState(mod);
+                mod.memoized_call_args.appendAssumeCapacity(try arg_val.intern(param_ty.toType(), mod));
             } else {
                 sema.inst_map.putAssumeCapacityNoClobber(inst, casted_arg);
             }
@@ -7315,11 +7302,8 @@ fn analyzeInlineCallArg(
                         try sema.resolveLazyValue(arg_val);
                     },
                 }
-                should_memoize.* = should_memoize.* and !arg_val.canMutateComptimeVarState(sema.mod);
-                memoized_call_key.args[arg_i.*] = .{
-                    .ty = sema.typeOf(uncasted_arg),
-                    .val = arg_val,
-                };
+                should_memoize.* = should_memoize.* and !arg_val.canMutateComptimeVarState(mod);
+                mod.memoized_call_args.appendAssumeCapacity(try arg_val.intern(sema.typeOf(uncasted_arg), mod));
             } else {
                 if (zir_tags[inst] == .param_anytype_comptime) {
                     _ = try sema.resolveConstMaybeUndefVal(arg_block, arg_src, uncasted_arg, "parameter is comptime");
@@ -8279,7 +8263,7 @@ fn zirEnumToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
     const int_tag_ty = try enum_tag_ty.intTagType(mod);
 
     if (try sema.typeHasOnePossibleValue(enum_tag_ty)) |opv| {
-        return sema.addConstant(int_tag_ty, opv);
+        return sema.addConstant(int_tag_ty, try mod.getCoerced(opv, int_tag_ty));
     }
 
     if (try sema.resolveMaybeUndefVal(enum_tag)) |enum_tag_val| {
@@ -8310,7 +8294,10 @@ fn zirIntToEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
         if (dest_ty.isNonexhaustiveEnum(mod)) {
             const int_tag_ty = try dest_ty.intTagType(mod);
             if (try sema.intFitsInType(int_val, int_tag_ty, null)) {
-                return sema.addConstant(dest_ty, int_val);
+                return sema.addConstant(dest_ty, (try mod.intern(.{ .enum_tag = .{
+                    .ty = dest_ty.toIntern(),
+                    .int = int_val.toIntern(),
+                } })).toValue());
             }
             const msg = msg: {
                 const msg = try sema.errMsg(
@@ -8657,8 +8644,10 @@ fn analyzeErrUnionCode(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air
     const result_ty = operand_ty.errorUnionSet(mod);
 
     if (try sema.resolveDefinedValue(block, src, operand)) |val| {
-        assert(val.getError(mod) != null);
-        return sema.addConstant(result_ty, val);
+        return sema.addConstant(result_ty, (try mod.intern(.{ .err = .{
+            .ty = result_ty.toIntern(),
+            .name = mod.intern_pool.indexToKey(val.toIntern()).error_union.val.err_name,
+        } })).toValue());
     }
 
     try sema.requireRuntimeBlock(block, src, null);
@@ -10737,7 +10726,6 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
                         block,
                         &range_set,
                         item_ref,
-                        operand_ty,
                         src_node_offset,
                         .{ .scalar = scalar_i },
                     );
@@ -10760,7 +10748,6 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
                             block,
                             &range_set,
                             item_ref,
-                            operand_ty,
                             src_node_offset,
                             .{ .multi = .{ .prong = multi_i, .item = @intCast(u32, item_i) } },
                         );
@@ -10778,7 +10765,6 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
                             &range_set,
                             item_first,
                             item_last,
-                            operand_ty,
                             src_node_offset,
                             .{ .range = .{ .prong = multi_i, .item = range_i } },
                         );
@@ -10792,7 +10778,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
                 if (operand_ty.zigTypeTag(mod) == .Int) {
                     const min_int = try operand_ty.minInt(mod, operand_ty);
                     const max_int = try operand_ty.maxInt(mod, operand_ty);
-                    if (try range_set.spans(min_int, max_int, operand_ty)) {
+                    if (try range_set.spans(min_int.toIntern(), max_int.toIntern())) {
                         if (special_prong == .@"else") {
                             return sema.fail(
                                 block,
@@ -10894,11 +10880,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
                 );
             }
 
-            var seen_values = ValueSrcMap.initContext(gpa, .{
-                .ty = operand_ty,
-                .mod = mod,
-            });
-            defer seen_values.deinit();
+            var seen_values = ValueSrcMap{};
+            defer seen_values.deinit(gpa);
 
             var extra_index: usize = special.end;
             {
@@ -11664,10 +11647,10 @@ const RangeSetUnhandledIterator = struct {
                 it.cur = try it.sema.intAddScalar(it.cur, try it.sema.mod.intValue(it.ty, 1), it.ty);
             }
             it.first = false;
-            if (it.cur.compareScalar(.lt, it.ranges[it.range_i].first, it.ty, it.sema.mod)) {
+            if (it.cur.compareScalar(.lt, it.ranges[it.range_i].first.toValue(), it.ty, it.sema.mod)) {
                 return it.cur;
             }
-            it.cur = it.ranges[it.range_i].last;
+            it.cur = it.ranges[it.range_i].last.toValue();
         }
         if (!it.first) {
             it.cur = try it.sema.intAddScalar(it.cur, try it.sema.mod.intValue(it.ty, 1), it.ty);
@@ -11687,16 +11670,15 @@ fn resolveSwitchItemVal(
     switch_node_offset: i32,
     switch_prong_src: Module.SwitchProngSrc,
     range_expand: Module.SwitchProngSrc.RangeExpand,
-) CompileError!TypedValue {
+) CompileError!InternPool.Index {
     const mod = sema.mod;
     const item = try sema.resolveInst(item_ref);
-    const item_ty = sema.typeOf(item);
     // Constructing a LazySrcLoc is costly because we only have the switch AST node.
     // Only if we know for sure we need to report a compile error do we resolve the
     // full source locations.
     if (sema.resolveConstValue(block, .unneeded, item, "")) |val| {
         try sema.resolveLazyValue(val);
-        return TypedValue{ .ty = item_ty, .val = val };
+        return val.toIntern();
     } else |err| switch (err) {
         error.NeededSourceLocation => {
             const src = switch_prong_src.resolve(mod, sema.mod.declPtr(block.src_decl), switch_node_offset, range_expand);
@@ -11713,18 +11695,17 @@ fn validateSwitchRange(
     range_set: *RangeSet,
     first_ref: Zir.Inst.Ref,
     last_ref: Zir.Inst.Ref,
-    operand_ty: Type,
     src_node_offset: i32,
     switch_prong_src: Module.SwitchProngSrc,
 ) CompileError!void {
     const mod = sema.mod;
-    const first_val = (try sema.resolveSwitchItemVal(block, first_ref, src_node_offset, switch_prong_src, .first)).val;
-    const last_val = (try sema.resolveSwitchItemVal(block, last_ref, src_node_offset, switch_prong_src, .last)).val;
-    if (first_val.compareScalar(.gt, last_val, operand_ty, mod)) {
+    const first = try sema.resolveSwitchItemVal(block, first_ref, src_node_offset, switch_prong_src, .first);
+    const last = try sema.resolveSwitchItemVal(block, last_ref, src_node_offset, switch_prong_src, .last);
+    if (first.toValue().compareScalar(.gt, last.toValue(), mod.intern_pool.typeOf(first).toType(), mod)) {
         const src = switch_prong_src.resolve(mod, mod.declPtr(block.src_decl), src_node_offset, .first);
         return sema.fail(block, src, "range start value is greater than the end value", .{});
     }
-    const maybe_prev_src = try range_set.add(first_val, last_val, operand_ty, switch_prong_src);
+    const maybe_prev_src = try range_set.add(first, last, switch_prong_src);
     return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);
 }
 
@@ -11733,12 +11714,11 @@ fn validateSwitchItem(
     block: *Block,
     range_set: *RangeSet,
     item_ref: Zir.Inst.Ref,
-    operand_ty: Type,
     src_node_offset: i32,
     switch_prong_src: Module.SwitchProngSrc,
 ) CompileError!void {
-    const item_val = (try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none)).val;
-    const maybe_prev_src = try range_set.add(item_val, item_val, operand_ty, switch_prong_src);
+    const item = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none);
+    const maybe_prev_src = try range_set.add(item, item, switch_prong_src);
     return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);
 }
 
@@ -11751,9 +11731,11 @@ fn validateSwitchItemEnum(
     src_node_offset: i32,
     switch_prong_src: Module.SwitchProngSrc,
 ) CompileError!void {
-    const item_tv = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none);
-    const field_index = item_tv.ty.enumTagFieldIndex(item_tv.val, sema.mod) orelse {
-        const maybe_prev_src = try range_set.add(item_tv.val, item_tv.val, item_tv.ty, switch_prong_src);
+    const ip = &sema.mod.intern_pool;
+    const item = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none);
+    const int = ip.indexToKey(item).enum_tag.int;
+    const field_index = ip.indexToKey(ip.typeOf(item)).enum_type.tagValueIndex(ip, int) orelse {
+        const maybe_prev_src = try range_set.add(int, int, switch_prong_src);
         return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);
     };
     const maybe_prev_src = seen_fields[field_index];
@@ -11770,9 +11752,9 @@ fn validateSwitchItemError(
     switch_prong_src: Module.SwitchProngSrc,
 ) CompileError!void {
     const ip = &sema.mod.intern_pool;
-    const item_tv = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none);
+    const item = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none);
     // TODO: Do i need to typecheck here?
-    const error_name = ip.stringToSlice(ip.indexToKey(item_tv.val.toIntern()).err.name);
+    const error_name = ip.stringToSlice(ip.indexToKey(item).err.name);
     const maybe_prev_src = if (try seen_errors.fetchPut(error_name, switch_prong_src)) |prev|
         prev.value
     else
@@ -11822,8 +11804,8 @@ fn validateSwitchItemBool(
     switch_prong_src: Module.SwitchProngSrc,
 ) CompileError!void {
     const mod = sema.mod;
-    const item_val = (try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none)).val;
-    if (item_val.toBool(mod)) {
+    const item = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none);
+    if (item.toValue().toBool()) {
         true_count.* += 1;
     } else {
         false_count.* += 1;
@@ -11835,7 +11817,7 @@ fn validateSwitchItemBool(
     }
 }
 
-const ValueSrcMap = std.HashMap(Value, Module.SwitchProngSrc, Value.HashContext, std.hash_map.default_max_load_percentage);
+const ValueSrcMap = std.AutoHashMapUnmanaged(InternPool.Index, Module.SwitchProngSrc);
 
 fn validateSwitchItemSparse(
     sema: *Sema,
@@ -11845,8 +11827,8 @@ fn validateSwitchItemSparse(
     src_node_offset: i32,
     switch_prong_src: Module.SwitchProngSrc,
 ) CompileError!void {
-    const item_val = (try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none)).val;
-    const kv = (try seen_values.fetchPut(item_val, switch_prong_src)) orelse return;
+    const item = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none);
+    const kv = (try seen_values.fetchPut(sema.gpa, item, switch_prong_src)) orelse return;
     return sema.validateSwitchDupe(block, kv.value, switch_prong_src, src_node_offset);
 }
 
@@ -13047,8 +13029,6 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
     const lhs_len = try sema.usizeCast(block, lhs_src, lhs_info.len);
 
     if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| {
-        const final_len_including_sent = result_len + @boolToInt(lhs_info.sentinel != null);
-
         const lhs_sub_val = if (lhs_ty.isSinglePointer(mod))
             (try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty)).?
         else
@@ -13065,7 +13045,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                 } });
             }
 
-            const element_vals = try sema.arena.alloc(InternPool.Index, final_len_including_sent);
+            const element_vals = try sema.arena.alloc(InternPool.Index, result_len);
             var elem_i: usize = 0;
             while (elem_i < result_len) {
                 var lhs_i: usize = 0;
@@ -13075,9 +13055,6 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                     elem_i += 1;
                 }
             }
-            if (lhs_info.sentinel) |sent_val| {
-                element_vals[result_len] = sent_val.toIntern();
-            }
             break :v try mod.intern(.{ .aggregate = .{
                 .ty = result_ty.toIntern(),
                 .storage = .{ .elems = element_vals },
@@ -14896,13 +14873,18 @@ fn analyzeArithmetic(
                     .ComptimeInt, .Int => try mod.intValue(scalar_type, 0),
                     else => unreachable,
                 };
+                const scalar_one = switch (scalar_tag) {
+                    .ComptimeFloat, .Float => try mod.floatValue(scalar_type, 1.0),
+                    .ComptimeInt, .Int => try mod.intValue(scalar_type, 1),
+                    else => unreachable,
+                };
                 if (maybe_lhs_val) |lhs_val| {
                     if (!lhs_val.isUndef(mod)) {
                         if (try lhs_val.compareAllWithZeroAdvanced(.eq, sema)) {
                             const zero_val = try sema.splat(resolved_type, scalar_zero);
                             return sema.addConstant(resolved_type, zero_val);
                         }
-                        if (try sema.compareAll(lhs_val, .eq, try mod.intValue(resolved_type, 1), resolved_type)) {
+                        if (try sema.compareAll(lhs_val, .eq, try sema.splat(resolved_type, scalar_one), resolved_type)) {
                             return casted_rhs;
                         }
                     }
@@ -14916,7 +14898,7 @@ fn analyzeArithmetic(
                         const zero_val = try sema.splat(resolved_type, scalar_zero);
                         return sema.addConstant(resolved_type, zero_val);
                     }
-                    if (try sema.compareAll(rhs_val, .eq, try mod.intValue(resolved_type, 1), resolved_type)) {
+                    if (try sema.compareAll(rhs_val, .eq, try sema.splat(resolved_type, scalar_one), resolved_type)) {
                         return casted_lhs;
                     }
                     if (maybe_lhs_val) |lhs_val| {
@@ -15524,7 +15506,7 @@ fn cmpSelf(
             } else {
                 if (resolved_type.zigTypeTag(mod) == .Bool) {
                     // We can lower bool eq/neq more efficiently.
-                    return sema.runtimeBoolCmp(block, src, op, casted_rhs, lhs_val.toBool(mod), rhs_src);
+                    return sema.runtimeBoolCmp(block, src, op, casted_rhs, lhs_val.toBool(), rhs_src);
                 }
                 break :src rhs_src;
             }
@@ -15534,7 +15516,7 @@ fn cmpSelf(
             if (resolved_type.zigTypeTag(mod) == .Bool) {
                 if (try sema.resolveMaybeUndefVal(casted_rhs)) |rhs_val| {
                     if (rhs_val.isUndef(mod)) return sema.addConstUndef(Type.bool);
-                    return sema.runtimeBoolCmp(block, src, op, casted_lhs, rhs_val.toBool(mod), lhs_src);
+                    return sema.runtimeBoolCmp(block, src, op, casted_lhs, rhs_val.toBool(), lhs_src);
                 }
             }
             break :src lhs_src;
@@ -15840,6 +15822,7 @@ fn zirBuiltinSrc(
         break :blk try mod.intern(.{ .ptr = .{
             .ty = .slice_const_u8_sentinel_0_type,
             .addr = .{ .decl = new_decl },
+            .len = (try mod.intValue(Type.usize, name.len)).toIntern(),
         } });
     };
 
@@ -15864,6 +15847,7 @@ fn zirBuiltinSrc(
         break :blk try mod.intern(.{ .ptr = .{
             .ty = .slice_const_u8_sentinel_0_type,
             .addr = .{ .decl = new_decl },
+            .len = (try mod.intValue(Type.usize, name.len)).toIntern(),
         } });
     };
 
@@ -16314,6 +16298,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                 break :v try mod.intern(.{ .ptr = .{
                     .ty = slice_errors_ty.toIntern(),
                     .addr = .{ .decl = new_decl },
+                    .len = (try mod.intValue(Type.usize, vals.len)).toIntern(),
                 } });
             } else .none;
             const errors_val = try mod.intern(.{ .opt = .{
@@ -16438,6 +16423,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                         .is_const = true,
                     })).toIntern(),
                     .addr = .{ .decl = new_decl },
+                    .len = (try mod.intValue(Type.usize, enum_field_vals.len)).toIntern(),
                 } });
             };
 
@@ -17141,7 +17127,7 @@ fn zirBoolNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
     if (try sema.resolveMaybeUndefVal(operand)) |val| {
         return if (val.isUndef(mod))
             sema.addConstUndef(Type.bool)
-        else if (val.toBool(mod))
+        else if (val.toBool())
             Air.Inst.Ref.bool_false
         else
             Air.Inst.Ref.bool_true;
@@ -17169,9 +17155,9 @@ fn zirBoolBr(
     const gpa = sema.gpa;
 
     if (try sema.resolveDefinedValue(parent_block, lhs_src, lhs)) |lhs_val| {
-        if (is_bool_or and lhs_val.toBool(mod)) {
+        if (is_bool_or and lhs_val.toBool()) {
             return Air.Inst.Ref.bool_true;
-        } else if (!is_bool_or and !lhs_val.toBool(mod)) {
+        } else if (!is_bool_or and !lhs_val.toBool()) {
             return Air.Inst.Ref.bool_false;
         }
         // comptime-known left-hand side. No need for a block here; the result
@@ -17215,9 +17201,9 @@ fn zirBoolBr(
     const result = sema.finishCondBr(parent_block, &child_block, &then_block, &else_block, lhs, block_inst);
     if (!sema.typeOf(rhs_result).isNoReturn(mod)) {
         if (try sema.resolveDefinedValue(rhs_block, sema.src, rhs_result)) |rhs_val| {
-            if (is_bool_or and rhs_val.toBool(mod)) {
+            if (is_bool_or and rhs_val.toBool()) {
                 return Air.Inst.Ref.bool_true;
-            } else if (!is_bool_or and !rhs_val.toBool(mod)) {
+            } else if (!is_bool_or and !rhs_val.toBool()) {
                 return Air.Inst.Ref.bool_false;
             }
         }
@@ -17371,7 +17357,7 @@ fn zirCondbr(
     const cond = try sema.coerce(parent_block, Type.bool, uncasted_cond, cond_src);
 
     if (try sema.resolveDefinedValue(parent_block, cond_src, cond)) |cond_val| {
-        const body = if (cond_val.toBool(mod)) then_body else else_body;
+        const body = if (cond_val.toBool()) then_body else else_body;
 
         try sema.maybeErrorUnwrapCondbr(parent_block, body, extra.data.condition, cond_src);
         // We use `analyzeBodyInner` since we want to propagate any possible
@@ -17444,7 +17430,7 @@ fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!
     const is_non_err = try sema.analyzeIsNonErrComptimeOnly(parent_block, operand_src, err_union);
     if (is_non_err != .none) {
         const is_non_err_val = (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)).?;
-        if (is_non_err_val.toBool(mod)) {
+        if (is_non_err_val.toBool()) {
             return sema.analyzeErrUnionPayload(parent_block, src, err_union_ty, err_union, operand_src, false);
         }
         // We can analyze the body directly in the parent block because we know there are
@@ -17491,7 +17477,7 @@ fn zirTryPtr(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileErr
     const is_non_err = try sema.analyzeIsNonErrComptimeOnly(parent_block, operand_src, err_union);
     if (is_non_err != .none) {
         const is_non_err_val = (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)).?;
-        if (is_non_err_val.toBool(mod)) {
+        if (is_non_err_val.toBool()) {
             return sema.analyzeErrUnionPayloadPtr(parent_block, src, operand, false, false);
         }
         // We can analyze the body directly in the parent block because we know there are
@@ -18858,7 +18844,7 @@ fn zirBoolToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
     const operand = try sema.resolveInst(inst_data.operand);
     if (try sema.resolveMaybeUndefVal(operand)) |val| {
         if (val.isUndef(mod)) return sema.addConstUndef(Type.u1);
-        if (val.toBool(mod)) return sema.addConstant(Type.u1, try mod.intValue(Type.u1, 1));
+        if (val.toBool()) return sema.addConstant(Type.u1, try mod.intValue(Type.u1, 1));
         return sema.addConstant(Type.u1, try mod.intValue(Type.u1, 0));
     }
     return block.addUnOp(.bool_to_int, operand);
@@ -19171,12 +19157,12 @@ fn zirReify(
 
             const ty = try mod.ptrType(.{
                 .size = ptr_size,
-                .is_const = is_const_val.toBool(mod),
-                .is_volatile = is_volatile_val.toBool(mod),
+                .is_const = is_const_val.toBool(),
+                .is_volatile = is_volatile_val.toBool(),
                 .alignment = abi_align,
                 .address_space = mod.toEnum(std.builtin.AddressSpace, address_space_val),
                 .elem_type = elem_ty.toIntern(),
-                .is_allowzero = is_allowzero_val.toBool(mod),
+                .is_allowzero = is_allowzero_val.toBool(),
                 .sentinel = actual_sentinel,
             });
             return sema.addType(ty);
@@ -19267,7 +19253,7 @@ fn zirReify(
                 return sema.fail(block, src, "non-packed struct does not support backing integer type", .{});
             }
 
-            return try sema.reifyStruct(block, inst, src, layout, backing_integer_val, fields_val, name_strategy, is_tuple_val.toBool(mod));
+            return try sema.reifyStruct(block, inst, src, layout, backing_integer_val, fields_val, name_strategy, is_tuple_val.toBool());
         },
         .Enum => {
             const fields = ip.typeOf(union_val.val).toType().structFields(mod);
@@ -19305,7 +19291,7 @@ fn zirReify(
                 .namespace = .none,
                 .fields_len = fields_len,
                 .has_values = true,
-                .tag_mode = if (!is_exhaustive_val.toBool(mod))
+                .tag_mode = if (!is_exhaustive_val.toBool())
                     .nonexhaustive
                 else
                     .explicit,
@@ -19619,12 +19605,12 @@ fn zirReify(
             const return_type_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex("return_type").?);
             const params_val = try union_val.val.toValue().fieldValue(mod, fields.getIndex("params").?);
 
-            const is_generic = is_generic_val.toBool(mod);
+            const is_generic = is_generic_val.toBool();
             if (is_generic) {
                 return sema.fail(block, src, "Type.Fn.is_generic must be false for @Type", .{});
             }
 
-            const is_var_args = is_var_args_val.toBool(mod);
+            const is_var_args = is_var_args_val.toBool();
             const cc = mod.toEnum(std.builtin.CallingConvention, calling_convention_val);
             if (is_var_args and cc != .C) {
                 return sema.fail(block, src, "varargs functions must have C calling convention", .{});
@@ -19653,9 +19639,9 @@ fn zirReify(
                 const arg_val = arg.castTag(.aggregate).?.data;
                 // TODO use reflection instead of magic numbers here
                 // is_generic: bool,
-                const arg_is_generic = arg_val[0].toBool(mod);
+                const arg_is_generic = arg_val[0].toBool();
                 // is_noalias: bool,
-                const arg_is_noalias = arg_val[1].toBool(mod);
+                const arg_is_noalias = arg_val[1].toBool();
                 // type: ?type,
                 const param_type_opt_val = arg_val[2];
 
@@ -19783,9 +19769,9 @@ fn reifyStruct(
 
         if (layout == .Packed) {
             if (abi_align != 0) return sema.fail(block, src, "alignment in a packed struct field must be set to 0", .{});
-            if (is_comptime_val.toBool(mod)) return sema.fail(block, src, "packed struct fields cannot be marked comptime", .{});
+            if (is_comptime_val.toBool()) return sema.fail(block, src, "packed struct fields cannot be marked comptime", .{});
         }
-        if (layout == .Extern and is_comptime_val.toBool(mod)) {
+        if (layout == .Extern and is_comptime_val.toBool()) {
             return sema.fail(block, src, "extern struct fields cannot be marked comptime", .{});
         }
 
@@ -19827,7 +19813,7 @@ fn reifyStruct(
                 opt_val;
             break :blk try payload_val.copy(new_decl_arena_allocator);
         } else Value.@"unreachable";
-        if (is_comptime_val.toBool(mod) and default_val.toIntern() == .unreachable_value) {
+        if (is_comptime_val.toBool() and default_val.toIntern() == .unreachable_value) {
             return sema.fail(block, src, "comptime field without default initialization value", .{});
         }
 
@@ -19836,7 +19822,7 @@ fn reifyStruct(
             .ty = field_ty,
             .abi_align = abi_align,
             .default_val = default_val,
-            .is_comptime = is_comptime_val.toBool(mod),
+            .is_comptime = is_comptime_val.toBool(),
             .offset = undefined,
         };
 
@@ -20400,13 +20386,17 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
         if (!dest_ty.ptrAllowsZero(mod) and operand_val.isNull(mod)) {
             return sema.fail(block, operand_src, "null pointer casted to type '{}'", .{dest_ty.fmt(mod)});
         }
-        if (dest_ty.zigTypeTag(mod) == .Optional and sema.typeOf(ptr).zigTypeTag(mod) != .Optional) {
-            return sema.addConstant(dest_ty, (try mod.intern(.{ .opt = .{
-                .ty = dest_ty.toIntern(),
-                .val = operand_val.toIntern(),
-            } })).toValue());
-        }
-        return sema.addConstant(aligned_dest_ty, try mod.getCoerced(operand_val, aligned_dest_ty));
+        return sema.addConstant(aligned_dest_ty, try mod.getCoerced(switch (mod.intern_pool.indexToKey(operand_val.toIntern())) {
+            .undef, .ptr => operand_val,
+            .opt => |opt| switch (opt.val) {
+                .none => if (dest_ty.ptrAllowsZero(mod))
+                    Value.zero_usize
+                else
+                    return sema.fail(block, operand_src, "null pointer casted to type '{}'", .{dest_ty.fmt(mod)}),
+                else => opt.val.toValue(),
+            },
+            else => unreachable,
+        }, aligned_dest_ty));
     }
 
     try sema.requireRuntimeBlock(block, src, null);
@@ -20534,10 +20524,10 @@ fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
     if (try sema.resolveMaybeUndefValIntable(operand)) |val| {
         if (val.isUndef(mod)) return sema.addConstUndef(dest_ty);
         if (!is_vector) {
-            return sema.addConstant(
-                dest_ty,
+            return sema.addConstant(dest_ty, try mod.getCoerced(
                 try val.intTrunc(operand_ty, sema.arena, dest_info.signedness, dest_info.bits, mod),
-            );
+                dest_ty,
+            ));
         }
         const elems = try sema.arena.alloc(InternPool.Index, operand_ty.vectorLen(mod));
         for (elems, 0..) |*elem, i| {
@@ -21410,7 +21400,10 @@ fn zirCmpxchg(
 
     // special case zero bit types
     if ((try sema.typeHasOnePossibleValue(elem_ty)) != null) {
-        return sema.addConstant(result_ty, Value.null);
+        return sema.addConstant(result_ty, (try mod.intern(.{ .opt = .{
+            .ty = result_ty.toIntern(),
+            .val = .none,
+        } })).toValue());
     }
 
     const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: {
@@ -21633,8 +21626,7 @@ fn analyzeShuffle(
         .{ b_len, b_src, b_ty },
     };
 
-    var i: usize = 0;
-    while (i < mask_len) : (i += 1) {
+    for (0..@intCast(usize, mask_len)) |i| {
         const elem = try mask.elemValue(sema.mod, i);
         if (elem.isUndef(mod)) continue;
         const int = elem.toSignedInt(mod);
@@ -21670,7 +21662,7 @@ fn analyzeShuffle(
     if (try sema.resolveMaybeUndefVal(a)) |a_val| {
         if (try sema.resolveMaybeUndefVal(b)) |b_val| {
             const values = try sema.arena.alloc(InternPool.Index, mask_len);
-            for (values) |*value| {
+            for (values, 0..) |*value, i| {
                 const mask_elem_val = try mask.elemValue(sema.mod, i);
                 if (mask_elem_val.isUndef(mod)) {
                     value.* = try mod.intern(.{ .undef = elem_ty.toIntern() });
@@ -21698,11 +21690,10 @@ fn analyzeShuffle(
         const max_len = try sema.usizeCast(block, max_src, std.math.max(a_len, b_len));
 
         const expand_mask_values = try sema.arena.alloc(InternPool.Index, max_len);
-        i = 0;
-        while (i < min_len) : (i += 1) {
+        for (@intCast(usize, 0)..@intCast(usize, min_len)) |i| {
             expand_mask_values[i] = (try mod.intValue(Type.comptime_int, i)).toIntern();
         }
-        while (i < max_len) : (i += 1) {
+        for (@intCast(usize, min_len)..@intCast(usize, max_len)) |i| {
             expand_mask_values[i] = (try mod.intValue(Type.comptime_int, -1)).toIntern();
         }
         const expand_mask = try mod.intern(.{ .aggregate = .{
@@ -21783,7 +21774,7 @@ fn zirSelect(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) C
                 const elems = try sema.gpa.alloc(InternPool.Index, vec_len);
                 for (elems, 0..) |*elem, i| {
                     const pred_elem_val = try pred_val.elemValue(mod, i);
-                    const should_choose_a = pred_elem_val.toBool(mod);
+                    const should_choose_a = pred_elem_val.toBool();
                     elem.* = try (try (if (should_choose_a) a_val else b_val).elemValue(mod, i)).intern(elem_ty, mod);
                 }
 
@@ -22853,15 +22844,15 @@ fn zirVarExtended(
         else
             uncasted_init;
 
-        break :blk (try sema.resolveMaybeUndefVal(init)) orelse
-            return sema.failWithNeededComptime(block, init_src, "container level variable initializers must be comptime-known");
-    } else Value.@"unreachable";
+        break :blk ((try sema.resolveMaybeUndefVal(init)) orelse
+            return sema.failWithNeededComptime(block, init_src, "container level variable initializers must be comptime-known")).toIntern();
+    } else .none;
 
     try sema.validateVarType(block, ty_src, var_ty, small.is_extern);
 
     return sema.addConstant(var_ty, (try mod.intern(.{ .variable = .{
         .ty = var_ty.toIntern(),
-        .init = init_val.toIntern(),
+        .init = init_val,
         .decl = sema.owner_decl_index,
         .lib_name = if (lib_name) |lname| (try mod.intern_pool.getOrPutString(
             sema.gpa,
@@ -23284,7 +23275,7 @@ fn resolveExternOptions(
         .name = name,
         .library_name = library_name,
         .linkage = linkage,
-        .is_thread_local = is_thread_local_val.toBool(mod),
+        .is_thread_local = is_thread_local_val.toBool(),
     };
 }
 
@@ -26190,7 +26181,7 @@ fn coerceExtra(
                             .addr = .{ .int = (if (dest_info.@"align" != 0)
                                 try mod.intValue(Type.usize, dest_info.@"align")
                             else
-                                try dest_info.pointee_type.lazyAbiAlignment(mod)).toIntern() },
+                                try mod.getCoerced(try dest_info.pointee_type.lazyAbiAlignment(mod), Type.usize)).toIntern() },
                             .len = (try mod.intValue(Type.usize, 0)).toIntern(),
                         } })).toValue());
                     }
@@ -27785,7 +27776,7 @@ fn beginComptimePtrMutation(
                         const payload = try arena.create(Value.Payload.SubValue);
                         payload.* = .{
                             .base = .{ .tag = .eu_payload },
-                            .data = Value.undef,
+                            .data = (try mod.intern(.{ .undef = payload_ty.toIntern() })).toValue(),
                         };
 
                         val_ptr.* = Value.initPayload(&payload.base);
@@ -27824,7 +27815,7 @@ fn beginComptimePtrMutation(
                             const payload = try arena.create(Value.Payload.SubValue);
                             payload.* = .{
                                 .base = .{ .tag = .opt_payload },
-                                .data = Value.undef,
+                                .data = (try mod.intern(.{ .undef = payload_ty.toIntern() })).toValue(),
                             };
 
                             val_ptr.* = Value.initPayload(&payload.base);
@@ -27898,30 +27889,6 @@ fn beginComptimePtrMutation(
                         }
 
                         switch (val_ptr.ip_index) {
-                            .undef => {
-                                // An array has been initialized to undefined at comptime and now we
-                                // are for the first time setting an element. We must change the representation
-                                // of the array from `undef` to `array`.
-                                const arena = parent.beginArena(sema.mod);
-                                defer parent.finishArena(sema.mod);
-
-                                const array_len_including_sentinel =
-                                    try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel(mod));
-                                const elems = try arena.alloc(Value, array_len_including_sentinel);
-                                @memset(elems, Value.undef);
-
-                                val_ptr.* = try Value.Tag.aggregate.create(arena, elems);
-
-                                return beginComptimePtrMutationInner(
-                                    sema,
-                                    block,
-                                    src,
-                                    elem_ty,
-                                    &elems[elem_ptr.index],
-                                    ptr_elem_ty,
-                                    parent.mut_decl,
-                                );
-                            },
                             .none => switch (val_ptr.tag()) {
                                 .bytes => {
                                     // An array is memory-optimized to store a slice of bytes, but we are about
@@ -27999,7 +27966,33 @@ fn beginComptimePtrMutation(
 
                                 else => unreachable,
                             },
-                            else => unreachable,
+                            else => switch (mod.intern_pool.indexToKey(val_ptr.toIntern())) {
+                                .undef => {
+                                    // An array has been initialized to undefined at comptime and now we
+                                    // are for the first time setting an element. We must change the representation
+                                    // of the array from `undef` to `array`.
+                                    const arena = parent.beginArena(sema.mod);
+                                    defer parent.finishArena(sema.mod);
+
+                                    const array_len_including_sentinel =
+                                        try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel(mod));
+                                    const elems = try arena.alloc(Value, array_len_including_sentinel);
+                                    @memset(elems, (try mod.intern(.{ .undef = elem_ty.toIntern() })).toValue());
+
+                                    val_ptr.* = try Value.Tag.aggregate.create(arena, elems);
+
+                                    return beginComptimePtrMutationInner(
+                                        sema,
+                                        block,
+                                        src,
+                                        elem_ty,
+                                        &elems[elem_ptr.index],
+                                        ptr_elem_ty,
+                                        parent.mut_decl,
+                                    );
+                                },
+                                else => unreachable,
+                            },
                         }
                     },
                     else => {
@@ -28052,83 +28045,6 @@ fn beginComptimePtrMutation(
             var parent = try sema.beginComptimePtrMutation(block, src, field_ptr.base.toValue(), base_child_ty);
             switch (parent.pointee) {
                 .direct => |val_ptr| switch (val_ptr.ip_index) {
-                    .undef => {
-                        // A struct or union has been initialized to undefined at comptime and now we
-                        // are for the first time setting a field. We must change the representation
-                        // of the struct/union from `undef` to `struct`/`union`.
-                        const arena = parent.beginArena(sema.mod);
-                        defer parent.finishArena(sema.mod);
-
-                        switch (parent.ty.zigTypeTag(mod)) {
-                            .Struct => {
-                                const fields = try arena.alloc(Value, parent.ty.structFieldCount(mod));
-                                @memset(fields, Value.undef);
-
-                                val_ptr.* = try Value.Tag.aggregate.create(arena, fields);
-
-                                return beginComptimePtrMutationInner(
-                                    sema,
-                                    block,
-                                    src,
-                                    parent.ty.structFieldType(field_index, mod),
-                                    &fields[field_index],
-                                    ptr_elem_ty,
-                                    parent.mut_decl,
-                                );
-                            },
-                            .Union => {
-                                const payload = try arena.create(Value.Payload.Union);
-                                const tag_ty = parent.ty.unionTagTypeHypothetical(mod);
-                                payload.* = .{ .data = .{
-                                    .tag = try mod.enumValueFieldIndex(tag_ty, field_index),
-                                    .val = Value.undef,
-                                } };
-
-                                val_ptr.* = Value.initPayload(&payload.base);
-
-                                return beginComptimePtrMutationInner(
-                                    sema,
-                                    block,
-                                    src,
-                                    parent.ty.structFieldType(field_index, mod),
-                                    &payload.data.val,
-                                    ptr_elem_ty,
-                                    parent.mut_decl,
-                                );
-                            },
-                            .Pointer => {
-                                assert(parent.ty.isSlice(mod));
-                                val_ptr.* = try Value.Tag.slice.create(arena, .{
-                                    .ptr = Value.undef,
-                                    .len = Value.undef,
-                                });
-
-                                switch (field_index) {
-                                    Value.slice_ptr_index => return beginComptimePtrMutationInner(
-                                        sema,
-                                        block,
-                                        src,
-                                        parent.ty.slicePtrFieldType(mod),
-                                        &val_ptr.castTag(.slice).?.data.ptr,
-                                        ptr_elem_ty,
-                                        parent.mut_decl,
-                                    ),
-                                    Value.slice_len_index => return beginComptimePtrMutationInner(
-                                        sema,
-                                        block,
-                                        src,
-                                        Type.usize,
-                                        &val_ptr.castTag(.slice).?.data.len,
-                                        ptr_elem_ty,
-                                        parent.mut_decl,
-                                    ),
-
-                                    else => unreachable,
-                                }
-                            },
-                            else => unreachable,
-                        }
-                    },
                     .empty_struct => {
                         const duped = try sema.arena.create(Value);
                         duped.* = val_ptr.*;
@@ -28210,10 +28126,92 @@ fn beginComptimePtrMutation(
 
                             else => unreachable,
                         },
+                        else => unreachable,
+                    },
+                    else => switch (mod.intern_pool.indexToKey(val_ptr.toIntern())) {
+                        .undef => {
+                            // A struct or union has been initialized to undefined at comptime and now we
+                            // are for the first time setting a field. We must change the representation
+                            // of the struct/union from `undef` to `struct`/`union`.
+                            const arena = parent.beginArena(sema.mod);
+                            defer parent.finishArena(sema.mod);
+
+                            switch (parent.ty.zigTypeTag(mod)) {
+                                .Struct => {
+                                    const fields = try arena.alloc(Value, parent.ty.structFieldCount(mod));
+                                    for (fields, 0..) |*field, i| field.* = (try mod.intern(.{
+                                        .undef = parent.ty.structFieldType(i, mod).toIntern(),
+                                    })).toValue();
+
+                                    val_ptr.* = try Value.Tag.aggregate.create(arena, fields);
+
+                                    return beginComptimePtrMutationInner(
+                                        sema,
+                                        block,
+                                        src,
+                                        parent.ty.structFieldType(field_index, mod),
+                                        &fields[field_index],
+                                        ptr_elem_ty,
+                                        parent.mut_decl,
+                                    );
+                                },
+                                .Union => {
+                                    const payload = try arena.create(Value.Payload.Union);
+                                    const tag_ty = parent.ty.unionTagTypeHypothetical(mod);
+                                    const payload_ty = parent.ty.structFieldType(field_index, mod);
+                                    payload.* = .{ .data = .{
+                                        .tag = try mod.enumValueFieldIndex(tag_ty, field_index),
+                                        .val = (try mod.intern(.{ .undef = payload_ty.toIntern() })).toValue(),
+                                    } };
 
+                                    val_ptr.* = Value.initPayload(&payload.base);
+
+                                    return beginComptimePtrMutationInner(
+                                        sema,
+                                        block,
+                                        src,
+                                        payload_ty,
+                                        &payload.data.val,
+                                        ptr_elem_ty,
+                                        parent.mut_decl,
+                                    );
+                                },
+                                .Pointer => {
+                                    assert(parent.ty.isSlice(mod));
+                                    const ptr_ty = parent.ty.slicePtrFieldType(mod);
+                                    val_ptr.* = try Value.Tag.slice.create(arena, .{
+                                        .ptr = (try mod.intern(.{ .undef = ptr_ty.toIntern() })).toValue(),
+                                        .len = (try mod.intern(.{ .undef = .usize_type })).toValue(),
+                                    });
+
+                                    switch (field_index) {
+                                        Value.slice_ptr_index => return beginComptimePtrMutationInner(
+                                            sema,
+                                            block,
+                                            src,
+                                            ptr_ty,
+                                            &val_ptr.castTag(.slice).?.data.ptr,
+                                            ptr_elem_ty,
+                                            parent.mut_decl,
+                                        ),
+                                        Value.slice_len_index => return beginComptimePtrMutationInner(
+                                            sema,
+                                            block,
+                                            src,
+                                            Type.usize,
+                                            &val_ptr.castTag(.slice).?.data.len,
+                                            ptr_elem_ty,
+                                            parent.mut_decl,
+                                        ),
+
+                                        else => unreachable,
+                                    }
+                                },
+                                else => unreachable,
+                            }
+                        },
                         else => unreachable,
                     },
-                    else => unreachable,
                 },
                 .reinterpret => |reinterpret| {
                     const field_offset_u64 = base_child_ty.structFieldOffset(field_index, mod);
@@ -28370,18 +28368,22 @@ fn beginComptimePtrLoad(
                         (try sema.coerceInMemoryAllowed(block, container_ty, tv.ty, false, target, src, src)) == .ok or
                         (try sema.coerceInMemoryAllowed(block, tv.ty, container_ty, false, target, src, src)) == .ok;
                     if (coerce_in_mem_ok) {
-                        const payload_val = switch (mod.intern_pool.indexToKey(tv.val.toIntern())) {
-                            .error_union => |error_union| switch (error_union.val) {
-                                .err_name => |err_name| return sema.fail(block, src, "attempt to unwrap error: {s}", .{mod.intern_pool.stringToSlice(err_name)}),
-                                .payload => |payload| payload,
-                            },
-                            .opt => |opt| switch (opt.val) {
-                                .none => return sema.fail(block, src, "attempt to use null value", .{}),
-                                else => opt.val,
-                            },
-                            else => unreachable,
+                        const payload_val = switch (tv.val.ip_index) {
+                            .none => tv.val.cast(Value.Payload.SubValue).?.data,
+                            .null_value => return sema.fail(block, src, "attempt to use null value", .{}),
+                            else => switch (mod.intern_pool.indexToKey(tv.val.toIntern())) {
+                                .error_union => |error_union| switch (error_union.val) {
+                                    .err_name => |err_name| return sema.fail(block, src, "attempt to unwrap error: {s}", .{mod.intern_pool.stringToSlice(err_name)}),
+                                    .payload => |payload| payload,
+                                },
+                                .opt => |opt| switch (opt.val) {
+                                    .none => return sema.fail(block, src, "attempt to use null value", .{}),
+                                    else => opt.val,
+                                },
+                                else => unreachable,
+                            }.toValue(),
                         };
-                        tv.* = TypedValue{ .ty = payload_ty, .val = payload_val.toValue() };
+                        tv.* = TypedValue{ .ty = payload_ty, .val = payload_val };
                         break :blk deref;
                     }
                 }
@@ -28960,7 +28962,7 @@ fn coerceArrayLike(
     if (in_memory_result == .ok) {
         if (try sema.resolveMaybeUndefVal(inst)) |inst_val| {
             // These types share the same comptime value representation.
-            return sema.addConstant(dest_ty, inst_val);
+            return sema.addConstant(dest_ty, try mod.getCoerced(inst_val, dest_ty));
         }
         try sema.requireRuntimeBlock(block, inst_src, null);
         return block.addBitCast(dest_ty, inst);
@@ -29024,7 +29026,7 @@ fn coerceTupleToArray(
         return sema.failWithOwnedErrorMsg(msg);
     }
 
-    const dest_elems = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLenIncludingSentinel(mod));
+    const dest_elems = try sema.usizeCast(block, dest_ty_src, dest_len);
     const element_vals = try sema.arena.alloc(InternPool.Index, dest_elems);
     const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_elems);
     const dest_elem_ty = dest_ty.childType(mod);
@@ -29430,7 +29432,7 @@ fn analyzeDeclRefInner(sema: *Sema, decl_index: Decl.Index, analyze_fn_body: boo
     const ptr_ty = try mod.ptrType(.{
         .elem_type = decl_tv.ty.toIntern(),
         .alignment = InternPool.Alignment.fromByteUnits(decl.@"align"),
-        .is_const = if (decl.val.getVariable(mod)) |variable| variable.is_const else false,
+        .is_const = if (decl.val.getVariable(mod)) |variable| variable.is_const else true,
         .address_space = decl.@"addrspace",
     });
     if (analyze_fn_body) {
@@ -29513,7 +29515,7 @@ fn analyzeLoad(
 
     if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| {
         if (try sema.pointerDeref(block, src, ptr_val, ptr_ty)) |elem_val| {
-            return sema.addConstant(elem_ty, elem_val);
+            return sema.addConstant(elem_ty, try mod.getCoerced(elem_val, elem_ty));
         }
     }
 
@@ -32610,8 +32612,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
 
     var int_tag_ty: Type = undefined;
     var enum_field_names: []InternPool.NullTerminatedString = &.{};
-    var enum_field_vals: []InternPool.Index = &.{};
-    var enum_field_vals_map: std.ArrayHashMapUnmanaged(Value, void, Value.ArrayHashContext, false) = .{};
+    var enum_field_vals: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .{};
     var explicit_tags_seen: []bool = &.{};
     var explicit_enum_info: ?InternPool.Key.EnumType = null;
     if (tag_type_ref != .none) {
@@ -32638,9 +32639,9 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
                     };
                     return sema.failWithOwnedErrorMsg(msg);
                 }
+                enum_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, fields_len);
+                try enum_field_vals.ensureTotalCapacity(sema.arena, fields_len);
             }
-            enum_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, fields_len);
-            enum_field_vals = try sema.arena.alloc(InternPool.Index, fields_len);
         } else {
             // The provided type is the enum tag type.
             union_obj.tag_ty = provided_ty;
@@ -32712,8 +32713,8 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
             break :blk try sema.resolveInst(tag_ref);
         } else .none;
 
-        if (enum_field_vals.len != 0) {
-            const copied_val = if (tag_ref != .none) blk: {
+        if (enum_field_vals.capacity() > 0) {
+            const enum_tag_val = if (tag_ref != .none) blk: {
                 const val = sema.semaUnionFieldVal(&block_scope, .unneeded, int_tag_ty, tag_ref) catch |err| switch (err) {
                     error.NeededSourceLocation => {
                         const val_src = mod.fieldSrcLoc(union_obj.owner_decl, .{
@@ -32737,16 +32738,12 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
 
                 break :blk val;
             };
-            enum_field_vals[field_i] = copied_val.toIntern();
-            const gop = enum_field_vals_map.getOrPutAssumeCapacityContext(copied_val, .{
-                .ty = int_tag_ty,
-                .mod = mod,
-            });
+            const gop = enum_field_vals.getOrPutAssumeCapacity(enum_tag_val.toIntern());
             if (gop.found_existing) {
                 const field_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = field_i }).lazy;
                 const other_field_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = gop.index }).lazy;
                 const msg = msg: {
-                    const msg = try sema.errMsg(&block_scope, field_src, "enum tag value {} already taken", .{copied_val.fmtValue(int_tag_ty, mod)});
+                    const msg = try sema.errMsg(&block_scope, field_src, "enum tag value {} already taken", .{enum_tag_val.fmtValue(int_tag_ty, mod)});
                     errdefer msg.destroy(gpa);
                     try sema.errNote(&block_scope, other_field_src, msg, "other occurrence here", .{});
                     break :msg msg;
@@ -32907,8 +32904,8 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
             };
             return sema.failWithOwnedErrorMsg(msg);
         }
-    } else if (enum_field_vals.len != 0) {
-        union_obj.tag_ty = try sema.generateUnionTagTypeNumbered(&block_scope, enum_field_names, enum_field_vals, union_obj);
+    } else if (enum_field_vals.count() > 0) {
+        union_obj.tag_ty = try sema.generateUnionTagTypeNumbered(&block_scope, enum_field_names, enum_field_vals.keys(), union_obj);
     } else {
         union_obj.tag_ty = try sema.generateUnionTagTypeSimple(&block_scope, enum_field_names, union_obj);
     }
@@ -33180,8 +33177,12 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
             .struct_type => |struct_type| {
                 const resolved_ty = try sema.resolveTypeFields(ty);
                 if (mod.structPtrUnwrap(struct_type.index)) |s| {
-                    for (s.fields.values(), 0..) |field, i| {
-                        if (field.is_comptime) continue;
+                    const field_vals = try sema.arena.alloc(InternPool.Index, s.fields.count());
+                    for (field_vals, s.fields.values(), 0..) |*field_val, field, i| {
+                        if (field.is_comptime) {
+                            field_val.* = try field.default_val.intern(field.ty, mod);
+                            continue;
+                        }
                         if (field.ty.eql(resolved_ty, sema.mod)) {
                             const msg = try Module.ErrorMsg.create(
                                 sema.gpa,
@@ -33192,24 +33193,25 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
                             try sema.addFieldErrNote(resolved_ty, i, msg, "while checking this field", .{});
                             return sema.failWithOwnedErrorMsg(msg);
                         }
-                        if ((try sema.typeHasOnePossibleValue(field.ty)) == null) {
-                            return null;
-                        }
+                        if (try sema.typeHasOnePossibleValue(field.ty)) |field_opv| {
+                            field_val.* = try field_opv.intern(field.ty, mod);
+                        } else return null;
                     }
+
+                    // In this case the struct has no runtime-known fields and
+                    // therefore has one possible value.
+                    return (try mod.intern(.{ .aggregate = .{
+                        .ty = ty.toIntern(),
+                        .storage = .{ .elems = field_vals },
+                    } })).toValue();
                 }
-                // In this case the struct has no runtime-known fields and
-                // therefore has one possible value.
 
-                // TODO: this is incorrect for structs with comptime fields, I think
-                // we should use a temporary allocator to construct an aggregate that
-                // is populated with the comptime values and then intern that value here.
-                // This TODO is repeated in the redundant implementation of
-                // one-possible-value in type.zig.
-                const empty = try mod.intern(.{ .aggregate = .{
+                // In this case the struct has no fields at all and
+                // therefore has one possible value.
+                return (try mod.intern(.{ .aggregate = .{
                     .ty = ty.toIntern(),
                     .storage = .{ .elems = &.{} },
-                } });
-                return empty.toValue();
+                } })).toValue();
             },
 
             .anon_struct_type => |tuple| {
@@ -33268,20 +33270,13 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
                 },
                 .auto, .explicit => switch (enum_type.names.len) {
                     0 => return Value.@"unreachable",
-                    1 => {
-                        if (enum_type.values.len == 0) {
-                            const only = try mod.intern(.{ .enum_tag = .{
-                                .ty = ty.toIntern(),
-                                .int = try mod.intern(.{ .int = .{
-                                    .ty = enum_type.tag_ty,
-                                    .storage = .{ .u64 = 0 },
-                                } }),
-                            } });
-                            return only.toValue();
-                        } else {
-                            return enum_type.values[0].toValue();
-                        }
-                    },
+                    1 => return try mod.getCoerced((if (enum_type.values.len == 0)
+                        try mod.intern(.{ .int = .{
+                            .ty = enum_type.tag_ty,
+                            .storage = .{ .u64 = 0 },
+                        } })
+                    else
+                        enum_type.values[0]).toValue(), ty),
                     else => return null,
                 },
             },
@@ -33427,7 +33422,7 @@ fn analyzeComptimeAlloc(
         // There will be stores before the first load, but they may be to sub-elements or
         // sub-fields. So we need to initialize with undef to allow the mechanism to expand
         // into fields/elements and have those overridden with stored values.
-        Value.undef,
+        (try sema.mod.intern(.{ .undef = var_type.toIntern() })).toValue(),
         alignment,
     );
     const decl = sema.mod.declPtr(decl_index);
@@ -34028,16 +34023,16 @@ fn intSubWithOverflow(
             const lhs_elem = try lhs.elemValue(sema.mod, i);
             const rhs_elem = try rhs.elemValue(sema.mod, i);
             const of_math_result = try sema.intSubWithOverflowScalar(lhs_elem, rhs_elem, scalar_ty);
-            of.* = try of_math_result.overflow_bit.intern(Type.bool, mod);
+            of.* = try of_math_result.overflow_bit.intern(Type.u1, mod);
             scalar.* = try of_math_result.wrapped_result.intern(scalar_ty, mod);
         }
         return Value.OverflowArithmeticResult{
             .overflow_bit = (try mod.intern(.{ .aggregate = .{
-                .ty = ty.toIntern(),
+                .ty = (try mod.vectorType(.{ .len = vec_len, .child = .u1_type })).toIntern(),
                 .storage = .{ .elems = overflowed_data },
             } })).toValue(),
             .wrapped_result = (try mod.intern(.{ .aggregate = .{
-                .ty = (try mod.vectorType(.{ .len = vec_len, .child = .u1_type })).toIntern(),
+                .ty = ty.toIntern(),
                 .storage = .{ .elems = result_data },
             } })).toValue(),
         };
@@ -34066,7 +34061,7 @@ fn intSubWithOverflowScalar(
     const overflowed = result_bigint.subWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits);
     const wrapped_result = try mod.intValue_big(ty, result_bigint.toConst());
     return Value.OverflowArithmeticResult{
-        .overflow_bit = Value.boolToInt(overflowed),
+        .overflow_bit = try mod.intValue(Type.u1, @boolToInt(overflowed)),
         .wrapped_result = wrapped_result,
     };
 }
@@ -34273,16 +34268,16 @@ fn intAddWithOverflow(
             const lhs_elem = try lhs.elemValue(sema.mod, i);
             const rhs_elem = try rhs.elemValue(sema.mod, i);
             const of_math_result = try sema.intAddWithOverflowScalar(lhs_elem, rhs_elem, scalar_ty);
-            of.* = try of_math_result.overflow_bit.intern(Type.bool, mod);
+            of.* = try of_math_result.overflow_bit.intern(Type.u1, mod);
             scalar.* = try of_math_result.wrapped_result.intern(scalar_ty, mod);
         }
         return Value.OverflowArithmeticResult{
             .overflow_bit = (try mod.intern(.{ .aggregate = .{
-                .ty = ty.toIntern(),
+                .ty = (try mod.vectorType(.{ .len = vec_len, .child = .u1_type })).toIntern(),
                 .storage = .{ .elems = overflowed_data },
             } })).toValue(),
             .wrapped_result = (try mod.intern(.{ .aggregate = .{
-                .ty = (try mod.vectorType(.{ .len = vec_len, .child = .u1_type })).toIntern(),
+                .ty = ty.toIntern(),
                 .storage = .{ .elems = result_data },
             } })).toValue(),
         };
@@ -34311,7 +34306,7 @@ fn intAddWithOverflowScalar(
     const overflowed = result_bigint.addWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits);
     const result = try mod.intValue_big(ty, result_bigint.toConst());
     return Value.OverflowArithmeticResult{
-        .overflow_bit = Value.boolToInt(overflowed),
+        .overflow_bit = try mod.intValue(Type.u1, @boolToInt(overflowed)),
         .wrapped_result = result,
     };
 }
@@ -34384,7 +34379,7 @@ fn compareVector(
         scalar.* = try Value.makeBool(res_bool).intern(Type.bool, mod);
     }
     return (try mod.intern(.{ .aggregate = .{
-        .ty = (try mod.vectorType(.{ .len = ty.vectorLen(mod), .child = .u1_type })).toIntern(),
+        .ty = (try mod.vectorType(.{ .len = ty.vectorLen(mod), .child = .bool_type })).toIntern(),
         .storage = .{ .elems = result_data },
     } })).toValue();
 }
src/type.zig
@@ -2481,25 +2481,32 @@ pub const Type = struct {
                 .struct_type => |struct_type| {
                     if (mod.structPtrUnwrap(struct_type.index)) |s| {
                         assert(s.haveFieldTypes());
-                        for (s.fields.values()) |field| {
-                            if (field.is_comptime) continue;
-                            if ((try field.ty.onePossibleValue(mod)) != null) continue;
-                            return null;
+                        const field_vals = try mod.gpa.alloc(InternPool.Index, s.fields.count());
+                        defer mod.gpa.free(field_vals);
+                        for (field_vals, s.fields.values()) |*field_val, field| {
+                            if (field.is_comptime) {
+                                field_val.* = try field.default_val.intern(field.ty, mod);
+                                continue;
+                            }
+                            if (try field.ty.onePossibleValue(mod)) |field_opv| {
+                                field_val.* = try field_opv.intern(field.ty, mod);
+                            } else return null;
                         }
+
+                        // In this case the struct has no runtime-known fields and
+                        // therefore has one possible value.
+                        return (try mod.intern(.{ .aggregate = .{
+                            .ty = ty.toIntern(),
+                            .storage = .{ .elems = field_vals },
+                        } })).toValue();
                     }
-                    // In this case the struct has no runtime-known fields and
-                    // therefore has one possible value.
 
-                    // TODO: this is incorrect for structs with comptime fields, I think
-                    // we should use a temporary allocator to construct an aggregate that
-                    // is populated with the comptime values and then intern that value here.
-                    // This TODO is repeated in the redundant implementation of
-                    // one-possible-value logic in Sema.zig.
-                    const empty = try mod.intern(.{ .aggregate = .{
+                    // In this case the struct has no fields at all and
+                    // therefore has one possible value.
+                    return (try mod.intern(.{ .aggregate = .{
                         .ty = ty.toIntern(),
                         .storage = .{ .elems = &.{} },
-                    } });
-                    return empty.toValue();
+                    } })).toValue();
                 },
 
                 .anon_struct_type => |tuple| {
src/value.zig
@@ -385,7 +385,7 @@ pub const Value = struct {
                 } });
             },
             .aggregate => {
-                const old_elems = val.castTag(.aggregate).?.data;
+                const old_elems = val.castTag(.aggregate).?.data[0..ty.arrayLen(mod)];
                 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.toIntern());
@@ -656,7 +656,7 @@ pub const Value = struct {
         };
     }
 
-    pub fn toBool(val: Value, _: *const Module) bool {
+    pub fn toBool(val: Value) bool {
         return switch (val.toIntern()) {
             .bool_true => true,
             .bool_false => false,
@@ -697,7 +697,7 @@ pub const Value = struct {
         switch (ty.zigTypeTag(mod)) {
             .Void => {},
             .Bool => {
-                buffer[0] = @boolToInt(val.toBool(mod));
+                buffer[0] = @boolToInt(val.toBool());
             },
             .Int, .Enum => {
                 const int_info = ty.intInfo(mod);
@@ -736,13 +736,20 @@ pub const Value = struct {
             },
             .Struct => switch (ty.containerLayout(mod)) {
                 .Auto => return error.IllDefinedMemoryLayout,
-                .Extern => {
-                    const fields = ty.structFields(mod).values();
-                    const field_vals = val.castTag(.aggregate).?.data;
-                    for (fields, 0..) |field, i| {
-                        const off = @intCast(usize, ty.structFieldOffset(i, mod));
-                        try writeToMemory(field_vals[i], field.ty, mod, buffer[off..]);
-                    }
+                .Extern => for (ty.structFields(mod).values(), 0..) |field, i| {
+                    const off = @intCast(usize, ty.structFieldOffset(i, mod));
+                    const field_val = switch (val.ip_index) {
+                        .none => val.castTag(.aggregate).?.data[i],
+                        else => switch (mod.intern_pool.indexToKey(val.toIntern()).aggregate.storage) {
+                            .bytes => |bytes| {
+                                buffer[off] = bytes[i];
+                                continue;
+                            },
+                            .elems => |elems| elems[i],
+                            .repeated_elem => |elem| elem,
+                        }.toValue(),
+                    };
+                    try writeToMemory(field_val, field.ty, mod, buffer[off..]);
                 },
                 .Packed => {
                     const byte_count = (@intCast(usize, ty.bitSize(mod)) + 7) / 8;
@@ -812,7 +819,7 @@ pub const Value = struct {
                     .Little => bit_offset / 8,
                     .Big => buffer.len - bit_offset / 8 - 1,
                 };
-                if (val.toBool(mod)) {
+                if (val.toBool()) {
                     buffer[byte_index] |= (@as(u8, 1) << @intCast(u3, bit_offset % 8));
                 } else {
                     buffer[byte_index] &= ~(@as(u8, 1) << @intCast(u3, bit_offset % 8));
@@ -1331,24 +1338,7 @@ pub const Value = struct {
             .gt => {},
         }
 
-        const lhs_float = lhs.isFloat(mod);
-        const rhs_float = rhs.isFloat(mod);
-        if (lhs_float and rhs_float) {
-            const lhs_tag = lhs.tag();
-            const rhs_tag = rhs.tag();
-            if (lhs_tag == rhs_tag) {
-                const lhs_storage = mod.intern_pool.indexToKey(lhs.toIntern()).float.storage;
-                const rhs_storage = mod.intern_pool.indexToKey(rhs.toIntern()).float.storage;
-                const lhs128: f128 = switch (lhs_storage) {
-                    inline else => |x| x,
-                };
-                const rhs128: f128 = switch (rhs_storage) {
-                    inline else => |x| x,
-                };
-                return std.math.order(lhs128, rhs128);
-            }
-        }
-        if (lhs_float or rhs_float) {
+        if (lhs.isFloat(mod) or rhs.isFloat(mod)) {
             const lhs_f128 = lhs.toFloat(f128, mod);
             const rhs_f128 = rhs.toFloat(f128, mod);
             return std.math.order(lhs_f128, rhs_f128);
@@ -1669,86 +1659,6 @@ pub const Value = struct {
         return (try orderAdvanced(a, b, mod, opt_sema)).compare(.eq);
     }
 
-    /// This function is used by hash maps and so treats floating-point NaNs as equal
-    /// to each other, and not equal to other floating-point values.
-    pub fn hash(val: Value, ty: Type, hasher: *std.hash.Wyhash, mod: *Module) void {
-        if (val.ip_index != .none) {
-            // The InternPool data structure hashes based on Key to make interned objects
-            // unique. An Index can be treated simply as u32 value for the
-            // purpose of Type/Value hashing and equality.
-            std.hash.autoHash(hasher, val.toIntern());
-            return;
-        }
-        const zig_ty_tag = ty.zigTypeTag(mod);
-        std.hash.autoHash(hasher, zig_ty_tag);
-        if (val.isUndef(mod)) return;
-        // The value is runtime-known and shouldn't affect the hash.
-        if (val.isRuntimeValue(mod)) return;
-
-        switch (zig_ty_tag) {
-            .Opaque => unreachable, // Cannot hash opaque types
-
-            .Void,
-            .NoReturn,
-            .Undefined,
-            .Null,
-            => {},
-
-            .Type,
-            .Float,
-            .ComptimeFloat,
-            .Bool,
-            .Int,
-            .ComptimeInt,
-            .Pointer,
-            .Optional,
-            .ErrorUnion,
-            .ErrorSet,
-            .Enum,
-            .EnumLiteral,
-            .Fn,
-            => unreachable, // handled via ip_index check above
-            .Array, .Vector => {
-                const len = ty.arrayLen(mod);
-                const elem_ty = ty.childType(mod);
-                var index: usize = 0;
-                while (index < len) : (index += 1) {
-                    const elem_val = val.elemValue(mod, index) catch |err| switch (err) {
-                        // Will be solved when arrays and vectors get migrated to the intern pool.
-                        error.OutOfMemory => @panic("OOM"),
-                    };
-                    elem_val.hash(elem_ty, hasher, mod);
-                }
-            },
-            .Struct => {
-                switch (val.tag()) {
-                    .aggregate => {
-                        const field_values = val.castTag(.aggregate).?.data;
-                        for (field_values, 0..) |field_val, i| {
-                            const field_ty = ty.structFieldType(i, mod);
-                            field_val.hash(field_ty, hasher, mod);
-                        }
-                    },
-                    else => unreachable,
-                }
-            },
-            .Union => {
-                const union_obj = val.cast(Payload.Union).?.data;
-                if (ty.unionTagType(mod)) |tag_ty| {
-                    union_obj.tag.hash(tag_ty, hasher, mod);
-                }
-                const active_field_ty = ty.unionFieldType(union_obj.tag, mod);
-                union_obj.val.hash(active_field_ty, hasher, mod);
-            },
-            .Frame => {
-                @panic("TODO implement hashing frame values");
-            },
-            .AnyFrame => {
-                @panic("TODO implement hashing anyframe values");
-            },
-        }
-    }
-
     /// This is a more conservative hash function that produces equal hashes for values
     /// that can coerce into each other.
     /// This function is used by hash maps and so treats floating-point NaNs as equal
@@ -1820,35 +1730,6 @@ pub const Value = struct {
         }
     }
 
-    pub const ArrayHashContext = struct {
-        ty: Type,
-        mod: *Module,
-
-        pub fn hash(self: @This(), val: Value) u32 {
-            const other_context: HashContext = .{ .ty = self.ty, .mod = self.mod };
-            return @truncate(u32, other_context.hash(val));
-        }
-        pub fn eql(self: @This(), a: Value, b: Value, b_index: usize) bool {
-            _ = b_index;
-            return a.eql(b, self.ty, self.mod);
-        }
-    };
-
-    pub const HashContext = struct {
-        ty: Type,
-        mod: *Module,
-
-        pub fn hash(self: @This(), val: Value) u64 {
-            var hasher = std.hash.Wyhash.init(0);
-            val.hash(self.ty, &hasher, self.mod);
-            return hasher.final();
-        }
-
-        pub fn eql(self: @This(), a: Value, b: Value) bool {
-            return a.eql(b, self.ty, self.mod);
-        }
-    };
-
     pub fn isComptimeMutablePtr(val: Value, mod: *Module) bool {
         return switch (mod.intern_pool.indexToKey(val.toIntern())) {
             .ptr => |ptr| switch (ptr.addr) {
@@ -1919,14 +1800,25 @@ pub const Value = struct {
     }
 
     pub fn sliceLen(val: Value, mod: *Module) u64 {
-        return mod.intern_pool.sliceLen(val.toIntern()).toValue().toUnsignedInt(mod);
+        const ptr = mod.intern_pool.indexToKey(val.toIntern()).ptr;
+        return switch (ptr.len) {
+            .none => switch (mod.intern_pool.indexToKey(switch (ptr.addr) {
+                .decl => |decl| mod.declPtr(decl).ty.toIntern(),
+                .mut_decl => |mut_decl| mod.declPtr(mut_decl.decl).ty.toIntern(),
+                .comptime_field => |comptime_field| mod.intern_pool.typeOf(comptime_field),
+                else => unreachable,
+            })) {
+                .array_type => |array_type| array_type.len,
+                else => 1,
+            },
+            else => ptr.len.toValue().toUnsignedInt(mod),
+        };
     }
 
     /// Asserts the value is a single-item pointer to an array, or an array,
     /// or an unknown-length pointer, and returns the element value at the index.
     pub fn elemValue(val: Value, mod: *Module, index: usize) Allocator.Error!Value {
         return switch (val.ip_index) {
-            .undef => Value.undef,
             .none => switch (val.tag()) {
                 .repeated => val.castTag(.repeated).?.data,
                 .aggregate => val.castTag(.aggregate).?.data[index],
@@ -1934,6 +1826,9 @@ pub const Value = struct {
                 else => unreachable,
             },
             else => switch (mod.intern_pool.indexToKey(val.toIntern())) {
+                .undef => |ty| (try mod.intern(.{
+                    .undef = ty.toType().elemType2(mod).toIntern(),
+                })).toValue(),
                 .ptr => |ptr| switch (ptr.addr) {
                     .decl => |decl| mod.declPtr(decl).val.elemValue(mod, index),
                     .mut_decl => |mut_decl| mod.declPtr(mut_decl.decl).val.elemValue(mod, index),
@@ -2492,7 +2387,7 @@ pub const Value = struct {
         }
 
         return OverflowArithmeticResult{
-            .overflow_bit = boolToInt(overflowed),
+            .overflow_bit = try mod.intValue(Type.u1, @boolToInt(overflowed)),
             .wrapped_result = try mod.intValue_big(ty, result_bigint.toConst()),
         };
     }
@@ -2645,7 +2540,8 @@ pub const Value = struct {
 
     /// operands must be integers; handles undefined.
     pub fn bitwiseNotScalar(val: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
-        if (val.isUndef(mod)) return Value.undef;
+        if (val.isUndef(mod)) return (try mod.intern(.{ .undef = ty.toIntern() })).toValue();
+        if (ty.toIntern() == .bool_type) return makeBool(!val.toBool());
 
         const info = ty.intInfo(mod);
 
@@ -2687,7 +2583,8 @@ pub const Value = struct {
 
     /// operands must be integers; handles undefined.
     pub fn bitwiseAndScalar(lhs: Value, rhs: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
-        if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.undef;
+        if (lhs.isUndef(mod) or rhs.isUndef(mod)) return (try mod.intern(.{ .undef = ty.toIntern() })).toValue();
+        if (ty.toIntern() == .bool_type) return makeBool(lhs.toBool() and rhs.toBool());
 
         // TODO is this a performance issue? maybe we should try the operation without
         // resorting to BigInt first.
@@ -2725,7 +2622,8 @@ pub const Value = struct {
 
     /// operands must be integers; handles undefined.
     pub fn bitwiseNandScalar(lhs: Value, rhs: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
-        if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.undef;
+        if (lhs.isUndef(mod) or rhs.isUndef(mod)) return (try mod.intern(.{ .undef = ty.toIntern() })).toValue();
+        if (ty.toIntern() == .bool_type) return makeBool(!(lhs.toBool() and rhs.toBool()));
 
         const anded = try bitwiseAnd(lhs, rhs, ty, arena, mod);
         const all_ones = if (ty.isSignedInt(mod)) try mod.intValue(ty, -1) else try ty.maxIntScalar(mod, ty);
@@ -2752,7 +2650,8 @@ pub const Value = struct {
 
     /// operands must be integers; handles undefined.
     pub fn bitwiseOrScalar(lhs: Value, rhs: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
-        if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.undef;
+        if (lhs.isUndef(mod) or rhs.isUndef(mod)) return (try mod.intern(.{ .undef = ty.toIntern() })).toValue();
+        if (ty.toIntern() == .bool_type) return makeBool(lhs.toBool() or rhs.toBool());
 
         // TODO is this a performance issue? maybe we should try the operation without
         // resorting to BigInt first.
@@ -2789,7 +2688,8 @@ pub const Value = struct {
 
     /// operands must be integers; handles undefined.
     pub fn bitwiseXorScalar(lhs: Value, rhs: Value, ty: Type, arena: Allocator, mod: *Module) !Value {
-        if (lhs.isUndef(mod) or rhs.isUndef(mod)) return Value.undef;
+        if (lhs.isUndef(mod) or rhs.isUndef(mod)) return (try mod.intern(.{ .undef = ty.toIntern() })).toValue();
+        if (ty.toIntern() == .bool_type) return makeBool(lhs.toBool() != rhs.toBool());
 
         // TODO is this a performance issue? maybe we should try the operation without
         // resorting to BigInt first.
@@ -3233,7 +3133,7 @@ pub const Value = struct {
             result_bigint.truncate(result_bigint.toConst(), info.signedness, info.bits);
         }
         return OverflowArithmeticResult{
-            .overflow_bit = boolToInt(overflowed),
+            .overflow_bit = try mod.intValue(Type.u1, @boolToInt(overflowed)),
             .wrapped_result = try mod.intValue_big(ty, result_bigint.toConst()),
         };
     }
@@ -4267,12 +4167,6 @@ pub const Value = struct {
         return if (x) Value.true else Value.false;
     }
 
-    pub fn boolToInt(x: bool) Value {
-        const zero: Value = .{ .ip_index = .zero, .legacy = undefined };
-        const one: Value = .{ .ip_index = .one, .legacy = undefined };
-        return if (x) one else zero;
-    }
-
     pub const RuntimeIndex = InternPool.RuntimeIndex;
 
     /// This function is used in the debugger pretty formatters in tools/ to fetch the
tools/lldb_pretty_printers.py
@@ -354,8 +354,8 @@ def Zir_Inst__Zir_Inst_Ref_SummaryProvider(value, _=None):
 
 def Air_Inst__Air_Inst_Ref_SummaryProvider(value, _=None):
     members = value.type.enum_members
-    # ignore .none
-    return value if any(value.unsigned == member.unsigned for member in members) else 'instructions[%d]' % (value.unsigned + 1 - len(members))
+    # ignore .var_args_param_type and .none
+    return value if any(value.unsigned == member.unsigned for member in members) else 'instructions[%d]' % (value.unsigned + 2 - len(members))
 
 class Module_Decl__Module_Decl_Index_SynthProvider:
     def __init__(self, value, _=None): self.value = value
@@ -365,7 +365,7 @@ class Module_Decl__Module_Decl_Index_SynthProvider:
                 mod = frame.FindVariable('mod') or frame.FindVariable('module')
                 if mod: break
             else: return
-            self.ptr = mod.GetChildMemberWithName('allocated_decls').GetChildAtIndex(self.value.unsigned).Clone('decl')
+            self.ptr = mod.GetChildMemberWithName('allocated_decls').GetChildAtIndex(self.value.unsigned).address_of.Clone('decl')
         except: pass
     def has_children(self): return True
     def num_children(self): return 1