Просмотр исходного кода

Remove all remaining uses of `TypeId`s as instruction operands. (#5280)

In preparation for shifting from `TypeId`s potentially representing
attached types to always representing unattached types, using
[terminology suggested on
Discord](https://discord.com/channels/655572317891461132/963846118964350976/1359286326779973712).
This change causes us to track slightly more type spelling information
through SemIR.

One change that has significant impact on the SemIR output is that we
now build a `struct_type` instruction in each class representing the
types of the fields, including the spelling used for those types. This
is now no longer always identical to the corresponding canonical
`struct_type` for the object representation, so it's built separately
and owned by the class.

Also remove `TypeBlock` support entirely, as its only use was
representing `TupleType`s, which now use an `InstBlock`.
Richard Smith 1 год назад
Родитель
Сommit
a74ca9071b
100 измененных файлов с 464 добавлено и 338 удалено
  1. 19 14
      toolchain/check/action.cpp
  2. 10 7
      toolchain/check/action.h
  3. 19 12
      toolchain/check/class.cpp
  4. 1 1
      toolchain/check/class.h
  5. 0 3
      toolchain/check/context.h
  6. 42 33
      toolchain/check/convert.cpp
  7. 1 15
      toolchain/check/deduce.cpp
  8. 0 6
      toolchain/check/dump.cpp
  9. 6 38
      toolchain/check/eval.cpp
  10. 9 6
      toolchain/check/eval_inst.cpp
  11. 8 7
      toolchain/check/handle_choice.cpp
  12. 5 4
      toolchain/check/handle_pattern_list.cpp
  13. 6 4
      toolchain/check/handle_struct.cpp
  14. 5 4
      toolchain/check/handle_tuple_literal.cpp
  15. 1 1
      toolchain/check/import_cpp.cpp
  16. 23 39
      toolchain/check/import_ref.cpp
  17. 5 3
      toolchain/check/member_access.cpp
  18. 11 8
      toolchain/check/pattern_match.cpp
  19. 10 36
      toolchain/check/subst.cpp
  20. 1 0
      toolchain/check/testdata/alias/no_prelude/alias_of_alias.carbon
  21. 1 0
      toolchain/check/testdata/alias/no_prelude/export_name.carbon
  22. 2 0
      toolchain/check/testdata/alias/no_prelude/fail_aliased_name_in_diag.carbon
  23. 1 0
      toolchain/check/testdata/alias/no_prelude/fail_modifiers.carbon
  24. 1 0
      toolchain/check/testdata/alias/no_prelude/fail_name_conflict.carbon
  25. 1 0
      toolchain/check/testdata/alias/no_prelude/import.carbon
  26. 1 0
      toolchain/check/testdata/alias/no_prelude/import_access.carbon
  27. 1 0
      toolchain/check/testdata/alias/no_prelude/import_order.carbon
  28. 1 0
      toolchain/check/testdata/alias/no_prelude/in_namespace.carbon
  29. 49 14
      toolchain/check/testdata/array/generic_empty.carbon
  30. 1 1
      toolchain/check/testdata/array/init_dependent_bound.carbon
  31. 21 15
      toolchain/check/testdata/as/adapter_conversion.carbon
  32. 1 0
      toolchain/check/testdata/as/identity.carbon
  33. 1 0
      toolchain/check/testdata/as/no_prelude/tuple.carbon
  34. 1 0
      toolchain/check/testdata/as/overloaded.carbon
  35. 0 2
      toolchain/check/testdata/basics/builtin_insts.carbon
  36. 2 0
      toolchain/check/testdata/builtins/bool/eq.carbon
  37. 2 0
      toolchain/check/testdata/builtins/bool/neq.carbon
  38. 2 0
      toolchain/check/testdata/builtins/float/eq.carbon
  39. 2 0
      toolchain/check/testdata/builtins/float/greater.carbon
  40. 2 0
      toolchain/check/testdata/builtins/float/greater_eq.carbon
  41. 2 0
      toolchain/check/testdata/builtins/float/less.carbon
  42. 2 0
      toolchain/check/testdata/builtins/float/less_eq.carbon
  43. 2 0
      toolchain/check/testdata/builtins/float/neq.carbon
  44. 4 4
      toolchain/check/testdata/choice/basic.carbon
  45. 1 1
      toolchain/check/testdata/choice/fail_invalid.carbon
  46. 3 3
      toolchain/check/testdata/choice/fail_todo_params.carbon
  47. 2 2
      toolchain/check/testdata/choice/generic.carbon
  48. 7 1
      toolchain/check/testdata/class/access_modifers.carbon
  49. 5 3
      toolchain/check/testdata/class/adapter/adapt.carbon
  50. 7 5
      toolchain/check/testdata/class/adapter/adapt_copy.carbon
  51. 9 6
      toolchain/check/testdata/class/adapter/extend_adapt.carbon
  52. 3 2
      toolchain/check/testdata/class/adapter/fail_adapt_bad_decl.carbon
  53. 6 5
      toolchain/check/testdata/class/adapter/fail_adapt_modifiers.carbon
  54. 1 0
      toolchain/check/testdata/class/adapter/fail_adapt_with_base.carbon
  55. 2 0
      toolchain/check/testdata/class/adapter/fail_adapt_with_subobjects.carbon
  56. 6 4
      toolchain/check/testdata/class/adapter/init_adapt.carbon
  57. 6 2
      toolchain/check/testdata/class/base.carbon
  58. 3 1
      toolchain/check/testdata/class/base_field.carbon
  59. 2 0
      toolchain/check/testdata/class/base_function_unqualified.carbon
  60. 3 1
      toolchain/check/testdata/class/base_method.carbon
  61. 3 1
      toolchain/check/testdata/class/base_method_qualified.carbon
  62. 7 3
      toolchain/check/testdata/class/base_method_shadow.carbon
  63. 1 0
      toolchain/check/testdata/class/basic.carbon
  64. 1 0
      toolchain/check/testdata/class/complete_in_member_fn.carbon
  65. 3 1
      toolchain/check/testdata/class/compound_field.carbon
  66. 1 0
      toolchain/check/testdata/class/cross_package_import.carbon
  67. 6 3
      toolchain/check/testdata/class/derived_to_base.carbon
  68. 1 0
      toolchain/check/testdata/class/fail_addr_not_self.carbon
  69. 1 0
      toolchain/check/testdata/class/fail_addr_self.carbon
  70. 15 6
      toolchain/check/testdata/class/fail_base_bad_type.carbon
  71. 3 0
      toolchain/check/testdata/class/fail_base_method_define.carbon
  72. 2 0
      toolchain/check/testdata/class/fail_base_misplaced.carbon
  73. 5 0
      toolchain/check/testdata/class/fail_base_modifiers.carbon
  74. 2 0
      toolchain/check/testdata/class/fail_base_no_extend.carbon
  75. 4 0
      toolchain/check/testdata/class/fail_base_repeated.carbon
  76. 3 1
      toolchain/check/testdata/class/fail_base_unbound.carbon
  77. 2 0
      toolchain/check/testdata/class/fail_compound_type_mismatch.carbon
  78. 2 1
      toolchain/check/testdata/class/fail_convert_to_invalid.carbon
  79. 4 1
      toolchain/check/testdata/class/fail_derived_to_base.carbon
  80. 4 1
      toolchain/check/testdata/class/fail_extend_cycle.carbon
  81. 1 0
      toolchain/check/testdata/class/fail_field_modifiers.carbon
  82. 5 4
      toolchain/check/testdata/class/fail_generic_method.carbon
  83. 2 0
      toolchain/check/testdata/class/fail_import_misuses.carbon
  84. 3 1
      toolchain/check/testdata/class/fail_incomplete.carbon
  85. 1 0
      toolchain/check/testdata/class/fail_init.carbon
  86. 2 1
      toolchain/check/testdata/class/fail_init_as_inplace.carbon
  87. 3 1
      toolchain/check/testdata/class/fail_memaccess_category.carbon
  88. 1 0
      toolchain/check/testdata/class/fail_member_of_let.carbon
  89. 1 0
      toolchain/check/testdata/class/fail_method.carbon
  90. 3 0
      toolchain/check/testdata/class/fail_method_modifiers.carbon
  91. 1 0
      toolchain/check/testdata/class/fail_method_redefinition.carbon
  92. 5 0
      toolchain/check/testdata/class/fail_modifiers.carbon
  93. 1 0
      toolchain/check/testdata/class/fail_out_of_line_decl.carbon
  94. 6 0
      toolchain/check/testdata/class/fail_redeclaration_scope.carbon
  95. 2 0
      toolchain/check/testdata/class/fail_redefinition.carbon
  96. 1 0
      toolchain/check/testdata/class/fail_scope.carbon
  97. 2 0
      toolchain/check/testdata/class/fail_self.carbon
  98. 1 0
      toolchain/check/testdata/class/fail_self_param.carbon
  99. 1 0
      toolchain/check/testdata/class/fail_self_type_member.carbon
  100. 1 0
      toolchain/check/testdata/class/fail_unbound_field.carbon

+ 19 - 14
toolchain/check/action.cpp

@@ -18,7 +18,9 @@ auto PerformAction(Context& context, SemIR::LocId loc_id,
                    SemIR::RefineTypeAction action) -> SemIR::InstId {
   return AddInst<SemIR::AsCompatible>(
       context, loc_id,
-      {.type_id = action.inst_type_id, .source_id = action.inst_id});
+      {.type_id =
+           context.types().GetTypeIdForTypeInstId(action.inst_type_inst_id),
+       .source_id = action.inst_id});
 }
 
 static auto OperandIsDependent(Context& context, SemIR::ConstantId const_id)
@@ -38,7 +40,7 @@ auto OperandIsDependent(Context& context, SemIR::TypeId type_id) -> bool {
   return OperandIsDependent(context, context.types().GetConstantId(type_id));
 }
 
-auto OperandIsDependent(Context& context, SemIR::MetaInstId inst_id) -> bool {
+auto OperandIsDependent(Context& context, SemIR::InstId inst_id) -> bool {
   // An instruction operand makes the instruction dependent if its type or
   // constant value is dependent.
   return OperandIsDependent(context, context.insts().Get(inst_id).type_id()) ||
@@ -52,8 +54,8 @@ static auto OperandIsDependent(Context& context, SemIR::Inst::ArgAndKind arg)
       return OperandIsDependent(context, inst_id);
     }
 
-    case CARBON_KIND(SemIR::TypeId type_id): {
-      return OperandIsDependent(context, type_id);
+    case CARBON_KIND(SemIR::InstId inst_id): {
+      return OperandIsDependent(context, inst_id);
     }
 
     case SemIR::IdKind::None:
@@ -71,7 +73,7 @@ auto ActionIsDependent(Context& context, SemIR::Inst action_inst) -> bool {
   if (auto refine_action = action_inst.TryAs<SemIR::RefineTypeAction>()) {
     // `RefineTypeAction` can be performed whenever the type is non-dependent,
     // even if we don't know the instruction yet.
-    return OperandIsDependent(context, refine_action->inst_type_id);
+    return OperandIsDependent(context, refine_action->inst_type_inst_id);
   }
 
   if (OperandIsDependent(context, action_inst.type_id())) {
@@ -83,19 +85,20 @@ auto ActionIsDependent(Context& context, SemIR::Inst action_inst) -> bool {
 
 static auto AddDependentActionSpliceImpl(Context& context,
                                          SemIR::LocIdAndInst action,
-                                         SemIR::TypeId result_type_id)
+                                         SemIR::InstId result_type_inst_id)
     -> SemIR::InstId {
   auto inst_id = AddDependentActionInst(context, action);
-  if (!result_type_id.has_value()) {
-    auto type_inst_id = AddDependentActionInst(
+  if (!result_type_inst_id.has_value()) {
+    result_type_inst_id = AddDependentActionInst(
         context, action.loc_id,
         SemIR::TypeOfInst{.type_id = SemIR::TypeType::SingletonTypeId,
                           .inst_id = inst_id});
-    result_type_id = context.types().GetTypeIdForTypeInstId(type_inst_id);
   }
   return AddInst(
       context, action.loc_id,
-      SemIR::SpliceInst{.type_id = result_type_id, .inst_id = inst_id});
+      SemIR::SpliceInst{.type_id = context.types().GetTypeIdForTypeInstId(
+                            result_type_inst_id),
+                        .inst_id = inst_id});
 }
 
 // Refine one operand of an action. Given an argument from a template, this
@@ -115,14 +118,15 @@ static auto RefineOperand(Context& context, SemIR::LocId loc_id,
     // If the type of the action argument is dependent, refine to an instruction
     // with a concrete type.
     if (OperandIsDependent(context, inst.type_id())) {
+      auto type_inst_id = context.types().GetInstId(inst.type_id());
       inst_id = AddDependentActionSpliceImpl(
           context,
           SemIR::LocIdAndInst(loc_id,
                               SemIR::RefineTypeAction{
                                   .type_id = SemIR::InstType::SingletonTypeId,
                                   .inst_id = *inst_id,
-                                  .inst_type_id = inst.type_id()}),
-          inst.type_id());
+                                  .inst_type_inst_id = type_inst_id}),
+          type_inst_id);
     }
 
     // TODO: Handle the case where the constant value of the instruction is
@@ -145,9 +149,10 @@ static auto RefineOperands(Context& context, SemIR::LocId loc_id,
 }
 
 auto AddDependentActionSplice(Context& context, SemIR::LocIdAndInst action,
-                              SemIR::TypeId result_type_id) -> SemIR::InstId {
+                              SemIR::InstId result_type_inst_id)
+    -> SemIR::InstId {
   action.inst = RefineOperands(context, action.loc_id, action.inst);
-  return AddDependentActionSpliceImpl(context, action, result_type_id);
+  return AddDependentActionSpliceImpl(context, action, result_type_inst_id);
 }
 
 auto Internal::BeginPerformDelayedAction(Context& context) -> void {

+ 10 - 7
toolchain/check/action.h

@@ -34,7 +34,7 @@ auto ActionIsDependent(Context& context, SemIR::Inst action_inst) -> bool;
 
 // Determines whether the given action operand depends on a template parameter
 // in a way that means the action cannot be performed immediately.
-auto OperandIsDependent(Context& context, SemIR::MetaInstId inst_id) -> bool;
+auto OperandIsDependent(Context& context, SemIR::InstId inst_id) -> bool;
 
 // Determines whether the given type depends on a template parameter
 // in a way that means the action cannot be performed immediately.
@@ -43,14 +43,16 @@ auto OperandIsDependent(Context& context, SemIR::TypeId type_id) -> bool;
 // Adds an instruction to the current block to splice in the result of
 // performing a dependent action.
 auto AddDependentActionSplice(Context& context, SemIR::LocIdAndInst action,
-                              SemIR::TypeId result_type_id) -> SemIR::InstId;
+                              SemIR::InstId result_type_inst_id)
+    -> SemIR::InstId;
 
 // Convenience wrapper for `AddDependentActionSplice`.
 template <typename LocT, typename InstT>
 auto AddDependentActionSplice(Context& context, LocT loc, InstT inst,
-                              SemIR::TypeId result_type_id) -> SemIR::InstId {
+                              SemIR::InstId result_type_inst_id)
+    -> SemIR::InstId {
   return AddDependentActionSplice(context, SemIR::LocIdAndInst(loc, inst),
-                                  result_type_id);
+                                  result_type_inst_id);
 }
 
 // Handles a new action. If the action is not dependent, it is performed
@@ -58,12 +60,13 @@ auto AddDependentActionSplice(Context& context, LocT loc, InstT inst,
 // block and creates an instruction to splice in the result of the action.
 template <typename ActionT>
 auto HandleAction(Context& context, SemIR::LocId loc_id, ActionT action_inst,
-                  SemIR::TypeId result_type_id = SemIR::TypeId::None)
+                  SemIR::InstId result_type_inst_id = SemIR::InstId::None)
     -> SemIR::InstId {
   if (ActionIsDependent(context, action_inst) ||
-      OperandIsDependent(context, result_type_id)) {
+      (result_type_inst_id.has_value() &&
+       OperandIsDependent(context, result_type_inst_id))) {
     return AddDependentActionSplice(
-        context, SemIR::LocIdAndInst(loc_id, action_inst), result_type_id);
+        context, SemIR::LocIdAndInst(loc_id, action_inst), result_type_inst_id);
   }
 
   return PerformAction(context, loc_id, action_inst);

+ 19 - 12
toolchain/check/class.cpp

@@ -10,6 +10,7 @@
 #include "toolchain/check/import_ref.h"
 #include "toolchain/check/inst.h"
 #include "toolchain/check/type.h"
+#include "toolchain/parse/node_ids.h"
 
 namespace Carbon::Check {
 
@@ -98,7 +99,8 @@ static auto CheckCompleteAdapterClassType(
       context, node_id,
       {.type_id =
            GetSingletonType(context, SemIR::WitnessType::SingletonInstId),
-       .object_repr_id = object_repr_id});
+       // TODO: Use InstId from the adapt declaration.
+       .object_repr_type_inst_id = context.types().GetInstId(object_repr_id)});
 }
 
 static auto AddStructTypeFields(
@@ -113,7 +115,7 @@ static auto AddStructTypeFields(
     if (field_decl.type_id == SemIR::ErrorInst::SingletonTypeId) {
       struct_type_fields.push_back(
           {.name_id = field_decl.name_id,
-           .type_id = SemIR::ErrorInst::SingletonTypeId});
+           .type_inst_id = SemIR::ErrorInst::SingletonInstId});
       continue;
     }
     auto unbound_element_type =
@@ -121,8 +123,7 @@ static auto AddStructTypeFields(
             field_decl.type_id);
     struct_type_fields.push_back(
         {.name_id = field_decl.name_id,
-         .type_id = context.types().GetTypeIdForTypeInstId(
-             unbound_element_type.element_type_inst_id)});
+         .type_inst_id = unbound_element_type.element_type_inst_id});
   }
   auto fields_id =
       context.struct_type_fields().AddCanonical(struct_type_fields);
@@ -189,7 +190,7 @@ static auto BuildVtable(Context& context, Parse::NodeId node_id,
 // Checks that the specified finished class definition is valid and builds and
 // returns a corresponding complete type witness instruction.
 static auto CheckCompleteClassType(
-    Context& context, Parse::NodeId node_id, SemIR::ClassId class_id,
+    Context& context, Parse::ClassDefinitionId node_id, SemIR::ClassId class_id,
     llvm::ArrayRef<SemIR::InstId> field_decls,
     llvm::ArrayRef<SemIR::InstId> vtable_contents,
     llvm::ArrayRef<SemIR::InstId> body) -> SemIR::InstId {
@@ -202,6 +203,8 @@ static auto CheckCompleteClassType(
   bool defining_vptr = class_info.is_dynamic;
   auto base_type_id =
       class_info.GetBaseType(context.sem_ir(), SemIR::SpecificId::None);
+  // TODO: Use InstId from base declaration.
+  auto base_type_inst_id = context.types().GetInstId(base_type_id);
   SemIR::Class* base_class_info = nullptr;
   if (base_type_id.has_value()) {
     // TODO: If the base class is template dependent, we will need to decide
@@ -218,8 +221,8 @@ static auto CheckCompleteClassType(
   if (defining_vptr) {
     struct_type_fields.push_back(
         {.name_id = SemIR::NameId::Vptr,
-         .type_id =
-             GetPointerType(context, SemIR::VtableType::SingletonInstId)});
+         .type_inst_id = context.types().GetInstId(
+             GetPointerType(context, SemIR::VtableType::SingletonInstId))});
   }
   if (base_type_id.has_value()) {
     auto base_decl = context.insts().GetAs<SemIR::BaseDecl>(class_info.base_id);
@@ -227,7 +230,7 @@ static auto CheckCompleteClassType(
         SemIR::ElementIndex{static_cast<int>(struct_type_fields.size())};
     ReplaceInstPreservingConstantValue(context, class_info.base_id, base_decl);
     struct_type_fields.push_back(
-        {.name_id = SemIR::NameId::Base, .type_id = base_type_id});
+        {.name_id = SemIR::NameId::Base, .type_inst_id = base_type_inst_id});
   }
 
   if (class_info.is_dynamic) {
@@ -237,16 +240,20 @@ static auto CheckCompleteClassType(
         vtable_contents);
   }
 
+  auto struct_type_inst_id = AddInst<SemIR::StructType>(
+      context, node_id,
+      {.type_id = SemIR::TypeType::SingletonTypeId,
+       .fields_id =
+           AddStructTypeFields(context, struct_type_fields, field_decls)});
+
   return AddInst<SemIR::CompleteTypeWitness>(
       context, node_id,
       {.type_id =
            GetSingletonType(context, SemIR::WitnessType::SingletonInstId),
-       .object_repr_id = GetStructType(
-           context,
-           AddStructTypeFields(context, struct_type_fields, field_decls))});
+       .object_repr_type_inst_id = struct_type_inst_id});
 }
 
-auto ComputeClassObjectRepr(Context& context, Parse::NodeId node_id,
+auto ComputeClassObjectRepr(Context& context, Parse::ClassDefinitionId node_id,
                             SemIR::ClassId class_id,
                             llvm::ArrayRef<SemIR::InstId> field_decls,
                             llvm::ArrayRef<SemIR::InstId> vtable_contents,

+ 1 - 1
toolchain/check/class.h

@@ -21,7 +21,7 @@ auto StartClassDefinition(Context& context, SemIR::Class& class_info,
                           SemIR::InstId definition_id) -> void;
 
 // Computes the object representation for a fully defined class.
-auto ComputeClassObjectRepr(Context& context, Parse::NodeId node_id,
+auto ComputeClassObjectRepr(Context& context, Parse::ClassDefinitionId node_id,
                             SemIR::ClassId class_id,
                             llvm::ArrayRef<SemIR::InstId> field_decls,
                             llvm::ArrayRef<SemIR::InstId> vtable_contents,

+ 0 - 3
toolchain/check/context.h

@@ -262,9 +262,6 @@ class Context {
     return sem_ir().struct_type_fields();
   }
   auto types() -> SemIR::TypeStore& { return sem_ir().types(); }
-  auto type_blocks() -> SemIR::BlockValueStore<SemIR::TypeBlockId>& {
-    return sem_ir().type_blocks();
-  }
   // Instructions should be added with `AddInst` or `AddInstInNoBlock` from
   // `inst.h`. This is `const` to prevent accidental misuse.
   auto insts() -> const SemIR::InstStore& { return sem_ir().insts(); }

+ 42 - 33
toolchain/check/convert.cpp

@@ -152,12 +152,17 @@ static auto MakeElementAccessInst(Context& context, SemIR::LocId loc_id,
 template <typename SourceAccessInstT, typename TargetAccessInstT>
 static auto ConvertAggregateElement(
     Context& context, SemIR::LocId loc_id, SemIR::InstId src_id,
-    SemIR::TypeId src_elem_type,
+    SemIR::InstId src_elem_type_inst,
     llvm::ArrayRef<SemIR::InstId> src_literal_elems,
     ConversionTarget::Kind kind, SemIR::InstId target_id,
-    SemIR::TypeId target_elem_type, PendingBlock* target_block,
+    SemIR::InstId target_elem_type_inst, PendingBlock* target_block,
     size_t src_field_index, size_t target_field_index,
     SemIR::InstId vtable_id = SemIR::InstId::None) -> SemIR::InstId {
+  auto src_elem_type =
+      context.types().GetTypeIdForTypeInstId(src_elem_type_inst);
+  auto target_elem_type =
+      context.types().GetTypeIdForTypeInstId(target_elem_type_inst);
+
   // Compute the location of the source element. This goes into the current code
   // block, not into the target block.
   // TODO: Ideally we would discard this instruction if it's unused.
@@ -191,7 +196,7 @@ static auto ConvertTupleToArray(Context& context, SemIR::TupleType tuple_type,
                                 SemIR::InstId value_id, ConversionTarget target)
     -> SemIR::InstId {
   auto& sem_ir = context.sem_ir();
-  auto tuple_elem_types = sem_ir.type_blocks().Get(tuple_type.elements_id);
+  auto tuple_elem_types = sem_ir.inst_blocks().Get(tuple_type.elements_id);
 
   auto value = sem_ir.insts().Get(value_id);
   auto value_loc_id = sem_ir.insts().GetLocId(value_id);
@@ -258,16 +263,14 @@ static auto ConvertTupleToArray(Context& context, SemIR::TupleType tuple_type,
   // if initializing from a tuple literal.
   llvm::SmallVector<SemIR::InstId> inits;
   inits.reserve(*array_bound + 1);
-  for (auto [i, src_type_id] : llvm::enumerate(tuple_elem_types)) {
+  for (auto [i, src_type_inst_id] : llvm::enumerate(tuple_elem_types)) {
     // TODO: This call recurses back into conversion. Switch to an iterative
     // approach.
     auto init_id =
         ConvertAggregateElement<SemIR::TupleAccess, SemIR::ArrayIndex>(
-            context, value_loc_id, value_id, src_type_id, literal_elems,
+            context, value_loc_id, value_id, src_type_inst_id, literal_elems,
             ConversionTarget::FullInitializer, return_slot_arg_id,
-            context.types().GetTypeIdForTypeInstId(
-                array_type.element_type_inst_id),
-            target_block, i, i);
+            array_type.element_type_inst_id, target_block, i, i);
     if (init_id == SemIR::ErrorInst::SingletonInstId) {
       return SemIR::ErrorInst::SingletonInstId;
     }
@@ -291,8 +294,8 @@ static auto ConvertTupleToTuple(Context& context, SemIR::TupleType src_type,
                                 SemIR::InstId value_id, ConversionTarget target)
     -> SemIR::InstId {
   auto& sem_ir = context.sem_ir();
-  auto src_elem_types = sem_ir.type_blocks().Get(src_type.elements_id);
-  auto dest_elem_types = sem_ir.type_blocks().Get(dest_type.elements_id);
+  auto src_elem_types = sem_ir.inst_blocks().Get(src_type.elements_id);
+  auto dest_elem_types = sem_ir.inst_blocks().Get(dest_type.elements_id);
 
   auto value = sem_ir.insts().Get(value_id);
   auto value_loc_id = sem_ir.insts().GetLocId(value_id);
@@ -344,14 +347,15 @@ static auto ConvertTupleToTuple(Context& context, SemIR::TupleType src_type,
           : SemIR::CopyOnWriteInstBlock(
                 &sem_ir, SemIR::CopyOnWriteInstBlock::UninitializedBlock{
                              src_elem_types.size()});
-  for (auto [i, src_type_id, dest_type_id] :
+  for (auto [i, src_type_inst_id, dest_type_inst_id] :
        llvm::enumerate(src_elem_types, dest_elem_types)) {
     // TODO: This call recurses back into conversion. Switch to an iterative
     // approach.
     auto init_id =
         ConvertAggregateElement<SemIR::TupleAccess, SemIR::TupleAccess>(
-            context, value_loc_id, value_id, src_type_id, literal_elems,
-            inner_kind, target.init_id, dest_type_id, target.init_block, i, i);
+            context, value_loc_id, value_id, src_type_inst_id, literal_elems,
+            inner_kind, target.init_id, dest_type_inst_id, target.init_block, i,
+            i);
     if (init_id == SemIR::ErrorInst::SingletonInstId) {
       return SemIR::ErrorInst::SingletonInstId;
     }
@@ -459,19 +463,20 @@ static auto ConvertStructToStructOrClass(
         CARBON_FATAL("Only classes should have vptrs.");
       }
       target.init_block->InsertHere();
+      auto vptr_type_id =
+          context.types().GetTypeIdForTypeInstId(dest_field.type_inst_id);
       auto dest_id =
           AddInst<SemIR::ClassElementAccess>(context, value_loc_id,
-                                             {.type_id = dest_field.type_id,
+                                             {.type_id = vptr_type_id,
                                               .base_id = target.init_id,
                                               .index = SemIR::ElementIndex(i)});
       auto vtable_ptr_id = AddInst<SemIR::VtablePtr>(
           context, value_loc_id,
-          {.type_id = dest_field.type_id, .vtable_id = dest_vtable_id});
-      auto init_id =
-          AddInst<SemIR::InitializeFrom>(context, value_loc_id,
-                                         {.type_id = dest_field.type_id,
-                                          .src_id = vtable_ptr_id,
-                                          .dest_id = dest_id});
+          {.type_id = vptr_type_id, .vtable_id = dest_vtable_id});
+      auto init_id = AddInst<SemIR::InitializeFrom>(context, value_loc_id,
+                                                    {.type_id = vptr_type_id,
+                                                     .src_id = vtable_ptr_id,
+                                                     .dest_id = dest_id});
       new_block.Set(i, init_id);
       continue;
     }
@@ -510,10 +515,10 @@ static auto ConvertStructToStructOrClass(
     // approach.
     auto init_id =
         ConvertAggregateElement<SemIR::StructAccess, TargetAccessInstT>(
-            context, value_loc_id, value_id, src_field.type_id, literal_elems,
-            inner_kind, target.init_id, dest_field.type_id, target.init_block,
-            src_field_index, src_field_index + dest_vptr_offset,
-            dest_vtable_id);
+            context, value_loc_id, value_id, src_field.type_inst_id,
+            literal_elems, inner_kind, target.init_id, dest_field.type_inst_id,
+            target.init_block, src_field_index,
+            src_field_index + dest_vptr_offset, dest_vtable_id);
     if (init_id == SemIR::ErrorInst::SingletonInstId) {
       return SemIR::ErrorInst::SingletonInstId;
     }
@@ -990,16 +995,18 @@ static auto PerformBuiltinConversion(
     // A tuple of types converts to type `type`.
     // TODO: This should apply even for non-literal tuples.
     if (auto tuple_literal = value.TryAs<SemIR::TupleLiteral>()) {
-      llvm::SmallVector<SemIR::TypeId> type_ids;
+      llvm::SmallVector<SemIR::InstId> type_inst_ids;
       for (auto tuple_inst_id :
            sem_ir.inst_blocks().Get(tuple_literal->elements_id)) {
         // TODO: This call recurses back into conversion. Switch to an
         // iterative approach.
-        type_ids.push_back(
+        type_inst_ids.push_back(
             ExprAsType(context, loc_id, tuple_inst_id, target.diagnose)
-                .type_id);
+                .inst_id);
       }
-      auto tuple_type_id = GetTupleType(context, type_ids);
+      // TODO: Should we add this as an instruction? It will contain references
+      // to local InstIds.
+      auto tuple_type_id = GetTupleType(context, type_inst_ids);
       return sem_ir.types().GetInstId(tuple_type_id);
     }
 
@@ -1131,9 +1138,10 @@ static auto PerformCopy(Context& context, SemIR::InstId expr_id, bool diagnose)
 
 auto PerformAction(Context& context, SemIR::LocId loc_id,
                    SemIR::ConvertToValueAction action) -> SemIR::InstId {
-  return Convert(
-      context, loc_id, action.inst_id,
-      {.kind = ConversionTarget::Value, .type_id = action.target_type_id});
+  return Convert(context, loc_id, action.inst_id,
+                 {.kind = ConversionTarget::Value,
+                  .type_id = context.types().GetTypeIdForTypeInstId(
+                      action.target_type_inst_id)});
 }
 
 auto Convert(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id,
@@ -1220,12 +1228,13 @@ auto Convert(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id,
       target.kind == ConversionTarget::Value &&
       (OperandIsDependent(context, expr_id) ||
        OperandIsDependent(context, target.type_id))) {
+    auto target_type_inst_id = context.types().GetInstId(target.type_id);
     return AddDependentActionSplice(
         context, loc_id,
         SemIR::ConvertToValueAction{.type_id = SemIR::InstType::SingletonTypeId,
                                     .inst_id = expr_id,
-                                    .target_type_id = target.type_id},
-        target.type_id);
+                                    .target_type_inst_id = target_type_inst_id},
+        target_type_inst_id);
   }
 
   // If this is not a builtin conversion, try an `ImplicitAs` conversion.

+ 1 - 15
toolchain/check/deduce.cpp

@@ -103,7 +103,7 @@ class DeductionWorklist {
     }
     for (auto [param, arg] :
          llvm::reverse(llvm::zip_equal(param_fields, arg_fields))) {
-      Add(param.type_id, arg.type_id, needs_substitution);
+      Add(param.type_inst_id, arg.type_inst_id, needs_substitution);
     }
   }
 
@@ -113,12 +113,6 @@ class DeductionWorklist {
            context_->inst_blocks().Get(args), needs_substitution);
   }
 
-  auto AddAll(SemIR::TypeBlockId params, SemIR::TypeBlockId args,
-              bool needs_substitution) -> void {
-    AddAll(context_->type_blocks().Get(params),
-           context_->type_blocks().Get(args), needs_substitution);
-  }
-
   // Adds a (param, arg) pair for an instruction argument, given its kind.
   auto AddInstArg(SemIR::Inst::ArgAndKind param, int32_t arg,
                   bool needs_substitution) -> void {
@@ -136,10 +130,6 @@ class DeductionWorklist {
         Add(inst_id, SemIR::InstId(arg), needs_substitution);
         break;
       }
-      case CARBON_KIND(SemIR::TypeId type_id): {
-        Add(type_id, SemIR::TypeId(arg), needs_substitution);
-        break;
-      }
       case CARBON_KIND(SemIR::StructTypeFieldsId fields_id): {
         AddAll(fields_id, SemIR::StructTypeFieldsId(arg), needs_substitution);
         break;
@@ -148,10 +138,6 @@ class DeductionWorklist {
         AddAll(inst_block_id, SemIR::InstBlockId(arg), needs_substitution);
         break;
       }
-      case CARBON_KIND(SemIR::TypeBlockId type_block_id): {
-        AddAll(type_block_id, SemIR::TypeBlockId(arg), needs_substitution);
-        break;
-      }
       case CARBON_KIND(SemIR::SpecificId specific_id): {
         Add(specific_id, SemIR::SpecificId(arg), needs_substitution);
         break;

+ 0 - 6
toolchain/check/dump.cpp

@@ -170,12 +170,6 @@ LLVM_DUMP_METHOD static auto Dump(
   return SemIR::Dump(context.sem_ir(), struct_type_fields_id);
 }
 
-LLVM_DUMP_METHOD static auto Dump(const Context& context,
-                                  SemIR::TypeBlockId type_block_id)
-    -> std::string {
-  return SemIR::Dump(context.sem_ir(), type_block_id);
-}
-
 LLVM_DUMP_METHOD static auto Dump(const Context& context, SemIR::TypeId type_id)
     -> std::string {
   return SemIR::Dump(context.sem_ir(), type_id);

+ 6 - 38
toolchain/check/eval.cpp

@@ -172,9 +172,6 @@ class EvalContext {
   auto specifics() -> const SemIR::SpecificStore& {
     return sem_ir().specifics();
   }
-  auto type_blocks() -> SemIR::BlockValueStore<SemIR::TypeBlockId>& {
-    return sem_ir().type_blocks();
-  }
   auto insts() -> const SemIR::InstStore& { return sem_ir().insts(); }
   auto inst_blocks() -> SemIR::InstBlockStore& {
     return sem_ir().inst_blocks();
@@ -489,8 +486,9 @@ static auto GetConstantValue(EvalContext& eval_context,
   auto fields = eval_context.context().struct_type_fields().Get(fields_id);
   llvm::SmallVector<SemIR::StructTypeField> new_fields;
   for (auto field : fields) {
-    auto new_type_id = GetConstantValue(eval_context, field.type_id, phase);
-    if (!new_type_id.has_value()) {
+    auto new_type_inst_id =
+        GetConstantValue(eval_context, field.type_inst_id, phase);
+    if (!new_type_inst_id.has_value()) {
       return SemIR::StructTypeFieldsId::None;
     }
 
@@ -501,43 +499,14 @@ static auto GetConstantValue(EvalContext& eval_context,
       new_fields.reserve(fields.size());
     }
 
-    new_fields.push_back({.name_id = field.name_id, .type_id = new_type_id});
+    new_fields.push_back(
+        {.name_id = field.name_id, .type_inst_id = new_type_inst_id});
   }
   // TODO: If the new block is identical to the original block, and we know the
   // old ID was canonical, return the original ID.
   return eval_context.context().struct_type_fields().AddCanonical(new_fields);
 }
 
-// Compute the constant value of a type block. This may be different from the
-// input type block if we have known generic arguments.
-static auto GetConstantValue(EvalContext& eval_context,
-                             SemIR::TypeBlockId type_block_id, Phase* phase)
-    -> SemIR::TypeBlockId {
-  if (!type_block_id.has_value()) {
-    return SemIR::TypeBlockId::None;
-  }
-  auto types = eval_context.type_blocks().Get(type_block_id);
-  llvm::SmallVector<SemIR::TypeId> new_types;
-  for (auto type_id : types) {
-    auto new_type_id = GetConstantValue(eval_context, type_id, phase);
-    if (!new_type_id.has_value()) {
-      return SemIR::TypeBlockId::None;
-    }
-
-    // Once we leave the small buffer, we know the first few elements are all
-    // constant, so it's likely that the entire block is constant. Resize to the
-    // target size given that we're going to allocate memory now anyway.
-    if (new_types.size() == new_types.capacity()) {
-      new_types.reserve(types.size());
-    }
-
-    new_types.push_back(new_type_id);
-  }
-  // TODO: If the new block is identical to the original block, and we know the
-  // old ID was canonical, return the original ID.
-  return eval_context.type_blocks().AddCanonical(new_types);
-}
-
 // The constant value of a specific is the specific with the corresponding
 // constant values for its arguments.
 static auto GetConstantValue(EvalContext& eval_context,
@@ -739,8 +708,7 @@ static auto GetConstantValueForArg(EvalContext& eval_context,
 static auto ReplaceAllFieldsWithConstantValues(EvalContext& eval_context,
                                                SemIR::Inst* inst, Phase* phase)
     -> bool {
-  auto type_id = SemIR::TypeId(
-      GetConstantValueForArg(eval_context, inst->type_id_and_kind(), phase));
+  auto type_id = GetConstantValue(eval_context, inst->type_id(), phase);
   inst->SetType(type_id);
   if (!IsConstant(*phase)) {
     return false;

+ 9 - 6
toolchain/check/eval_inst.cpp

@@ -7,6 +7,7 @@
 #include <variant>
 
 #include "toolchain/check/action.h"
+#include "toolchain/check/diagnostic_helpers.h"
 #include "toolchain/check/facet_type.h"
 #include "toolchain/check/generic.h"
 #include "toolchain/check/impl_lookup.h"
@@ -312,24 +313,26 @@ auto EvalConstantInst(Context& context, SemIR::InstId inst_id,
       GetSingletonType(context, SemIR::WitnessType::SingletonInstId);
 
   // If the type is a concrete constant, require it to be complete now.
-  auto complete_type_id = inst.complete_type_id;
-  if (context.types().GetConstantId(complete_type_id).is_concrete()) {
+  auto complete_type_id =
+      context.types().GetTypeIdForTypeInstId(inst.complete_type_inst_id);
+  if (complete_type_id.is_concrete()) {
     if (!TryToCompleteType(context, complete_type_id, inst_id, [&] {
           CARBON_DIAGNOSTIC(IncompleteTypeInMonomorphization, Error,
                             "{0} evaluates to incomplete type {1}",
-                            SemIR::TypeId, SemIR::TypeId);
+                            InstIdAsType, InstIdAsType);
           return context.emitter().Build(
               inst_id, IncompleteTypeInMonomorphization,
               context.insts()
                   .GetAs<SemIR::RequireCompleteType>(inst_id)
-                  .complete_type_id,
-              complete_type_id);
+                  .complete_type_inst_id,
+              inst.complete_type_inst_id);
         })) {
       return ConstantEvalResult::Error;
     }
     return ConstantEvalResult::NewSamePhase(SemIR::CompleteTypeWitness{
         .type_id = witness_type_id,
-        .object_repr_id = context.types().GetObjectRepr(complete_type_id)});
+        .object_repr_type_inst_id = context.types().GetInstId(
+            context.types().GetObjectRepr(complete_type_id))});
   }
 
   // If it's not a concrete constant, require it to be complete once it

+ 8 - 7
toolchain/check/handle_choice.cpp

@@ -274,16 +274,17 @@ auto HandleParseNode(Context& context, Parse::ChoiceDefinitionId node_id)
   llvm::SmallVector<SemIR::StructTypeField, 1> struct_type_fields;
   struct_type_fields.push_back({
       .name_id = SemIR::NameId::ChoiceDiscriminant,
-      .type_id = discriminant_type_id,
+      .type_inst_id = context.types().GetInstId(discriminant_type_id),
   });
   auto fields_id =
       context.struct_type_fields().AddCanonical(struct_type_fields);
-  auto choice_witness_id =
-      AddInst(context, node_id,
-              SemIR::CompleteTypeWitness{
-                  .type_id = GetSingletonType(
-                      context, SemIR::WitnessType::SingletonInstId),
-                  .object_repr_id = GetStructType(context, fields_id)});
+  auto choice_witness_id = AddInst(
+      context, node_id,
+      SemIR::CompleteTypeWitness{
+          .type_id =
+              GetSingletonType(context, SemIR::WitnessType::SingletonInstId),
+          .object_repr_type_inst_id =
+              context.types().GetInstId(GetStructType(context, fields_id))});
   // Note: avoid storing a reference to the returned Class, since it may be
   // invalidated by other type constructions.
   context.classes().Get(class_id).complete_type_witness_id = choice_witness_id;

+ 5 - 4
toolchain/check/handle_pattern_list.cpp

@@ -75,12 +75,13 @@ auto HandleParseNode(Context& context, Parse::TuplePatternId node_id) -> bool {
       .PopAndDiscardSoloNodeId<Parse::NodeKind::TuplePatternStart>();
 
   const auto& inst_block = context.inst_blocks().Get(refs_id);
-  llvm::SmallVector<SemIR::TypeId> type_ids;
-  type_ids.reserve(inst_block.size());
+  llvm::SmallVector<SemIR::InstId> type_inst_ids;
+  type_inst_ids.reserve(inst_block.size());
   for (auto inst : inst_block) {
-    type_ids.push_back(context.insts().Get(inst).type_id());
+    type_inst_ids.push_back(
+        context.types().GetInstId(context.insts().Get(inst).type_id()));
   }
-  auto type_id = GetTupleType(context, type_ids);
+  auto type_id = GetTupleType(context, type_inst_ids);
   context.node_stack().Push(
       node_id,
       AddPatternInst<SemIR::TuplePattern>(

+ 6 - 4
toolchain/check/handle_struct.cpp

@@ -54,9 +54,10 @@ auto HandleParseNode(Context& context, Parse::StructLiteralFieldId node_id)
   auto name_id = context.node_stack().Peek<Parse::NodeCategory::MemberName>();
 
   // Store the name for the type.
-  auto value_type_id = context.insts().Get(value_inst_id).type_id();
+  auto value_type_inst_id =
+      context.types().GetInstId(context.insts().Get(value_inst_id).type_id());
   context.struct_type_fields_stack().AppendToTop(
-      {.name_id = name_id, .type_id = value_type_id});
+      {.name_id = name_id, .type_inst_id = value_type_inst_id});
 
   // Push the value back on the stack as an argument.
   context.node_stack().Push(node_id, value_inst_id);
@@ -66,12 +67,13 @@ auto HandleParseNode(Context& context, Parse::StructLiteralFieldId node_id)
 auto HandleParseNode(Context& context,
                      Parse::StructTypeLiteralFieldId /*node_id*/) -> bool {
   auto [type_node, type_id] = context.node_stack().PopExprWithNodeId();
-  SemIR::TypeId cast_type_id = ExprAsType(context, type_node, type_id).type_id;
+  SemIR::InstId cast_type_inst_id =
+      ExprAsType(context, type_node, type_id).inst_id;
   // Get the name while leaving it on the stack.
   auto name_id = context.node_stack().Peek<Parse::NodeCategory::MemberName>();
 
   context.struct_type_fields_stack().AppendToTop(
-      {.name_id = name_id, .type_id = cast_type_id});
+      {.name_id = name_id, .type_inst_id = cast_type_inst_id});
   return true;
 }
 

+ 5 - 4
toolchain/check/handle_tuple_literal.cpp

@@ -29,12 +29,13 @@ auto HandleParseNode(Context& context, Parse::TupleLiteralId node_id) -> bool {
   context.node_stack()
       .PopAndDiscardSoloNodeId<Parse::NodeKind::TupleLiteralStart>();
   const auto& inst_block = context.inst_blocks().Get(refs_id);
-  llvm::SmallVector<SemIR::TypeId> type_ids;
-  type_ids.reserve(inst_block.size());
+  llvm::SmallVector<SemIR::InstId> type_inst_ids;
+  type_inst_ids.reserve(inst_block.size());
   for (auto inst : inst_block) {
-    type_ids.push_back(context.insts().Get(inst).type_id());
+    type_inst_ids.push_back(
+        context.types().GetInstId(context.insts().Get(inst).type_id()));
   }
-  auto type_id = GetTupleType(context, type_ids);
+  auto type_id = GetTupleType(context, type_inst_ids);
 
   auto value_id = AddInst<SemIR::TupleLiteral>(
       context, node_id, {.type_id = type_id, .elements_id = refs_id});

+ 1 - 1
toolchain/check/import_cpp.cpp

@@ -524,7 +524,7 @@ static auto ImportCXXRecordDecl(Context& context, SemIR::LocId loc_id,
   // The class type is now fully defined. Compute its object representation.
   ComputeClassObjectRepr(context,
                          // TODO: Consider having a proper location here.
-                         Parse::NodeId::None, class_id,
+                         Parse::ClassDefinitionId::None, class_id,
                          // TODO: Set fields.
                          /*field_decls=*/{},
                          // TODO: Set vtable.

+ 23 - 39
toolchain/check/import_ref.cpp

@@ -288,9 +288,6 @@ class ImportContext {
   auto import_struct_type_fields() -> decltype(auto) {
     return import_ir().struct_type_fields();
   }
-  auto import_type_blocks() -> decltype(auto) {
-    return import_ir().type_blocks();
-  }
   auto import_types() -> decltype(auto) { return import_ir().types(); }
 
   // Returns the local file's import ID for the IR we are importing from.
@@ -1890,18 +1887,16 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
     -> ResolveResult {
   CARBON_CHECK(resolver.import_types().GetInstId(inst.type_id) ==
                SemIR::WitnessType::SingletonInstId);
-  auto object_repr_const_id = GetLocalConstantId(resolver, inst.object_repr_id);
+  auto object_repr_type_inst_id =
+      GetLocalConstantInstId(resolver, inst.object_repr_type_inst_id);
   if (resolver.HasNewWork()) {
     return ResolveResult::Retry();
   }
-  auto object_repr_id =
-      resolver.local_context().types().GetTypeIdForTypeConstantId(
-          object_repr_const_id);
   return ResolveAs<SemIR::CompleteTypeWitness>(
       resolver,
       {.type_id = GetSingletonType(resolver.local_context(),
                                    SemIR::WitnessType::SingletonInstId),
-       .object_repr_id = object_repr_id});
+       .object_repr_type_inst_id = object_repr_type_inst_id});
 }
 
 static auto TryResolveTypedInst(ImportRefResolver& resolver,
@@ -2677,20 +2672,17 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
   CARBON_CHECK(resolver.import_types().GetInstId(inst.type_id) ==
                SemIR::WitnessType::SingletonInstId);
 
-  auto complete_type_const_id =
-      GetLocalConstantId(resolver, inst.complete_type_id);
+  auto complete_type_inst_id =
+      GetLocalConstantInstId(resolver, inst.complete_type_inst_id);
   if (resolver.HasNewWork()) {
     return ResolveResult::Retry();
   }
 
-  auto complete_type_id =
-      resolver.local_context().types().GetTypeIdForTypeConstantId(
-          complete_type_const_id);
   return ResolveAs<SemIR::RequireCompleteType>(
       resolver,
       {.type_id = GetSingletonType(resolver.local_context(),
                                    SemIR::WitnessType::SingletonInstId),
-       .complete_type_id = complete_type_id});
+       .complete_type_inst_id = complete_type_inst_id});
 }
 
 static auto TryResolveTypedInst(ImportRefResolver& resolver,
@@ -2733,10 +2725,11 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
                                 SemIR::StructType inst) -> ResolveResult {
   CARBON_CHECK(inst.type_id == SemIR::TypeType::SingletonTypeId);
   auto orig_fields = resolver.import_struct_type_fields().Get(inst.fields_id);
-  llvm::SmallVector<SemIR::ConstantId> field_const_ids;
-  field_const_ids.reserve(orig_fields.size());
+  llvm::SmallVector<SemIR::InstId> field_type_inst_ids;
+  field_type_inst_ids.reserve(orig_fields.size());
   for (auto field : orig_fields) {
-    field_const_ids.push_back(GetLocalConstantId(resolver, field.type_id));
+    field_type_inst_ids.push_back(
+        GetLocalConstantInstId(resolver, field.type_inst_id));
   }
   if (resolver.HasNewWork()) {
     return ResolveResult::Retry();
@@ -2745,13 +2738,11 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
   // Prepare a vector of fields for GetStructType.
   llvm::SmallVector<SemIR::StructTypeField> new_fields;
   new_fields.reserve(orig_fields.size());
-  for (auto [orig_field, field_const_id] :
-       llvm::zip(orig_fields, field_const_ids)) {
+  for (auto [orig_field, field_type_inst_id] :
+       llvm::zip(orig_fields, field_type_inst_ids)) {
     auto name_id = GetLocalNameId(resolver, orig_field.name_id);
-    auto field_type_id =
-        resolver.local_context().types().GetTypeIdForTypeConstantId(
-            field_const_id);
-    new_fields.push_back({.name_id = name_id, .type_id = field_type_id});
+    new_fields.push_back(
+        {.name_id = name_id, .type_inst_id = field_type_inst_id});
   }
 
   return ResolveAs<SemIR::StructType>(
@@ -2780,27 +2771,20 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
                                 SemIR::TupleType inst) -> ResolveResult {
   CARBON_CHECK(inst.type_id == SemIR::TypeType::SingletonTypeId);
 
-  auto orig_elem_type_ids = resolver.import_type_blocks().Get(inst.elements_id);
-  llvm::SmallVector<SemIR::ConstantId> elem_const_ids;
-  elem_const_ids.reserve(orig_elem_type_ids.size());
-  for (auto elem_type_id : orig_elem_type_ids) {
-    elem_const_ids.push_back(GetLocalConstantId(resolver, elem_type_id));
+  auto orig_element_ids = resolver.import_inst_blocks().Get(inst.elements_id);
+  llvm::SmallVector<SemIR::InstId> element_ids;
+  element_ids.reserve(orig_element_ids.size());
+  for (auto elem_type_inst_id : orig_element_ids) {
+    element_ids.push_back(GetLocalConstantInstId(resolver, elem_type_inst_id));
   }
   if (resolver.HasNewWork()) {
     return ResolveResult::Retry();
   }
 
-  // Prepare a vector of the tuple types for GetTupleType.
-  llvm::SmallVector<SemIR::TypeId> elem_type_ids;
-  elem_type_ids.reserve(orig_elem_type_ids.size());
-  for (auto elem_const_id : elem_const_ids) {
-    elem_type_ids.push_back(
-        resolver.local_context().types().GetTypeIdForTypeConstantId(
-            elem_const_id));
-  }
-
-  return ResolveResult::Done(resolver.local_types().GetConstantId(
-      GetTupleType(resolver.local_context(), elem_type_ids)));
+  return ResolveAs<SemIR::TupleType>(
+      resolver, {.type_id = SemIR::TypeType::SingletonTypeId,
+                 .elements_id = GetLocalCanonicalInstBlockId(
+                     resolver, inst.elements_id, element_ids)});
 }
 
 static auto TryResolveTypedInst(ImportRefResolver& resolver,

+ 5 - 3
toolchain/check/member_access.cpp

@@ -555,7 +555,8 @@ static auto PerformActionHelper(Context& context, SemIR::LocId loc_id,
           // binding separately. Perhaps a struct type should be a name scope.
           return GetOrAddInst<SemIR::StructAccess>(
               context, loc_id,
-              {.type_id = field.type_id,
+              {.type_id =
+                   context.types().GetTypeIdForTypeInstId(field.type_inst_id),
                .struct_id = base_id,
                .index = SemIR::ElementIndex(i)});
         }
@@ -797,7 +798,7 @@ auto PerformTupleAccess(Context& context, SemIR::LocId loc_id,
 
   auto index_literal = context.insts().GetAs<SemIR::IntValue>(
       context.constant_values().GetInstId(index_const_id));
-  auto type_block = context.type_blocks().Get(tuple_type->elements_id);
+  auto type_block = context.inst_blocks().Get(tuple_type->elements_id);
   std::optional<llvm::APInt> index_val = ValidateTupleIndex(
       context, loc_id, tuple_inst_id, index_literal, type_block.size());
   if (!index_val) {
@@ -805,7 +806,8 @@ auto PerformTupleAccess(Context& context, SemIR::LocId loc_id,
   }
 
   // TODO: Handle the case when `index_val->getZExtValue()` has too many bits.
-  element_type_id = type_block[index_val->getZExtValue()];
+  element_type_id = context.types().GetTypeIdForTypeInstId(
+      type_block[index_val->getZExtValue()]);
   auto tuple_index = SemIR::ElementIndex(index_val->getZExtValue());
 
   return GetOrAddInst<SemIR::TupleAccess>(context, loc_id,

+ 11 - 8
toolchain/check/pattern_match.cpp

@@ -527,15 +527,18 @@ auto MatchContext::DoEmitPatternMatch(Context& context,
 
   auto tuple_type =
       context.types().GetAs<SemIR::TupleType>(tuple_pattern.type_id);
-  auto element_type_ids = context.type_blocks().Get(tuple_type.elements_id);
+  auto element_type_inst_ids =
+      context.inst_blocks().Get(tuple_type.elements_id);
   llvm::SmallVector<SemIR::InstId> subscrutinee_ids;
-  subscrutinee_ids.reserve(element_type_ids.size());
-  for (auto [i, element_type_id] : llvm::enumerate(element_type_ids)) {
-    subscrutinee_ids.push_back(
-        AddInst<SemIR::TupleAccess>(context, scrutinee.loc_id,
-                                    {.type_id = element_type_id,
-                                     .tuple_id = entry.scrutinee_id,
-                                     .index = SemIR::ElementIndex(i)}));
+  subscrutinee_ids.reserve(element_type_inst_ids.size());
+  for (auto [i, element_type_inst_id] :
+       llvm::enumerate(element_type_inst_ids)) {
+    subscrutinee_ids.push_back(AddInst<SemIR::TupleAccess>(
+        context, scrutinee.loc_id,
+        {.type_id =
+             context.types().GetTypeIdForTypeInstId(element_type_inst_id),
+         .tuple_id = entry.scrutinee_id,
+         .index = SemIR::ElementIndex(i)}));
   }
   add_all_subscrutinees(subscrutinee_ids);
 }

+ 10 - 36
toolchain/check/subst.cpp

@@ -89,25 +89,13 @@ static auto PushOperand(Context& context, Worklist& worklist,
       }
       break;
     }
-    case CARBON_KIND(SemIR::TypeId type_id): {
-      if (type_id.has_value()) {
-        worklist.Push(context.types().GetInstId(type_id));
-      }
-      break;
-    }
     case CARBON_KIND(SemIR::InstBlockId inst_block_id): {
       push_block(inst_block_id);
       break;
     }
     case CARBON_KIND(SemIR::StructTypeFieldsId fields_id): {
       for (auto field : context.struct_type_fields().Get(fields_id)) {
-        worklist.Push(context.types().GetInstId(field.type_id));
-      }
-      break;
-    }
-    case CARBON_KIND(SemIR::TypeBlockId type_block_id): {
-      for (auto type_id : context.type_blocks().Get(type_block_id)) {
-        worklist.Push(context.types().GetInstId(type_id));
+        worklist.Push(field.type_inst_id);
       }
       break;
     }
@@ -145,7 +133,9 @@ static auto PushOperand(Context& context, Worklist& worklist,
 static auto ExpandOperands(Context& context, Worklist& worklist,
                            SemIR::InstId inst_id) -> void {
   auto inst = context.insts().Get(inst_id);
-  PushOperand(context, worklist, inst.type_id_and_kind());
+  if (inst.type_id().has_value()) {
+    worklist.Push(context.types().GetInstId(inst.type_id()));
+  }
   PushOperand(context, worklist, inst.arg0_and_kind());
   PushOperand(context, worklist, inst.arg1_and_kind());
 }
@@ -185,12 +175,6 @@ static auto PopOperand(Context& context, Worklist& worklist,
       }
       return worklist.Pop().index;
     }
-    case CARBON_KIND(SemIR::TypeId type_id): {
-      if (!type_id.has_value()) {
-        return arg.value();
-      }
-      return context.types().GetTypeIdForTypeInstId(worklist.Pop()).index;
-    }
     case CARBON_KIND(SemIR::InstBlockId inst_block_id): {
       return pop_block_id(inst_block_id).index;
     }
@@ -200,21 +184,10 @@ static auto PopOperand(Context& context, Worklist& worklist,
                                                          old_fields_id);
       for (auto i : llvm::reverse(llvm::seq(old_fields.size()))) {
         new_fields.Set(i, {.name_id = old_fields[i].name_id,
-                           .type_id = context.types().GetTypeIdForTypeInstId(
-                               worklist.Pop())});
+                           .type_inst_id = worklist.Pop()});
       }
       return new_fields.GetCanonical().index;
     }
-    case CARBON_KIND(SemIR::TypeBlockId old_type_block_id): {
-      auto size = context.type_blocks().Get(old_type_block_id).size();
-      SemIR::CopyOnWriteTypeBlock new_type_block(&context.sem_ir(),
-                                                 old_type_block_id);
-      for (auto i : llvm::reverse(llvm::seq(size))) {
-        new_type_block.Set(
-            i, context.types().GetTypeIdForTypeInstId(worklist.Pop()));
-      }
-      return new_type_block.GetCanonical().index;
-    }
     case CARBON_KIND(SemIR::SpecificId specific_id): {
       return pop_specific(specific_id).index;
     }
@@ -281,14 +254,15 @@ static auto Rebuild(Context& context, Worklist& worklist, SemIR::InstId inst_id,
   // Note that we pop in reverse order because we pushed them in forwards order.
   int32_t arg1 = PopOperand(context, worklist, inst.arg1_and_kind());
   int32_t arg0 = PopOperand(context, worklist, inst.arg0_and_kind());
-  int32_t type_id = PopOperand(context, worklist, inst.type_id_and_kind());
-  if (type_id == inst.type_id().index && arg0 == inst.arg0() &&
-      arg1 == inst.arg1()) {
+  auto type_id = inst.type_id().has_value()
+                     ? context.types().GetTypeIdForTypeInstId(worklist.Pop())
+                     : SemIR::TypeId::None;
+  if (type_id == inst.type_id() && arg0 == inst.arg0() && arg1 == inst.arg1()) {
     return callbacks.ReuseUnchanged(inst_id);
   }
 
   // TODO: Do we need to require this type to be complete?
-  inst.SetType(SemIR::TypeId(type_id));
+  inst.SetType(type_id);
   inst.SetArgs(arg0, arg1);
   return callbacks.Rebuild(inst_id, inst);
 }

+ 1 - 0
toolchain/check/testdata/alias/no_prelude/alias_of_alias.carbon

@@ -50,6 +50,7 @@ let d: c = {};
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @C {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 1 - 0
toolchain/check/testdata/alias/no_prelude/export_name.carbon

@@ -88,6 +88,7 @@ var d: D* = &c;
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @C {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 2 - 0
toolchain/check/testdata/alias/no_prelude/fail_aliased_name_in_diag.carbon

@@ -58,6 +58,7 @@ let c_var: c = d;
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @C {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -66,6 +67,7 @@ let c_var: c = d;
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @D {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 1 - 0
toolchain/check/testdata/alias/no_prelude/fail_modifiers.carbon

@@ -74,6 +74,7 @@ extern alias C = Class;
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @Class {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 1 - 0
toolchain/check/testdata/alias/no_prelude/fail_name_conflict.carbon

@@ -67,6 +67,7 @@ alias b = C;
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @C {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 1 - 0
toolchain/check/testdata/alias/no_prelude/import.carbon

@@ -98,6 +98,7 @@ var c: () = a_alias_alias;
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @C {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 1 - 0
toolchain/check/testdata/alias/no_prelude/import_access.carbon

@@ -72,6 +72,7 @@ var inst: Test.A = {};
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @C {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 1 - 0
toolchain/check/testdata/alias/no_prelude/import_order.carbon

@@ -65,6 +65,7 @@ var a_val: a = {.v = b_val.v};
 // CHECK:STDOUT:     %.loc4_11: %C.elem = var_pattern %.loc4_16
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.var: ref %C.elem = var <none>
+// CHECK:STDOUT:   %struct_type.v: type = struct_type {.v: %empty_tuple.type} [concrete = constants.%struct_type.v]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.v [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 1 - 0
toolchain/check/testdata/alias/no_prelude/in_namespace.carbon

@@ -79,6 +79,7 @@ fn F() -> NS.a {
 // CHECK:STDOUT:     %.loc11_11: %C.elem = var_pattern %.loc11_16
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.var: ref %C.elem = var <none>
+// CHECK:STDOUT:   %struct_type.v: type = struct_type {.v: %empty_tuple.type} [concrete = constants.%struct_type.v]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.v [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 49 - 14
toolchain/check/testdata/array/generic_empty.carbon

@@ -13,6 +13,10 @@ fn G(T:! type) {
   var arr: array(T, 0) = ();
 }
 
+fn H() {
+  G(i32);
+}
+
 // CHECK:STDOUT: --- generic_empty.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
@@ -22,13 +26,22 @@ fn G(T:! type) {
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
 // CHECK:STDOUT:   %G: %G.type = struct_value () [concrete]
 // CHECK:STDOUT:   %int_0: Core.IntLiteral = int_value 0 [concrete]
-// CHECK:STDOUT:   %array_type: type = array_type %int_0, %T [symbolic]
-// CHECK:STDOUT:   %require_complete: <witness> = require_complete_type %array_type [symbolic]
-// CHECK:STDOUT:   %array: %array_type = tuple_value () [symbolic]
+// CHECK:STDOUT:   %array_type.281: type = array_type %int_0, %T [symbolic]
+// CHECK:STDOUT:   %require_complete.b7f: <witness> = require_complete_type %array_type.281 [symbolic]
+// CHECK:STDOUT:   %array.2ed: %array_type.281 = tuple_value () [symbolic]
+// CHECK:STDOUT:   %H.type: type = fn_type @H [concrete]
+// CHECK:STDOUT:   %H: %H.type = struct_value () [concrete]
+// CHECK:STDOUT:   %int_32: Core.IntLiteral = int_value 32 [concrete]
+// CHECK:STDOUT:   %i32: type = class_type @Int, @Int(%int_32) [concrete]
+// CHECK:STDOUT:   %G.specific_fn: <specific function> = specific_function %G, @G(%i32) [concrete]
+// CHECK:STDOUT:   %array_type.f5c: type = array_type %int_0, %i32 [concrete]
+// CHECK:STDOUT:   %complete_type.cff: <witness> = complete_type_witness %array_type.f5c [concrete]
+// CHECK:STDOUT:   %array.812: %array_type.f5c = tuple_value () [concrete]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
 // CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [concrete] {
+// CHECK:STDOUT:     .Int = %Core.Int
 // CHECK:STDOUT:     import Core//prelude
 // CHECK:STDOUT:     import Core//prelude/...
 // CHECK:STDOUT:   }
@@ -38,6 +51,7 @@ fn G(T:! type) {
 // CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
 // CHECK:STDOUT:     .Core = imports.%Core
 // CHECK:STDOUT:     .G = %G.decl
+// CHECK:STDOUT:     .H = %H.decl
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Core.import = import Core
 // CHECK:STDOUT:   %G.decl: %G.type = fn_decl @G [concrete = constants.%G] {
@@ -45,6 +59,7 @@ fn G(T:! type) {
 // CHECK:STDOUT:   } {
 // CHECK:STDOUT:     %T.loc11_6.1: type = bind_symbolic_name T, 0 [symbolic = %T.loc11_6.2 (constants.%T)]
 // CHECK:STDOUT:   }
+// CHECK:STDOUT:   %H.decl: %H.type = fn_decl @H [concrete = constants.%H] {} {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: generic fn @G(%T.loc11_6.1: type) {
@@ -52,33 +67,53 @@ fn G(T:! type) {
 // CHECK:STDOUT:   %T.patt.loc11_6.2: type = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc11_6.2 (constants.%T.patt)]
 // CHECK:STDOUT:
 // CHECK:STDOUT: !definition:
-// CHECK:STDOUT:   %array_type.loc13_22.2: type = array_type constants.%int_0, %T.loc11_6.2 [symbolic = %array_type.loc13_22.2 (constants.%array_type)]
-// CHECK:STDOUT:   %require_complete: <witness> = require_complete_type @G.%array_type.loc13_22.2 (%array_type) [symbolic = %require_complete (constants.%require_complete)]
-// CHECK:STDOUT:   %array: @G.%array_type.loc13_22.2 (%array_type) = tuple_value () [symbolic = %array (constants.%array)]
+// CHECK:STDOUT:   %array_type.loc13_22.2: type = array_type constants.%int_0, %T.loc11_6.2 [symbolic = %array_type.loc13_22.2 (constants.%array_type.281)]
+// CHECK:STDOUT:   %require_complete: <witness> = require_complete_type %array_type.loc13_22.2 [symbolic = %require_complete (constants.%require_complete.b7f)]
+// CHECK:STDOUT:   %array: @G.%array_type.loc13_22.2 (%array_type.281) = tuple_value () [symbolic = %array (constants.%array.2ed)]
 // CHECK:STDOUT:
 // CHECK:STDOUT:   fn(%T.patt.loc11_6.1: type) {
 // CHECK:STDOUT:   !entry:
 // CHECK:STDOUT:     name_binding_decl {
-// CHECK:STDOUT:       %arr.patt: @G.%array_type.loc13_22.2 (%array_type) = binding_pattern arr
-// CHECK:STDOUT:       %.loc13_3.1: @G.%array_type.loc13_22.2 (%array_type) = var_pattern %arr.patt
+// CHECK:STDOUT:       %arr.patt: @G.%array_type.loc13_22.2 (%array_type.281) = binding_pattern arr
+// CHECK:STDOUT:       %.loc13_3.1: @G.%array_type.loc13_22.2 (%array_type.281) = var_pattern %arr.patt
 // CHECK:STDOUT:     }
-// CHECK:STDOUT:     %arr.var: ref @G.%array_type.loc13_22.2 (%array_type) = var arr
+// CHECK:STDOUT:     %arr.var: ref @G.%array_type.loc13_22.2 (%array_type.281) = var arr
 // CHECK:STDOUT:     %.loc13_27.1: %empty_tuple.type = tuple_literal ()
-// CHECK:STDOUT:     %.loc13_27.2: init @G.%array_type.loc13_22.2 (%array_type) = array_init () to %arr.var [symbolic = %array (constants.%array)]
-// CHECK:STDOUT:     %.loc13_3.2: init @G.%array_type.loc13_22.2 (%array_type) = converted %.loc13_27.1, %.loc13_27.2 [symbolic = %array (constants.%array)]
+// CHECK:STDOUT:     %.loc13_27.2: init @G.%array_type.loc13_22.2 (%array_type.281) = array_init () to %arr.var [symbolic = %array (constants.%array.2ed)]
+// CHECK:STDOUT:     %.loc13_3.2: init @G.%array_type.loc13_22.2 (%array_type.281) = converted %.loc13_27.1, %.loc13_27.2 [symbolic = %array (constants.%array.2ed)]
 // CHECK:STDOUT:     assign %arr.var, %.loc13_3.2
-// CHECK:STDOUT:     %.loc13_22: type = splice_block %array_type.loc13_22.1 [symbolic = %array_type.loc13_22.2 (constants.%array_type)] {
+// CHECK:STDOUT:     %.loc13_22: type = splice_block %array_type.loc13_22.1 [symbolic = %array_type.loc13_22.2 (constants.%array_type.281)] {
 // CHECK:STDOUT:       %T.ref: type = name_ref T, %T.loc11_6.1 [symbolic = %T.loc11_6.2 (constants.%T)]
 // CHECK:STDOUT:       %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0]
-// CHECK:STDOUT:       %array_type.loc13_22.1: type = array_type %int_0, %T.ref [symbolic = %array_type.loc13_22.2 (constants.%array_type)]
+// CHECK:STDOUT:       %array_type.loc13_22.1: type = array_type %int_0, %T.ref [symbolic = %array_type.loc13_22.2 (constants.%array_type.281)]
 // CHECK:STDOUT:     }
-// CHECK:STDOUT:     %arr: ref @G.%array_type.loc13_22.2 (%array_type) = bind_name arr, %arr.var
+// CHECK:STDOUT:     %arr: ref @G.%array_type.loc13_22.2 (%array_type.281) = bind_name arr, %arr.var
 // CHECK:STDOUT:     return
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: fn @H() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %G.ref: %G.type = name_ref G, file.%G.decl [concrete = constants.%G]
+// CHECK:STDOUT:   %int_32: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
+// CHECK:STDOUT:   %i32: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
+// CHECK:STDOUT:   %G.specific_fn: <specific function> = specific_function %G.ref, @G(constants.%i32) [concrete = constants.%G.specific_fn]
+// CHECK:STDOUT:   %G.call: init %empty_tuple.type = call %G.specific_fn()
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
 // CHECK:STDOUT: specific @G(constants.%T) {
 // CHECK:STDOUT:   %T.loc11_6.2 => constants.%T
 // CHECK:STDOUT:   %T.patt.loc11_6.2 => constants.%T.patt
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: specific @G(constants.%i32) {
+// CHECK:STDOUT:   %T.loc11_6.2 => constants.%i32
+// CHECK:STDOUT:   %T.patt.loc11_6.2 => constants.%T.patt
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %array_type.loc13_22.2 => constants.%array_type.f5c
+// CHECK:STDOUT:   %require_complete => constants.%complete_type.cff
+// CHECK:STDOUT:   %array => constants.%array.812
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 1 - 1
toolchain/check/testdata/array/init_dependent_bound.carbon

@@ -98,7 +98,7 @@ fn H() { G(3); }
 // CHECK:STDOUT:   %bound_method.loc9_23.3: <bound method> = bound_method %N.loc4_6.2, constants.%Convert.specific_fn [symbolic = %bound_method.loc9_23.3 (constants.%bound_method)]
 // CHECK:STDOUT:   %int.convert_checked.loc9_23.2: init Core.IntLiteral = call %bound_method.loc9_23.3(%N.loc4_6.2) [symbolic = %int.convert_checked.loc9_23.2 (constants.%int.convert_checked)]
 // CHECK:STDOUT:   %array_type.loc9_24.2: type = array_type %int.convert_checked.loc9_23.2, constants.%i32 [symbolic = %array_type.loc9_24.2 (constants.%array_type)]
-// CHECK:STDOUT:   %require_complete: <witness> = require_complete_type @F.%array_type.loc9_24.2 (%array_type) [symbolic = %require_complete (constants.%require_complete.7cb)]
+// CHECK:STDOUT:   %require_complete: <witness> = require_complete_type %array_type.loc9_24.2 [symbolic = %require_complete (constants.%require_complete.7cb)]
 // CHECK:STDOUT:
 // CHECK:STDOUT:   fn(%N.patt.loc4_6.1: %i32) {
 // CHECK:STDOUT:   !entry:

+ 21 - 15
toolchain/check/testdata/as/adapter_conversion.carbon

@@ -272,7 +272,8 @@ var b: B = {.x = 1} as B;
 // CHECK:STDOUT:     %return.param: ref %A = out_param call_param0
 // CHECK:STDOUT:     %return: ref %A = return_slot %return.param
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.x.y.871 [concrete = constants.%complete_type.70a]
+// CHECK:STDOUT:   %struct_type.x.y: type = struct_type {.x: %i32, .y: %i32} [concrete = constants.%struct_type.x.y.871]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.x.y [concrete = constants.%complete_type.70a]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -286,7 +287,7 @@ var b: B = {.x = 1} as B;
 // CHECK:STDOUT: class @B {
 // CHECK:STDOUT:   %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A]
 // CHECK:STDOUT:   adapt_decl %A.ref [concrete]
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.x.y.871 [concrete = constants.%complete_type.70a]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness constants.%struct_type.x.y.871 [concrete = constants.%complete_type.70a]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -425,7 +426,7 @@ var b: B = {.x = 1} as B;
 // CHECK:STDOUT:   %int_32: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
 // CHECK:STDOUT:   %i32: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
 // CHECK:STDOUT:   adapt_decl %i32 [concrete]
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %i32.builtin [concrete = constants.%complete_type.f8a]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness constants.%i32.builtin [concrete = constants.%complete_type.f8a]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -500,7 +501,7 @@ var b: B = {.x = 1} as B;
 // CHECK:STDOUT:   %.loc4_18: %empty_struct_type = struct_literal ()
 // CHECK:STDOUT:   %.loc4_19: type = converted %.loc4_18, constants.%empty_struct_type [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   adapt_decl %.loc4_19 [concrete]
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -510,7 +511,7 @@ var b: B = {.x = 1} as B;
 // CHECK:STDOUT: class @B {
 // CHECK:STDOUT:   %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A]
 // CHECK:STDOUT:   adapt_decl %A.ref [concrete]
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -521,7 +522,7 @@ var b: B = {.x = 1} as B;
 // CHECK:STDOUT: class @C {
 // CHECK:STDOUT:   %B.ref: type = name_ref B, file.%B.decl [concrete = constants.%B]
 // CHECK:STDOUT:   adapt_decl %B.ref [concrete]
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -532,7 +533,7 @@ var b: B = {.x = 1} as B;
 // CHECK:STDOUT: class @D {
 // CHECK:STDOUT:   %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C]
 // CHECK:STDOUT:   adapt_decl %C.ref [concrete]
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -617,7 +618,8 @@ var b: B = {.x = 1} as B;
 // CHECK:STDOUT:     %.loc6_3: %A.elem = var_pattern %.loc6_8
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.var.loc6: ref %A.elem = var <none>
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.x.y.871 [concrete = constants.%complete_type.70a]
+// CHECK:STDOUT:   %struct_type.x.y: type = struct_type {.x: %i32, .y: %i32} [concrete = constants.%struct_type.x.y.871]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.x.y [concrete = constants.%complete_type.70a]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -629,7 +631,7 @@ var b: B = {.x = 1} as B;
 // CHECK:STDOUT: class @B {
 // CHECK:STDOUT:   %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A]
 // CHECK:STDOUT:   adapt_decl %A.ref [concrete]
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.x.y.871 [concrete = constants.%complete_type.70a]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness constants.%struct_type.x.y.871 [concrete = constants.%complete_type.70a]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -738,7 +740,8 @@ var b: B = {.x = 1} as B;
 // CHECK:STDOUT:     %.loc6_3: %A.elem = var_pattern %.loc6_8
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.var.loc6: ref %A.elem = var <none>
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.x.y.871 [concrete = constants.%complete_type.70a]
+// CHECK:STDOUT:   %struct_type.x.y: type = struct_type {.x: %i32, .y: %i32} [concrete = constants.%struct_type.x.y.871]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.x.y [concrete = constants.%complete_type.70a]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -750,7 +753,7 @@ var b: B = {.x = 1} as B;
 // CHECK:STDOUT: class @B {
 // CHECK:STDOUT:   %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A]
 // CHECK:STDOUT:   adapt_decl %A.ref [concrete]
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.x.y.871 [concrete = constants.%complete_type.70a]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness constants.%struct_type.x.y.871 [concrete = constants.%complete_type.70a]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -837,6 +840,7 @@ var b: B = {.x = 1} as B;
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @Noncopyable {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type.357]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -851,7 +855,7 @@ var b: B = {.x = 1} as B;
 // CHECK:STDOUT:   %.loc9_26: %tuple.type.24b = tuple_literal (%i32, %Noncopyable.ref)
 // CHECK:STDOUT:   %.loc9_27: type = converted %.loc9_26, constants.%tuple.type.560 [concrete = constants.%tuple.type.560]
 // CHECK:STDOUT:   adapt_decl %.loc9_27 [concrete]
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %tuple.type.560 [concrete = constants.%complete_type.ce5]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness constants.%tuple.type.560 [concrete = constants.%complete_type.ce5]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -925,6 +929,7 @@ var b: B = {.x = 1} as B;
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @Noncopyable {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type.357]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -939,7 +944,7 @@ var b: B = {.x = 1} as B;
 // CHECK:STDOUT:   %.loc9_26: %tuple.type.24b = tuple_literal (%i32, %Noncopyable.ref)
 // CHECK:STDOUT:   %.loc9_27: type = converted %.loc9_26, constants.%tuple.type.560 [concrete = constants.%tuple.type.560]
 // CHECK:STDOUT:   adapt_decl %.loc9_27 [concrete]
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %tuple.type.560 [concrete = constants.%complete_type.ce5]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness constants.%tuple.type.560 [concrete = constants.%complete_type.ce5]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -1025,7 +1030,8 @@ var b: B = {.x = 1} as B;
 // CHECK:STDOUT:     %.loc5_3: %A.elem = var_pattern %.loc5_8
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.var: ref %A.elem = var <none>
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.x.ed6 [concrete = constants.%complete_type.1ec]
+// CHECK:STDOUT:   %struct_type.x: type = struct_type {.x: %i32} [concrete = constants.%struct_type.x.ed6]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.x [concrete = constants.%complete_type.1ec]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -1036,7 +1042,7 @@ var b: B = {.x = 1} as B;
 // CHECK:STDOUT: class @B {
 // CHECK:STDOUT:   %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A]
 // CHECK:STDOUT:   adapt_decl %A.ref [concrete]
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.x.ed6 [concrete = constants.%complete_type.1ec]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness constants.%struct_type.x.ed6 [concrete = constants.%complete_type.1ec]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:

+ 1 - 0
toolchain/check/testdata/as/identity.carbon

@@ -94,6 +94,7 @@ fn Initializing() {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @X {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 1 - 0
toolchain/check/testdata/as/no_prelude/tuple.carbon

@@ -61,6 +61,7 @@ fn Var() {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @X {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 1 - 0
toolchain/check/testdata/as/overloaded.carbon

@@ -158,6 +158,7 @@ let n: i32 = ((4 as i32) as X) as i32;
 // CHECK:STDOUT:     %.loc12_3: %X.elem = var_pattern %.loc12_8
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.var: ref %X.elem = var <none>
+// CHECK:STDOUT:   %struct_type.n: type = struct_type {.n: %i32} [concrete = constants.%struct_type.n]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.n [concrete = constants.%complete_type.54b]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 0 - 2
toolchain/check/testdata/basics/builtin_insts.carbon

@@ -32,8 +32,6 @@
 // CHECK:STDOUT:       value_repr:      {kind: copy, type: type(Error)}
 // CHECK:STDOUT:     'type(inst(NamespaceType))':
 // CHECK:STDOUT:       value_repr:      {kind: copy, type: type(inst(NamespaceType))}
-// CHECK:STDOUT:   type_blocks:
-// CHECK:STDOUT:     type_block0:     {}
 // CHECK:STDOUT:   insts:
 // CHECK:STDOUT:     'inst(TypeType)':  {kind: TypeType, type: type(TypeType)}
 // CHECK:STDOUT:     'inst(AutoType)':  {kind: AutoType, type: type(TypeType)}

+ 2 - 0
toolchain/check/testdata/builtins/bool/eq.carbon

@@ -214,6 +214,7 @@ var d: C(false == false) = True();
 // CHECK:STDOUT: !definition:
 // CHECK:STDOUT:
 // CHECK:STDOUT:   class {
+// CHECK:STDOUT:     %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:     %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:     complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -427,6 +428,7 @@ var d: C(false == false) = True();
 // CHECK:STDOUT: !definition:
 // CHECK:STDOUT:
 // CHECK:STDOUT:   class {
+// CHECK:STDOUT:     %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:     %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:     complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 2 - 0
toolchain/check/testdata/builtins/bool/neq.carbon

@@ -214,6 +214,7 @@ var d: C(false != false) = False();
 // CHECK:STDOUT: !definition:
 // CHECK:STDOUT:
 // CHECK:STDOUT:   class {
+// CHECK:STDOUT:     %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:     %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:     complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -427,6 +428,7 @@ var d: C(false != false) = False();
 // CHECK:STDOUT: !definition:
 // CHECK:STDOUT:
 // CHECK:STDOUT:   class {
+// CHECK:STDOUT:     %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:     %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:     complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 2 - 0
toolchain/check/testdata/builtins/float/eq.carbon

@@ -155,6 +155,7 @@ fn WrongResult(a: f64, b: f64) -> f64 = "float.eq";
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @True {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -163,6 +164,7 @@ fn WrongResult(a: f64, b: f64) -> f64 = "float.eq";
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @False {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 2 - 0
toolchain/check/testdata/builtins/float/greater.carbon

@@ -175,6 +175,7 @@ fn RuntimeCallIsValid(a: f64, b: f64) -> bool {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @True {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -183,6 +184,7 @@ fn RuntimeCallIsValid(a: f64, b: f64) -> bool {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @False {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 2 - 0
toolchain/check/testdata/builtins/float/greater_eq.carbon

@@ -175,6 +175,7 @@ fn RuntimeCallIsValid(a: f64, b: f64) -> bool {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @True {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -183,6 +184,7 @@ fn RuntimeCallIsValid(a: f64, b: f64) -> bool {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @False {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 2 - 0
toolchain/check/testdata/builtins/float/less.carbon

@@ -175,6 +175,7 @@ fn RuntimeCallIsValid(a: f64, b: f64) -> bool {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @True {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -183,6 +184,7 @@ fn RuntimeCallIsValid(a: f64, b: f64) -> bool {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @False {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 2 - 0
toolchain/check/testdata/builtins/float/less_eq.carbon

@@ -175,6 +175,7 @@ fn RuntimeCallIsValid(a: f64, b: f64) -> bool {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @True {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -183,6 +184,7 @@ fn RuntimeCallIsValid(a: f64, b: f64) -> bool {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @False {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 2 - 0
toolchain/check/testdata/builtins/float/neq.carbon

@@ -155,6 +155,7 @@ fn WrongResult(a: f64, b: f64) -> f64 = "float.neq";
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @True {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -163,6 +164,7 @@ fn WrongResult(a: f64, b: f64) -> f64 = "float.neq";
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @False {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 4 - 4
toolchain/check/testdata/choice/basic.carbon

@@ -82,7 +82,7 @@ fn G() {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @Never {
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.discriminant [concrete = constants.%complete_type]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness constants.%struct_type.discriminant [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -121,7 +121,7 @@ fn G() {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @Always {
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.discriminant [concrete = constants.%complete_type]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness constants.%struct_type.discriminant [concrete = constants.%complete_type]
 // CHECK:STDOUT:   %.loc5_1.1: %empty_tuple.type = tuple_literal ()
 // CHECK:STDOUT:   %empty_tuple: %empty_tuple.type = tuple_value () [concrete = constants.%empty_tuple]
 // CHECK:STDOUT:   %.loc5_1.2: %empty_tuple.type = converted %.loc5_1.1, %empty_tuple [concrete = constants.%empty_tuple]
@@ -216,7 +216,7 @@ fn G() {
 // CHECK:STDOUT: class @Ordering {
 // CHECK:STDOUT:   %int_2.loc8: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc]
 // CHECK:STDOUT:   %u2: type = class_type @UInt, @UInt(constants.%int_2.ecc) [concrete = constants.%u2]
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.discriminant [concrete = constants.%complete_type.de2]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness constants.%struct_type.discriminant [concrete = constants.%complete_type.de2]
 // CHECK:STDOUT:   %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0.5c6]
 // CHECK:STDOUT:   %impl.elem0.loc4: %.a2e = impl_witness_access constants.%impl_witness.f5e, element0 [concrete = constants.%Convert.474]
 // CHECK:STDOUT:   %bound_method.loc4_7.1: <bound method> = bound_method %int_0, %impl.elem0.loc4 [concrete = constants.%Convert.bound.5bb]
@@ -360,7 +360,7 @@ fn G() {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @Always {
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.discriminant [concrete = constants.%complete_type]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness constants.%struct_type.discriminant [concrete = constants.%complete_type]
 // CHECK:STDOUT:   %.loc9_1.1: %empty_tuple.type = tuple_literal ()
 // CHECK:STDOUT:   %empty_tuple: %empty_tuple.type = tuple_value () [concrete = constants.%empty_tuple]
 // CHECK:STDOUT:   %.loc9_1.2: %empty_tuple.type = converted %.loc9_1.1, %empty_tuple [concrete = constants.%empty_tuple]

+ 1 - 1
toolchain/check/testdata/choice/fail_invalid.carbon

@@ -53,7 +53,7 @@ fn F() {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @Never {
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.discriminant [concrete = constants.%complete_type]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness constants.%struct_type.discriminant [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:

+ 3 - 3
toolchain/check/testdata/choice/fail_todo_params.carbon

@@ -108,7 +108,7 @@ choice C {
 // CHECK:STDOUT:   %b: %i64 = bind_name b, %b.param
 // CHECK:STDOUT:   %int_1.loc11: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8]
 // CHECK:STDOUT:   %u1: type = class_type @UInt, @UInt(constants.%int_1.5b8) [concrete = constants.%u1]
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.discriminant [concrete = constants.%complete_type.df6]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness constants.%struct_type.discriminant [concrete = constants.%complete_type.df6]
 // CHECK:STDOUT:   %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0.5c6]
 // CHECK:STDOUT:   %impl.elem0.loc4: %.df4 = impl_witness_access constants.%impl_witness.514, element0 [concrete = constants.%Convert.84e]
 // CHECK:STDOUT:   %bound_method.loc4_7.1: <bound method> = bound_method %int_0, %impl.elem0.loc4 [concrete = constants.%Convert.bound.db0]
@@ -196,7 +196,7 @@ choice C {
 // CHECK:STDOUT:     %a.param: @C.%T.loc3_10.2 (%T) = value_param call_param0
 // CHECK:STDOUT:     %T.ref: type = name_ref T, %T.loc3_10.1 [symbolic = %T.loc3_10.2 (constants.%T)]
 // CHECK:STDOUT:     %a: @C.%T.loc3_10.2 (%T) = bind_name a, %a.param
-// CHECK:STDOUT:     %complete_type: <witness> = complete_type_witness %struct_type.discriminant [concrete = constants.%complete_type]
+// CHECK:STDOUT:     %complete_type: <witness> = complete_type_witness constants.%struct_type.discriminant [concrete = constants.%complete_type]
 // CHECK:STDOUT:     complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT:   !members:
@@ -244,7 +244,7 @@ choice C {
 // CHECK:STDOUT:     %ptr: type = ptr_type %Self.ref [concrete = constants.%ptr]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %a: %ptr = bind_name a, %a.param
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.discriminant [concrete = constants.%complete_type]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness constants.%struct_type.discriminant [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:

+ 2 - 2
toolchain/check/testdata/choice/generic.carbon

@@ -54,11 +54,11 @@ choice Always(T:! type) {
 // CHECK:STDOUT:
 // CHECK:STDOUT: !definition:
 // CHECK:STDOUT:   %Always: type = class_type @Always, @Always(%T.loc11_15.2) [symbolic = %Always (constants.%Always)]
-// CHECK:STDOUT:   %require_complete: <witness> = require_complete_type @Always.%Always (%Always) [symbolic = %require_complete (constants.%require_complete)]
+// CHECK:STDOUT:   %require_complete: <witness> = require_complete_type %Always [symbolic = %require_complete (constants.%require_complete)]
 // CHECK:STDOUT:   %Always.val: @Always.%Always (%Always) = struct_value (constants.%empty_tuple) [symbolic = %Always.val (constants.%Always.val)]
 // CHECK:STDOUT:
 // CHECK:STDOUT:   class {
-// CHECK:STDOUT:     %complete_type: <witness> = complete_type_witness %struct_type.discriminant [concrete = constants.%complete_type]
+// CHECK:STDOUT:     %complete_type: <witness> = complete_type_witness constants.%struct_type.discriminant [concrete = constants.%complete_type]
 // CHECK:STDOUT:     %.loc13_1.1: %empty_tuple.type = tuple_literal ()
 // CHECK:STDOUT:     %empty_tuple: %empty_tuple.type = tuple_value () [concrete = constants.%empty_tuple]
 // CHECK:STDOUT:     %.loc13_1.2: %empty_tuple.type = converted %.loc13_1.1, %empty_tuple [concrete = constants.%empty_tuple]

+ 7 - 1
toolchain/check/testdata/class/access_modifers.carbon

@@ -240,7 +240,8 @@ class A {
 // CHECK:STDOUT:     %return.param: ref %Circle = out_param call_param0
 // CHECK:STDOUT:     %return: ref %Circle = return_slot %return.param
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.radius.251 [concrete = constants.%complete_type.5a5]
+// CHECK:STDOUT:   %struct_type.radius: type = struct_type {.radius: %i32} [concrete = constants.%struct_type.radius.251]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.radius [concrete = constants.%complete_type.5a5]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -352,6 +353,7 @@ class A {
 // CHECK:STDOUT:     %.loc5_13: %A.elem = var_pattern %.loc5_18
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.var: ref %A.elem = var <none>
+// CHECK:STDOUT:   %struct_type.x: type = struct_type {.x: %i32} [concrete = constants.%struct_type.x]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.x [concrete = constants.%complete_type.1ec]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -465,6 +467,7 @@ class A {
 // CHECK:STDOUT:     %return.param: ref %i32 = out_param call_param1
 // CHECK:STDOUT:     %return: ref %i32 = return_slot %return.param
 // CHECK:STDOUT:   }
+// CHECK:STDOUT:   %struct_type.radius: type = struct_type {.radius: %i32} [concrete = constants.%struct_type.radius]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.radius [concrete = constants.%complete_type.5a5]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -574,6 +577,7 @@ class A {
 // CHECK:STDOUT:   %.loc5_16.1: %i32 = value_of_initializer %int.convert_checked [concrete = constants.%int_5.0f6]
 // CHECK:STDOUT:   %.loc5_16.2: %i32 = converted %int_5, %.loc5_16.1 [concrete = constants.%int_5.0f6]
 // CHECK:STDOUT:   %x: %i32 = bind_name x, %.loc5_16.2
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type.357]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -680,6 +684,7 @@ class A {
 // CHECK:STDOUT:   %.loc6_24.1: %i32 = value_of_initializer %int.convert_checked.loc6 [concrete = constants.%int_5.0f6]
 // CHECK:STDOUT:   %.loc6_24.2: %i32 = converted %int_5.loc6, %.loc6_24.1 [concrete = constants.%int_5.0f6]
 // CHECK:STDOUT:   %y: %i32 = bind_name y, %.loc6_24.2
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type.357]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -730,6 +735,7 @@ class A {
 // CHECK:STDOUT: class @A {
 // CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {}
 // CHECK:STDOUT:   %G.decl: %G.type = fn_decl @G [concrete = constants.%G] {} {}
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 5 - 3
toolchain/check/testdata/class/adapter/adapt.carbon

@@ -109,6 +109,7 @@ interface I {
 // CHECK:STDOUT:     %.loc6_3: %SomeClass.elem = var_pattern %.loc6_8
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.var.loc6: ref %SomeClass.elem = var <none>
+// CHECK:STDOUT:   %struct_type.a.b: type = struct_type {.a: %i32, .b: %i32} [concrete = constants.%struct_type.a.b]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.a.b [concrete = constants.%complete_type.705]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -121,7 +122,7 @@ interface I {
 // CHECK:STDOUT: class @SomeClassAdapter {
 // CHECK:STDOUT:   %SomeClass.ref: type = name_ref SomeClass, file.%SomeClass.decl [concrete = constants.%SomeClass]
 // CHECK:STDOUT:   adapt_decl %SomeClass.ref [concrete]
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.a.b [concrete = constants.%complete_type.705]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness constants.%struct_type.a.b [concrete = constants.%complete_type.705]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -136,7 +137,7 @@ interface I {
 // CHECK:STDOUT:   %i32.loc14_23: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
 // CHECK:STDOUT:   %struct_type.a.b: type = struct_type {.a: %i32, .b: %i32} [concrete = constants.%struct_type.a.b]
 // CHECK:STDOUT:   adapt_decl %struct_type.a.b [concrete]
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.a.b [concrete = constants.%complete_type.705]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness constants.%struct_type.a.b [concrete = constants.%complete_type.705]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -185,6 +186,7 @@ interface I {
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @Adapted {
 // CHECK:STDOUT:   %F.decl: %F.type.967 = fn_decl @F.1 [concrete = constants.%F.9eb] {} {}
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -196,7 +198,7 @@ interface I {
 // CHECK:STDOUT: class @AdaptNotExtend {
 // CHECK:STDOUT:   %Adapted.ref: type = name_ref Adapted, file.%Adapted.decl [concrete = constants.%Adapted]
 // CHECK:STDOUT:   adapt_decl %Adapted.ref [concrete]
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:

+ 7 - 5
toolchain/check/testdata/class/adapter/adapt_copy.carbon

@@ -197,7 +197,7 @@ fn InTuple(c: (AdaptStruct, u32)) -> (AdaptStruct, u32) {
 // CHECK:STDOUT:   %int_32: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
 // CHECK:STDOUT:   %i32: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
 // CHECK:STDOUT:   adapt_decl %i32 [concrete]
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %i32.builtin [concrete = constants.%complete_type.f8a]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness constants.%i32.builtin [concrete = constants.%complete_type.f8a]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -340,7 +340,7 @@ fn InTuple(c: (AdaptStruct, u32)) -> (AdaptStruct, u32) {
 // CHECK:STDOUT:   %.loc5_18: %tuple.type.24b = tuple_literal (%i32.loc5_10, %i32.loc5_15)
 // CHECK:STDOUT:   %.loc5_19: type = converted %.loc5_18, constants.%tuple.type.d07 [concrete = constants.%tuple.type.d07]
 // CHECK:STDOUT:   adapt_decl %.loc5_19 [concrete]
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %tuple.type.d07 [concrete = constants.%complete_type.65d]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness constants.%tuple.type.d07 [concrete = constants.%complete_type.65d]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -490,6 +490,7 @@ fn InTuple(c: (AdaptStruct, u32)) -> (AdaptStruct, u32) {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @Noncopyable {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -500,7 +501,7 @@ fn InTuple(c: (AdaptStruct, u32)) -> (AdaptStruct, u32) {
 // CHECK:STDOUT: class @AdaptNoncopyable {
 // CHECK:STDOUT:   %Noncopyable.ref: type = name_ref Noncopyable, file.%Noncopyable.decl [concrete = constants.%Noncopyable]
 // CHECK:STDOUT:   adapt_decl %Noncopyable.ref [concrete]
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -574,6 +575,7 @@ fn InTuple(c: (AdaptStruct, u32)) -> (AdaptStruct, u32) {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @Noncopyable {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type.357]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -590,7 +592,7 @@ fn InTuple(c: (AdaptStruct, u32)) -> (AdaptStruct, u32) {
 // CHECK:STDOUT:   %.loc9_31: %tuple.type.ff9 = tuple_literal (%i32.loc9_10, %Noncopyable.ref, %i32.loc9_28)
 // CHECK:STDOUT:   %.loc9_32: type = converted %.loc9_31, constants.%tuple.type.c9a [concrete = constants.%tuple.type.c9a]
 // CHECK:STDOUT:   adapt_decl %.loc9_32 [concrete]
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %tuple.type.c9a [concrete = constants.%complete_type.201]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness constants.%tuple.type.c9a [concrete = constants.%complete_type.201]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -707,7 +709,7 @@ fn InTuple(c: (AdaptStruct, u32)) -> (AdaptStruct, u32) {
 // CHECK:STDOUT:   %i32.loc5_23: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
 // CHECK:STDOUT:   %struct_type.e.f: type = struct_type {.e: %i32, .f: %i32} [concrete = constants.%struct_type.e.f]
 // CHECK:STDOUT:   adapt_decl %struct_type.e.f [concrete]
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.e.f [concrete = constants.%complete_type.511]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness constants.%struct_type.e.f [concrete = constants.%complete_type.511]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:

+ 9 - 6
toolchain/check/testdata/class/adapter/extend_adapt.carbon

@@ -200,7 +200,7 @@ fn F(a: IntAdapter) -> i32 {
 // CHECK:STDOUT: class @SomeClassAdapter {
 // CHECK:STDOUT:   %SomeClass.ref: type = name_ref SomeClass, file.%SomeClass.decl [concrete = constants.%SomeClass]
 // CHECK:STDOUT:   adapt_decl %SomeClass.ref [concrete]
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.a.b [concrete = constants.%complete_type.705]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness constants.%struct_type.a.b [concrete = constants.%complete_type.705]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -231,6 +231,7 @@ fn F(a: IntAdapter) -> i32 {
 // CHECK:STDOUT:     %SomeClassAdapter.ref: type = name_ref SomeClassAdapter, file.%SomeClassAdapter.decl.loc4 [concrete = constants.%SomeClassAdapter]
 // CHECK:STDOUT:     %self: %SomeClassAdapter = bind_name self, %self.param
 // CHECK:STDOUT:   }
+// CHECK:STDOUT:   %struct_type.a.b: type = struct_type {.a: %i32, .b: %i32} [concrete = constants.%struct_type.a.b]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.a.b [concrete = constants.%complete_type.705]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -315,6 +316,7 @@ fn F(a: IntAdapter) -> i32 {
 // CHECK:STDOUT:     %Self.ref: type = name_ref Self, constants.%SomeClass [concrete = constants.%SomeClass]
 // CHECK:STDOUT:     %self: %SomeClass = bind_name self, %self.param
 // CHECK:STDOUT:   }
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -326,7 +328,7 @@ fn F(a: IntAdapter) -> i32 {
 // CHECK:STDOUT: class @SomeClassAdapter {
 // CHECK:STDOUT:   %SomeClass.ref: type = name_ref SomeClass, file.%SomeClass.decl [concrete = constants.%SomeClass]
 // CHECK:STDOUT:   adapt_decl %SomeClass.ref [concrete]
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -408,6 +410,7 @@ fn F(a: IntAdapter) -> i32 {
 // CHECK:STDOUT:     %.loc6_3: %SomeClass.elem = var_pattern %.loc6_8
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.var.loc6: ref %SomeClass.elem = var <none>
+// CHECK:STDOUT:   %struct_type.a.b: type = struct_type {.a: %i32, .b: %i32} [concrete = constants.%struct_type.a.b]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.a.b [concrete = constants.%complete_type.705]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -420,7 +423,7 @@ fn F(a: IntAdapter) -> i32 {
 // CHECK:STDOUT: class @SomeClassAdapter {
 // CHECK:STDOUT:   %SomeClass.ref: type = name_ref SomeClass, file.%SomeClass.decl [concrete = constants.%SomeClass]
 // CHECK:STDOUT:   adapt_decl %SomeClass.ref [concrete]
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.a.b [concrete = constants.%complete_type.705]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness constants.%struct_type.a.b [concrete = constants.%complete_type.705]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -490,7 +493,7 @@ fn F(a: IntAdapter) -> i32 {
 // CHECK:STDOUT:   %i32.loc5_30: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
 // CHECK:STDOUT:   %struct_type.a.b: type = struct_type {.a: %i32, .b: %i32} [concrete = constants.%struct_type.a.b]
 // CHECK:STDOUT:   adapt_decl %struct_type.a.b [concrete]
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.a.b [concrete = constants.%complete_type.705]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness constants.%struct_type.a.b [concrete = constants.%complete_type.705]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -560,7 +563,7 @@ fn F(a: IntAdapter) -> i32 {
 // CHECK:STDOUT:   %.loc5_25: %tuple.type.24b = tuple_literal (%i32.loc5_17, %i32.loc5_22)
 // CHECK:STDOUT:   %.loc5_26: type = converted %.loc5_25, constants.%tuple.type.d07 [concrete = constants.%tuple.type.d07]
 // CHECK:STDOUT:   adapt_decl %.loc5_26 [concrete]
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %tuple.type.d07 [concrete = constants.%complete_type.65d]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness constants.%tuple.type.d07 [concrete = constants.%complete_type.65d]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -651,7 +654,7 @@ fn F(a: IntAdapter) -> i32 {
 // CHECK:STDOUT:   %.loc7_27.1: type = value_of_initializer %int.make_type_signed [concrete = constants.%i32.builtin]
 // CHECK:STDOUT:   %.loc7_27.2: type = converted %int.make_type_signed, %.loc7_27.1 [concrete = constants.%i32.builtin]
 // CHECK:STDOUT:   adapt_decl %.loc7_27.2 [concrete]
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %i32.builtin [concrete = constants.%complete_type.f8a]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness constants.%i32.builtin [concrete = constants.%complete_type.f8a]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:

+ 3 - 2
toolchain/check/testdata/class/adapter/fail_adapt_bad_decl.carbon

@@ -245,7 +245,7 @@ class C {
 // CHECK:STDOUT:   %.loc5_11: type = converted %.loc5_10, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type]
 // CHECK:STDOUT:   adapt_decl %.loc5_11 [concrete]
 // CHECK:STDOUT:   %.loc13: %empty_struct_type = struct_literal ()
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_tuple.type [concrete = constants.%complete_type]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness constants.%empty_tuple.type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -257,7 +257,7 @@ class C {
 // CHECK:STDOUT:   %.loc17_11: type = converted %.loc17_10, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type]
 // CHECK:STDOUT:   adapt_decl %.loc17_11 [concrete]
 // CHECK:STDOUT:   %.loc25: %empty_tuple.type = tuple_literal ()
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_tuple.type [concrete = constants.%complete_type]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness constants.%empty_tuple.type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -315,6 +315,7 @@ class C {
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @C {
 // CHECK:STDOUT:   %I.decl: type = interface_decl @I.2 [concrete = constants.%I.type.e30] {} {}
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 6 - 5
toolchain/check/testdata/class/adapter/fail_adapt_modifiers.carbon

@@ -96,6 +96,7 @@ class C5 {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @B {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -106,7 +107,7 @@ class C5 {
 // CHECK:STDOUT: class @C1 {
 // CHECK:STDOUT:   %B.ref: type = name_ref B, file.%B.decl [concrete = constants.%B]
 // CHECK:STDOUT:   adapt_decl %B.ref [concrete]
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -117,7 +118,7 @@ class C5 {
 // CHECK:STDOUT: class @C2 {
 // CHECK:STDOUT:   %B.ref: type = name_ref B, file.%B.decl [concrete = constants.%B]
 // CHECK:STDOUT:   adapt_decl %B.ref [concrete]
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -128,7 +129,7 @@ class C5 {
 // CHECK:STDOUT: class @C3 {
 // CHECK:STDOUT:   %B.ref: type = name_ref B, file.%B.decl [concrete = constants.%B]
 // CHECK:STDOUT:   adapt_decl %B.ref [concrete]
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -139,7 +140,7 @@ class C5 {
 // CHECK:STDOUT: class @C4 {
 // CHECK:STDOUT:   %B.ref: type = name_ref B, file.%B.decl [concrete = constants.%B]
 // CHECK:STDOUT:   adapt_decl %B.ref [concrete]
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -151,7 +152,7 @@ class C5 {
 // CHECK:STDOUT: class @C5 {
 // CHECK:STDOUT:   %B.ref: type = name_ref B, file.%B.decl [concrete = constants.%B]
 // CHECK:STDOUT:   adapt_decl %B.ref [concrete]
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:

+ 1 - 0
toolchain/check/testdata/class/adapter/fail_adapt_with_base.carbon

@@ -50,6 +50,7 @@ base class AdaptWithVirtual {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @Simple {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 2 - 0
toolchain/check/testdata/class/adapter/fail_adapt_with_subobjects.carbon

@@ -107,6 +107,7 @@ class AdaptWithBaseAndFields {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @Base {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type.357]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -236,6 +237,7 @@ class AdaptWithBaseAndFields {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @Base {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type.357]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 6 - 4
toolchain/check/testdata/class/adapter/init_adapt.carbon

@@ -229,7 +229,8 @@ var e: C = MakeAdaptC();
 // CHECK:STDOUT:     %.loc6_3: %C.elem = var_pattern %.loc6_8
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.var.loc6: ref %C.elem = var <none>
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.a.b.501 [concrete = constants.%complete_type.705]
+// CHECK:STDOUT:   %struct_type.a.b: type = struct_type {.a: %i32, .b: %i32} [concrete = constants.%struct_type.a.b.501]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.a.b [concrete = constants.%complete_type.705]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -241,7 +242,7 @@ var e: C = MakeAdaptC();
 // CHECK:STDOUT: class @AdaptC {
 // CHECK:STDOUT:   %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C]
 // CHECK:STDOUT:   adapt_decl %C.ref [concrete]
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.a.b.501 [concrete = constants.%complete_type.705]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness constants.%struct_type.a.b.501 [concrete = constants.%complete_type.705]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -422,7 +423,8 @@ var e: C = MakeAdaptC();
 // CHECK:STDOUT:     %.loc6_3: %C.elem = var_pattern %.loc6_8
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.var.loc6: ref %C.elem = var <none>
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.a.b.501 [concrete = constants.%complete_type.705]
+// CHECK:STDOUT:   %struct_type.a.b: type = struct_type {.a: %i32, .b: %i32} [concrete = constants.%struct_type.a.b.501]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.a.b [concrete = constants.%complete_type.705]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -434,7 +436,7 @@ var e: C = MakeAdaptC();
 // CHECK:STDOUT: class @AdaptC {
 // CHECK:STDOUT:   %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C]
 // CHECK:STDOUT:   adapt_decl %C.ref [concrete]
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.a.b.501 [concrete = constants.%complete_type.705]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness constants.%struct_type.a.b.501 [concrete = constants.%complete_type.705]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:

+ 6 - 2
toolchain/check/testdata/class/base.carbon

@@ -141,7 +141,8 @@ class Derived {
 // CHECK:STDOUT:     %.loc4_3: %Base.elem = var_pattern %.loc4_8
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.var: ref %Base.elem = var <none>
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.b.0a3 [concrete = constants.%complete_type.ba8]
+// CHECK:STDOUT:   %struct_type.b: type = struct_type {.b: %i32} [concrete = constants.%struct_type.b.0a3]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.b [concrete = constants.%complete_type.ba8]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -157,7 +158,8 @@ class Derived {
 // CHECK:STDOUT:     %.loc10_3: %Derived.elem.344 = var_pattern %.loc10_8
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.var: ref %Derived.elem.344 = var <none>
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.base.d.f8f [concrete = constants.%complete_type.da6]
+// CHECK:STDOUT:   %struct_type.base.d: type = struct_type {.base: %Base, .d: %i32} [concrete = constants.%struct_type.base.d.f8f]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.base.d [concrete = constants.%complete_type.da6]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -255,6 +257,7 @@ class Derived {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @Base {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type.357]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -269,6 +272,7 @@ class Derived {
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.var: ref %Derived.elem = var <none>
 // CHECK:STDOUT:   %Base.ref: type = name_ref Base, file.%Base.decl [concrete = constants.%Base]
+// CHECK:STDOUT:   %struct_type.d: type = struct_type {.d: %i32} [concrete = constants.%struct_type.d]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.d [concrete = constants.%complete_type.860]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 3 - 1
toolchain/check/testdata/class/base_field.carbon

@@ -99,6 +99,7 @@ fn Access(p: Derived*) -> i32* {
 // CHECK:STDOUT:     %.loc14_3: %Base.elem = var_pattern %.loc14_8
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.var.loc14: ref %Base.elem = var <none>
+// CHECK:STDOUT:   %struct_type.a.b.c: type = struct_type {.a: %i32, .b: %i32, .c: %i32} [concrete = constants.%struct_type.a.b.c]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.a.b.c [concrete = constants.%complete_type.ebc]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -122,7 +123,8 @@ fn Access(p: Derived*) -> i32* {
 // CHECK:STDOUT:     %.loc21_3: %Derived.elem.344 = var_pattern %.loc21_8
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.var.loc21: ref %Derived.elem.344 = var <none>
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.base.d.e.6a7 [concrete = constants.%complete_type.401]
+// CHECK:STDOUT:   %struct_type.base.d.e: type = struct_type {.base: %Base, .d: %i32, .e: %i32} [concrete = constants.%struct_type.base.d.e.6a7]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.base.d.e [concrete = constants.%complete_type.401]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:

+ 2 - 0
toolchain/check/testdata/class/base_function_unqualified.carbon

@@ -63,6 +63,7 @@ fn Derived.H() {
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @Base {
 // CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {}
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type.357]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -76,6 +77,7 @@ fn Derived.H() {
 // CHECK:STDOUT:   %.loc16: %Derived.elem = base_decl %Base.ref, element0 [concrete]
 // CHECK:STDOUT:   %G.decl: %G.type = fn_decl @G [concrete = constants.%G] {} {}
 // CHECK:STDOUT:   %H.decl: %H.type = fn_decl @H [concrete = constants.%H] {} {}
+// CHECK:STDOUT:   %struct_type.base: type = struct_type {.base: %Base} [concrete = constants.%struct_type.base]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.base [concrete = constants.%complete_type.15c]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 3 - 1
toolchain/check/testdata/class/base_method.carbon

@@ -122,6 +122,7 @@ fn Call(p: Derived*) {
 // CHECK:STDOUT:     }
 // CHECK:STDOUT:     %self.loc14: %ptr.11f = bind_name self, %self.param.loc14
 // CHECK:STDOUT:   }
+// CHECK:STDOUT:   %struct_type.a: type = struct_type {.a: %i32} [concrete = constants.%struct_type.a]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.a [concrete = constants.%complete_type.fd7]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -134,7 +135,8 @@ fn Call(p: Derived*) {
 // CHECK:STDOUT: class @Derived {
 // CHECK:STDOUT:   %Base.ref: type = name_ref Base, file.%Base.decl [concrete = constants.%Base]
 // CHECK:STDOUT:   %.loc22: %Derived.elem = base_decl %Base.ref, element0 [concrete]
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.base.b1e [concrete = constants.%complete_type.15c]
+// CHECK:STDOUT:   %struct_type.base: type = struct_type {.base: %Base} [concrete = constants.%struct_type.base.b1e]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.base [concrete = constants.%complete_type.15c]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:

+ 3 - 1
toolchain/check/testdata/class/base_method_qualified.carbon

@@ -174,7 +174,8 @@ fn PassDerivedToBaseIndirect(p: Derived*) -> i32 {
 // CHECK:STDOUT:     %Self.ref: type = name_ref Self, constants.%Derived [concrete = constants.%Derived]
 // CHECK:STDOUT:     %self: %Derived = bind_name self, %self.param
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.base.b1e [concrete = constants.%complete_type.15c]
+// CHECK:STDOUT:   %struct_type.base: type = struct_type {.base: %Base} [concrete = constants.%struct_type.base.b1e]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.base [concrete = constants.%complete_type.15c]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -215,6 +216,7 @@ fn PassDerivedToBaseIndirect(p: Derived*) -> i32 {
 // CHECK:STDOUT:     %return.param: ref %i32 = out_param call_param1
 // CHECK:STDOUT:     %return: ref %i32 = return_slot %return.param
 // CHECK:STDOUT:   }
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type.357]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 7 - 3
toolchain/check/testdata/class/base_method_shadow.carbon

@@ -135,6 +135,7 @@ fn Call(a: A*, b: B*, c: C*, d: D*) {
 // CHECK:STDOUT:     }
 // CHECK:STDOUT:     %self: %ptr.6db = bind_name self, %self.param
 // CHECK:STDOUT:   }
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type.357]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -158,7 +159,8 @@ fn Call(a: A*, b: B*, c: C*, d: D*) {
 // CHECK:STDOUT:     }
 // CHECK:STDOUT:     %self: %ptr.e79 = bind_name self, %self.param
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.base.953 [concrete = constants.%complete_type.020]
+// CHECK:STDOUT:   %struct_type.base: type = struct_type {.base: %A} [concrete = constants.%struct_type.base.953]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.base [concrete = constants.%complete_type.020]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -184,7 +186,8 @@ fn Call(a: A*, b: B*, c: C*, d: D*) {
 // CHECK:STDOUT:     }
 // CHECK:STDOUT:     %self: %ptr.019 = bind_name self, %self.param
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.base.0ff [concrete = constants.%complete_type.98e]
+// CHECK:STDOUT:   %struct_type.base: type = struct_type {.base: %B} [concrete = constants.%struct_type.base.0ff]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.base [concrete = constants.%complete_type.98e]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -198,7 +201,8 @@ fn Call(a: A*, b: B*, c: C*, d: D*) {
 // CHECK:STDOUT: class @D {
 // CHECK:STDOUT:   %B.ref: type = name_ref B, file.%B.decl [concrete = constants.%B]
 // CHECK:STDOUT:   %.loc26: %D.elem = base_decl %B.ref, element0 [concrete]
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.base.0ff [concrete = constants.%complete_type.98e]
+// CHECK:STDOUT:   %struct_type.base: type = struct_type {.base: %B} [concrete = constants.%struct_type.base.0ff]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.base [concrete = constants.%complete_type.98e]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:

+ 1 - 0
toolchain/check/testdata/class/basic.carbon

@@ -142,6 +142,7 @@ fn Run() -> i32 {
 // CHECK:STDOUT:     %.loc18_3: %Class.elem = var_pattern %.loc18_8
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.var: ref %Class.elem = var <none>
+// CHECK:STDOUT:   %struct_type.k: type = struct_type {.k: %i32} [concrete = constants.%struct_type.k]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.k [concrete = constants.%complete_type.954]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 1 - 0
toolchain/check/testdata/class/complete_in_member_fn.carbon

@@ -64,6 +64,7 @@ class C {
 // CHECK:STDOUT:     %.loc14_3: %C.elem = var_pattern %.loc14_8
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.var: ref %C.elem = var <none>
+// CHECK:STDOUT:   %struct_type.a: type = struct_type {.a: %i32} [concrete = constants.%struct_type.a]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.a [concrete = constants.%complete_type.fd7]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 3 - 1
toolchain/check/testdata/class/compound_field.carbon

@@ -166,6 +166,7 @@ fn AccessBaseIndirect(p: Derived*) -> i32* {
 // CHECK:STDOUT:     %.loc14_3: %Base.elem = var_pattern %.loc14_8
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.var.loc14: ref %Base.elem = var <none>
+// CHECK:STDOUT:   %struct_type.a.b.c: type = struct_type {.a: %i32, .b: %i32, .c: %i32} [concrete = constants.%struct_type.a.b.c]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.a.b.c [concrete = constants.%complete_type.ebc]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -189,7 +190,8 @@ fn AccessBaseIndirect(p: Derived*) -> i32* {
 // CHECK:STDOUT:     %.loc21_3: %Derived.elem.344 = var_pattern %.loc21_8
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.var.loc21: ref %Derived.elem.344 = var <none>
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.base.d.e.6a7 [concrete = constants.%complete_type.401]
+// CHECK:STDOUT:   %struct_type.base.d.e: type = struct_type {.base: %Base, .d: %i32, .e: %i32} [concrete = constants.%struct_type.base.d.e.6a7]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.base.d.e [concrete = constants.%complete_type.401]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:

+ 1 - 0
toolchain/check/testdata/class/cross_package_import.carbon

@@ -125,6 +125,7 @@ var c: Other.C = {};
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @C {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 6 - 3
toolchain/check/testdata/class/derived_to_base.carbon

@@ -211,7 +211,8 @@ fn ConvertInit() {
 // CHECK:STDOUT:     %.loc12_3: %A.elem = var_pattern %.loc12_8
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.var: ref %A.elem = var <none>
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.a.ba9 [concrete = constants.%complete_type.fd7]
+// CHECK:STDOUT:   %struct_type.a: type = struct_type {.a: %i32} [concrete = constants.%struct_type.a.ba9]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.a [concrete = constants.%complete_type.fd7]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -227,7 +228,8 @@ fn ConvertInit() {
 // CHECK:STDOUT:     %.loc17_3: %B.elem.5c3 = var_pattern %.loc17_8
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.var: ref %B.elem.5c3 = var <none>
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.base.b.b44 [concrete = constants.%complete_type.725]
+// CHECK:STDOUT:   %struct_type.base.b: type = struct_type {.base: %A, .b: %i32} [concrete = constants.%struct_type.base.b.b44]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.base.b [concrete = constants.%complete_type.725]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -246,7 +248,8 @@ fn ConvertInit() {
 // CHECK:STDOUT:     %.loc22_3: %C.elem.646 = var_pattern %.loc22_8
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.var: ref %C.elem.646 = var <none>
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.base.c.8e2 [concrete = constants.%complete_type.58a]
+// CHECK:STDOUT:   %struct_type.base.c: type = struct_type {.base: %B, .c: %i32} [concrete = constants.%struct_type.base.c.8e2]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.base.c [concrete = constants.%complete_type.58a]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:

+ 1 - 0
toolchain/check/testdata/class/fail_addr_not_self.carbon

@@ -74,6 +74,7 @@ class Class {
 // CHECK:STDOUT:     }
 // CHECK:STDOUT:     %b: %ptr = bind_name b, %b.param
 // CHECK:STDOUT:   }
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 1 - 0
toolchain/check/testdata/class/fail_addr_self.carbon

@@ -105,6 +105,7 @@ fn F(c: Class, p: Class*) {
 // CHECK:STDOUT:     %Class.ref: type = name_ref Class, file.%Class.decl [concrete = constants.%Class]
 // CHECK:STDOUT:     %self: %Class = bind_name self, %self.param
 // CHECK:STDOUT:   }
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 15 - 6
toolchain/check/testdata/class/fail_base_bad_type.carbon

@@ -228,7 +228,8 @@ fn AccessMemberWithInvalidBaseFinal_NoMember(p: DeriveFromFinal*) -> i32 {
 // CHECK:STDOUT: class @DeriveFromError {
 // CHECK:STDOUT:   %error.ref: <error> = name_ref error, <error> [concrete = <error>]
 // CHECK:STDOUT:   %.loc9: <error> = base_decl <error>, element0 [concrete]
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness <error> [concrete = <error>]
+// CHECK:STDOUT:   %struct_type.base: type = struct_type {.base: <error>} [concrete = <error>]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.base [concrete = <error>]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -298,7 +299,8 @@ fn AccessMemberWithInvalidBaseFinal_NoMember(p: DeriveFromFinal*) -> i32 {
 // CHECK:STDOUT:   %int_32: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
 // CHECK:STDOUT:   %.loc12_16: type = converted %int_32, <error> [concrete = <error>]
 // CHECK:STDOUT:   %.loc12_18: <error> = base_decl <error>, element0 [concrete]
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness <error> [concrete = <error>]
+// CHECK:STDOUT:   %struct_type.base: type = struct_type {.base: <error>} [concrete = <error>]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.base [concrete = <error>]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -391,6 +393,7 @@ fn AccessMemberWithInvalidBaseFinal_NoMember(p: DeriveFromFinal*) -> i32 {
 // CHECK:STDOUT:   %int_32: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
 // CHECK:STDOUT:   %i32: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
 // CHECK:STDOUT:   %.loc9: %DeriveFromi32.elem = base_decl %i32, element0 [concrete]
+// CHECK:STDOUT:   %struct_type.base: type = struct_type {.base: %i32} [concrete = constants.%struct_type.base]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.base [concrete = constants.%complete_type.386]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -497,6 +500,7 @@ fn AccessMemberWithInvalidBaseFinal_NoMember(p: DeriveFromFinal*) -> i32 {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @Base {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type.357]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -509,7 +513,8 @@ fn AccessMemberWithInvalidBaseFinal_NoMember(p: DeriveFromFinal*) -> i32 {
 // CHECK:STDOUT:   %.loc11_22.1: %tuple.type.85c = tuple_literal (%Base.ref)
 // CHECK:STDOUT:   %.loc11_22.2: type = converted %.loc11_22.1, constants.%tuple.type.469 [concrete = constants.%tuple.type.469]
 // CHECK:STDOUT:   %.loc11_23: <error> = base_decl <error>, element0 [concrete]
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness <error> [concrete = <error>]
+// CHECK:STDOUT:   %struct_type.base: type = struct_type {.base: <error>} [concrete = <error>]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.base [concrete = <error>]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -615,7 +620,8 @@ fn AccessMemberWithInvalidBaseFinal_NoMember(p: DeriveFromFinal*) -> i32 {
 // CHECK:STDOUT:   %i32.loc11_30: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
 // CHECK:STDOUT:   %struct_type.a.b: type = struct_type {.a: %i32, .b: %i32} [concrete = constants.%struct_type.a.b]
 // CHECK:STDOUT:   %.loc11: <error> = base_decl <error>, element0 [concrete]
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness <error> [concrete = <error>]
+// CHECK:STDOUT:   %struct_type.base: type = struct_type {.base: <error>} [concrete = <error>]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.base [concrete = <error>]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -716,7 +722,8 @@ fn AccessMemberWithInvalidBaseFinal_NoMember(p: DeriveFromFinal*) -> i32 {
 // CHECK:STDOUT: class @DeriveFromIncomplete {
 // CHECK:STDOUT:   %Incomplete.ref: type = name_ref Incomplete, file.%Incomplete.decl [concrete = constants.%Incomplete]
 // CHECK:STDOUT:   %.loc18: <error> = base_decl <error>, element0 [concrete]
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness <error> [concrete = <error>]
+// CHECK:STDOUT:   %struct_type.base: type = struct_type {.base: <error>} [concrete = <error>]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.base [concrete = <error>]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
@@ -844,6 +851,7 @@ fn AccessMemberWithInvalidBaseFinal_NoMember(p: DeriveFromFinal*) -> i32 {
 // CHECK:STDOUT:     %.loc5_3: %Final.elem = var_pattern %.loc5_8
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.var: ref %Final.elem = var <none>
+// CHECK:STDOUT:   %struct_type.a: type = struct_type {.a: %i32} [concrete = constants.%struct_type.a]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.a [concrete = constants.%complete_type.fd7]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -856,7 +864,8 @@ fn AccessMemberWithInvalidBaseFinal_NoMember(p: DeriveFromFinal*) -> i32 {
 // CHECK:STDOUT: class @DeriveFromFinal {
 // CHECK:STDOUT:   %Final.ref: type = name_ref Final, file.%Final.decl [concrete = constants.%Final]
 // CHECK:STDOUT:   %.loc13: %DeriveFromFinal.elem = base_decl %Final.ref, element0 [concrete]
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.base.dae [concrete = constants.%complete_type.970]
+// CHECK:STDOUT:   %struct_type.base: type = struct_type {.base: %Final} [concrete = constants.%struct_type.base.dae]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.base [concrete = constants.%complete_type.970]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:

+ 3 - 0
toolchain/check/testdata/class/fail_base_method_define.carbon

@@ -76,6 +76,7 @@ fn D.C.F() {}
 // CHECK:STDOUT: class @B {
 // CHECK:STDOUT:   %F.decl: %F.type.8c6 = fn_decl @F.1 [concrete = constants.%F.92a] {} {}
 // CHECK:STDOUT:   %C.decl: type = class_decl @C [concrete = constants.%C] {} {}
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type.357]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -87,6 +88,7 @@ fn D.C.F() {}
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @C {
 // CHECK:STDOUT:   %F.decl: %F.type.b77 = fn_decl @F.2 [concrete = constants.%F.a5f] {} {}
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type.357]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -98,6 +100,7 @@ fn D.C.F() {}
 // CHECK:STDOUT: class @D {
 // CHECK:STDOUT:   %B.ref: type = name_ref B, file.%B.decl [concrete = constants.%B]
 // CHECK:STDOUT:   %.loc20: %D.elem = base_decl %B.ref, element0 [concrete]
+// CHECK:STDOUT:   %struct_type.base: type = struct_type {.base: %B} [concrete = constants.%struct_type.base]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.base [concrete = constants.%complete_type.98e]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 2 - 0
toolchain/check/testdata/class/fail_base_misplaced.carbon

@@ -69,6 +69,7 @@ class C {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @B {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -78,6 +79,7 @@ class C {
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @C {
 // CHECK:STDOUT:   %F.decl: %F.type.c29 = fn_decl @F.2 [concrete = constants.%F.437] {} {}
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 5 - 0
toolchain/check/testdata/class/fail_base_modifiers.carbon

@@ -95,6 +95,7 @@ class C4 {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @B {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type.357]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -105,6 +106,7 @@ class C4 {
 // CHECK:STDOUT: class @C1 {
 // CHECK:STDOUT:   %B.ref: type = name_ref B, file.%B.decl [concrete = constants.%B]
 // CHECK:STDOUT:   %.loc18: %C1.elem = base_decl %B.ref, element0 [concrete]
+// CHECK:STDOUT:   %struct_type.base: type = struct_type {.base: %B} [concrete = constants.%struct_type.base]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.base [concrete = constants.%complete_type.98e]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -118,6 +120,7 @@ class C4 {
 // CHECK:STDOUT: class @C2 {
 // CHECK:STDOUT:   %B.ref: type = name_ref B, file.%B.decl [concrete = constants.%B]
 // CHECK:STDOUT:   %.loc30: %C2.elem = base_decl %B.ref, element0 [concrete]
+// CHECK:STDOUT:   %struct_type.base: type = struct_type {.base: %B} [concrete = constants.%struct_type.base]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.base [concrete = constants.%complete_type.98e]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -130,6 +133,7 @@ class C4 {
 // CHECK:STDOUT: class @C3 {
 // CHECK:STDOUT:   %B.ref: type = name_ref B, file.%B.decl [concrete = constants.%B]
 // CHECK:STDOUT:   %.loc41: %C3.elem = base_decl %B.ref, element0 [concrete]
+// CHECK:STDOUT:   %struct_type.base: type = struct_type {.base: %B} [concrete = constants.%struct_type.base]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.base [concrete = constants.%complete_type.98e]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -143,6 +147,7 @@ class C4 {
 // CHECK:STDOUT: class @C4 {
 // CHECK:STDOUT:   %B.ref: type = name_ref B, file.%B.decl [concrete = constants.%B]
 // CHECK:STDOUT:   %.loc52: %C4.elem = base_decl %B.ref, element0 [concrete]
+// CHECK:STDOUT:   %struct_type.base: type = struct_type {.base: %B} [concrete = constants.%struct_type.base]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.base [concrete = constants.%complete_type.98e]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 2 - 0
toolchain/check/testdata/class/fail_base_no_extend.carbon

@@ -49,6 +49,7 @@ class C {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @B {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type.357]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -59,6 +60,7 @@ class C {
 // CHECK:STDOUT: class @C {
 // CHECK:STDOUT:   %B.ref: type = name_ref B, file.%B.decl [concrete = constants.%B]
 // CHECK:STDOUT:   %.loc18: %C.elem = base_decl %B.ref, element0 [concrete]
+// CHECK:STDOUT:   %struct_type.base: type = struct_type {.base: %B} [concrete = constants.%struct_type.base]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.base [concrete = constants.%complete_type.98e]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 4 - 0
toolchain/check/testdata/class/fail_base_repeated.carbon

@@ -74,6 +74,7 @@ class D {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @B1 {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type.357]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -84,6 +85,7 @@ class D {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @B2 {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type.357]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -95,6 +97,7 @@ class D {
 // CHECK:STDOUT:   %B1.ref: type = name_ref B1, file.%B1.decl [concrete = constants.%B1]
 // CHECK:STDOUT:   %.loc15: %C.elem = base_decl %B1.ref, element0 [concrete]
 // CHECK:STDOUT:   %B2.ref: type = name_ref B2, file.%B2.decl [concrete = constants.%B2]
+// CHECK:STDOUT:   %struct_type.base: type = struct_type {.base: %B1} [concrete = constants.%struct_type.base]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.base [concrete = constants.%complete_type.5ac]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -110,6 +113,7 @@ class D {
 // CHECK:STDOUT:   %B1.ref.loc28: type = name_ref B1, file.%B1.decl [concrete = constants.%B1]
 // CHECK:STDOUT:   %.loc28: %D.elem = base_decl %B1.ref.loc28, element0 [concrete]
 // CHECK:STDOUT:   %B1.ref.loc36: type = name_ref B1, file.%B1.decl [concrete = constants.%B1]
+// CHECK:STDOUT:   %struct_type.base: type = struct_type {.base: %B1} [concrete = constants.%struct_type.base]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.base [concrete = constants.%complete_type.5ac]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 3 - 1
toolchain/check/testdata/class/fail_base_unbound.carbon

@@ -57,6 +57,7 @@ let b: B = C.base;
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @B {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type.357]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -67,7 +68,8 @@ let b: B = C.base;
 // CHECK:STDOUT: class @C {
 // CHECK:STDOUT:   %B.ref: type = name_ref B, file.%B.decl [concrete = constants.%B]
 // CHECK:STDOUT:   %.loc14: %C.elem = base_decl %B.ref, element0 [concrete]
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.base.0ff [concrete = constants.%complete_type.98e]
+// CHECK:STDOUT:   %struct_type.base: type = struct_type {.base: %B} [concrete = constants.%struct_type.base.0ff]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.base [concrete = constants.%complete_type.98e]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:

+ 2 - 0
toolchain/check/testdata/class/fail_compound_type_mismatch.carbon

@@ -85,6 +85,7 @@ fn AccessBInA(a: A) -> i32 {
 // CHECK:STDOUT:     %.loc12_3: %A.elem = var_pattern %.loc12_8
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.var: ref %A.elem = var <none>
+// CHECK:STDOUT:   %struct_type.a: type = struct_type {.a: %i32} [concrete = constants.%struct_type.a]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.a [concrete = constants.%complete_type.fd7]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -99,6 +100,7 @@ fn AccessBInA(a: A) -> i32 {
 // CHECK:STDOUT:     %.loc16_3: %B.elem = var_pattern %.loc16_8
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.var: ref %B.elem = var <none>
+// CHECK:STDOUT:   %struct_type.b: type = struct_type {.b: %i32} [concrete = constants.%struct_type.b]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.b [concrete = constants.%complete_type.ba8]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 2 - 1
toolchain/check/testdata/class/fail_convert_to_invalid.carbon

@@ -62,7 +62,8 @@ fn Make() -> C {
 // CHECK:STDOUT:     %.loc16_3: <error> = var_pattern %.loc16_8
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.var: ref <error> = var <none>
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness <error> [concrete = <error>]
+// CHECK:STDOUT:   %struct_type.a: type = struct_type {.a: <error>} [concrete = <error>]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.a [concrete = <error>]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:

+ 4 - 1
toolchain/check/testdata/class/fail_derived_to_base.carbon

@@ -134,6 +134,7 @@ fn ConvertIncomplete(p: Incomplete*) -> A2* { return p; }
 // CHECK:STDOUT:     %.loc12_3: %A1.elem = var_pattern %.loc12_8
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.var: ref %A1.elem = var <none>
+// CHECK:STDOUT:   %struct_type.a: type = struct_type {.a: %i32} [concrete = constants.%struct_type.a]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.a [concrete = constants.%complete_type.fd7]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -148,6 +149,7 @@ fn ConvertIncomplete(p: Incomplete*) -> A2* { return p; }
 // CHECK:STDOUT:     %.loc16_3: %A2.elem = var_pattern %.loc16_8
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.var: ref %A2.elem = var <none>
+// CHECK:STDOUT:   %struct_type.a: type = struct_type {.a: %i32} [concrete = constants.%struct_type.a]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.a [concrete = constants.%complete_type.fd7]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -164,7 +166,8 @@ fn ConvertIncomplete(p: Incomplete*) -> A2* { return p; }
 // CHECK:STDOUT:     %.loc21_3: %B2.elem.4b2 = var_pattern %.loc21_8
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.var: ref %B2.elem.4b2 = var <none>
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.base.b.618 [concrete = constants.%complete_type.92f]
+// CHECK:STDOUT:   %struct_type.base.b: type = struct_type {.base: %A2, .b: %i32} [concrete = constants.%struct_type.base.b.618]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.base.b [concrete = constants.%complete_type.92f]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:

+ 4 - 1
toolchain/check/testdata/class/fail_extend_cycle.carbon

@@ -67,6 +67,7 @@ base class A {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @A.1 {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type.357]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -78,6 +79,7 @@ base class A {
 // CHECK:STDOUT: class @B {
 // CHECK:STDOUT:   %A.ref: type = name_ref A, file.%A.decl.loc11 [concrete = constants.%A.466950.1]
 // CHECK:STDOUT:   %.loc16: %B.elem = base_decl %A.ref, element0 [concrete]
+// CHECK:STDOUT:   %struct_type.base: type = struct_type {.base: %A.466950.1} [concrete = constants.%struct_type.base]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.base [concrete = constants.%complete_type.020]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -96,7 +98,8 @@ base class A {
 // CHECK:STDOUT:     %.loc32_3: <error> = var_pattern %.loc32_8
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.var: ref <error> = var <none>
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness <error> [concrete = <error>]
+// CHECK:STDOUT:   %struct_type.base.c: type = struct_type {.base: %A.466950.1, .c: <error>} [concrete = <error>]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.base.c [concrete = <error>]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:

+ 1 - 0
toolchain/check/testdata/class/fail_field_modifiers.carbon

@@ -123,6 +123,7 @@ class Class {
 // CHECK:STDOUT:   %.loc35_22.1: %i32 = value_of_initializer %int.convert_checked.loc35 [concrete = constants.%int_1.5d2]
 // CHECK:STDOUT:   %.loc35_22.2: %i32 = converted %int_1, %.loc35_22.1 [concrete = constants.%int_1.5d2]
 // CHECK:STDOUT:   %m: %i32 = bind_name m, %.loc35_22.2
+// CHECK:STDOUT:   %struct_type.j.k: type = struct_type {.j: %i32, .k: %i32} [concrete = constants.%struct_type.j.k]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.j.k [concrete = constants.%complete_type.cf7]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 5 - 4
toolchain/check/testdata/class/fail_generic_method.carbon

@@ -99,13 +99,13 @@ fn Class(N:! i32).F[self: Self](n: T) {}
 // CHECK:STDOUT:   %T.patt.loc11_13.2: type = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc11_13.2 (constants.%T.patt)]
 // CHECK:STDOUT:
 // CHECK:STDOUT: !definition:
-// CHECK:STDOUT:   %require_complete: <witness> = require_complete_type @Class.%T.loc11_13.2 (%T) [symbolic = %require_complete (constants.%require_complete.4ae)]
+// CHECK:STDOUT:   %require_complete: <witness> = require_complete_type %T.loc11_13.2 [symbolic = %require_complete (constants.%require_complete.4ae)]
 // CHECK:STDOUT:   %Class: type = class_type @Class, @Class(%T.loc11_13.2) [symbolic = %Class (constants.%Class)]
 // CHECK:STDOUT:   %Class.elem: type = unbound_element_type %Class, %T.loc11_13.2 [symbolic = %Class.elem (constants.%Class.elem)]
 // CHECK:STDOUT:   %F.type: type = fn_type @F.1, @Class(%T.loc11_13.2) [symbolic = %F.type (constants.%F.type.6d6)]
 // CHECK:STDOUT:   %F: @Class.%F.type (%F.type.6d6) = struct_value () [symbolic = %F (constants.%F.cca)]
-// CHECK:STDOUT:   %struct_type.a: type = struct_type {.a: @Class.%T.loc11_13.2 (%T)} [symbolic = %struct_type.a (constants.%struct_type.a)]
-// CHECK:STDOUT:   %complete_type.loc14_1.2: <witness> = complete_type_witness @Class.%struct_type.a (%struct_type.a) [symbolic = %complete_type.loc14_1.2 (constants.%complete_type.f1b)]
+// CHECK:STDOUT:   %struct_type.a.loc14_1.2: type = struct_type {.a: @Class.%T.loc11_13.2 (%T)} [symbolic = %struct_type.a.loc14_1.2 (constants.%struct_type.a)]
+// CHECK:STDOUT:   %complete_type.loc14_1.2: <witness> = complete_type_witness %struct_type.a.loc14_1.2 [symbolic = %complete_type.loc14_1.2 (constants.%complete_type.f1b)]
 // CHECK:STDOUT:
 // CHECK:STDOUT:   class {
 // CHECK:STDOUT:     %.loc12_8: @Class.%Class.elem (%Class.elem) = field_decl a, element0 [concrete]
@@ -129,7 +129,8 @@ fn Class(N:! i32).F[self: Self](n: T) {}
 // CHECK:STDOUT:       %T.ref: type = name_ref T, @Class.%T.loc11_13.1 [symbolic = %T (constants.%T)]
 // CHECK:STDOUT:       %n: @F.1.%T (%T) = bind_name n, %n.param
 // CHECK:STDOUT:     }
-// CHECK:STDOUT:     %complete_type.loc14_1.1: <witness> = complete_type_witness %struct_type.a [symbolic = %complete_type.loc14_1.2 (constants.%complete_type.f1b)]
+// CHECK:STDOUT:     %struct_type.a.loc14_1.1: type = struct_type {.a: %T} [symbolic = %struct_type.a.loc14_1.2 (constants.%struct_type.a)]
+// CHECK:STDOUT:     %complete_type.loc14_1.1: <witness> = complete_type_witness %struct_type.a.loc14_1.1 [symbolic = %complete_type.loc14_1.2 (constants.%complete_type.f1b)]
 // CHECK:STDOUT:     complete_type_witness = %complete_type.loc14_1.1
 // CHECK:STDOUT:
 // CHECK:STDOUT:   !members:

+ 2 - 0
toolchain/check/testdata/class/fail_import_misuses.carbon

@@ -72,6 +72,7 @@ var a: Incomplete;
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @Empty {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -129,6 +130,7 @@ var a: Incomplete;
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @Empty.2 {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 3 - 1
toolchain/check/testdata/class/fail_incomplete.carbon

@@ -376,6 +376,7 @@ class C {
 // CHECK:STDOUT:     }
 // CHECK:STDOUT:     %self: %ptr.e71 = bind_name self, %self.param
 // CHECK:STDOUT:   }
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type.357]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -501,7 +502,8 @@ class C {
 // CHECK:STDOUT:     %.loc12_3: <error> = var_pattern %.loc12_8
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.var: ref <error> = var <none>
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness <error> [concrete = <error>]
+// CHECK:STDOUT:   %struct_type.c: type = struct_type {.c: <error>} [concrete = <error>]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.c [concrete = <error>]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:

+ 1 - 0
toolchain/check/testdata/class/fail_init.carbon

@@ -92,6 +92,7 @@ fn F() {
 // CHECK:STDOUT:     %.loc13_3: %Class.elem = var_pattern %.loc13_8
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.var.loc13: ref %Class.elem = var <none>
+// CHECK:STDOUT:   %struct_type.a.b: type = struct_type {.a: %i32, .b: %i32} [concrete = constants.%struct_type.a.b]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.a.b [concrete = constants.%complete_type.705]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 2 - 1
toolchain/check/testdata/class/fail_init_as_inplace.carbon

@@ -105,7 +105,8 @@ fn F() {
 // CHECK:STDOUT:     %.loc13_3: %Class.elem = var_pattern %.loc13_8
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.var.loc13: ref %Class.elem = var <none>
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.a.b.501 [concrete = constants.%complete_type.705]
+// CHECK:STDOUT:   %struct_type.a.b: type = struct_type {.a: %i32, .b: %i32} [concrete = constants.%struct_type.a.b.501]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.a.b [concrete = constants.%complete_type.705]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:

+ 3 - 1
toolchain/check/testdata/class/fail_memaccess_category.carbon

@@ -105,6 +105,7 @@ fn F(s: {.a: A}, b: B) {
 // CHECK:STDOUT:     }
 // CHECK:STDOUT:     %self: %ptr.6db = bind_name self, %self.param
 // CHECK:STDOUT:   }
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type.357]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -120,7 +121,8 @@ fn F(s: {.a: A}, b: B) {
 // CHECK:STDOUT:     %.loc16_3: %B.elem = var_pattern %.loc16_8
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.var: ref %B.elem = var <none>
-// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.a.72c [concrete = constants.%complete_type.2b9]
+// CHECK:STDOUT:   %struct_type.a: type = struct_type {.a: %A} [concrete = constants.%struct_type.a.72c]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.a [concrete = constants.%complete_type.2b9]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:

+ 1 - 0
toolchain/check/testdata/class/fail_member_of_let.carbon

@@ -73,6 +73,7 @@ fn T.F() {}
 // CHECK:STDOUT:     %return.param: ref %i32 = out_param call_param0
 // CHECK:STDOUT:     %return: ref %i32 = return_slot %return.param
 // CHECK:STDOUT:   }
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type.357]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 1 - 0
toolchain/check/testdata/class/fail_method.carbon

@@ -101,6 +101,7 @@ fn F(c: Class) {
 // CHECK:STDOUT:     %Class.ref: type = name_ref Class, file.%Class.decl [concrete = constants.%Class]
 // CHECK:STDOUT:     %self: %Class = bind_name self, %self.param
 // CHECK:STDOUT:   }
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 3 - 0
toolchain/check/testdata/class/fail_method_modifiers.carbon

@@ -113,6 +113,7 @@ base class BaseClass {
 // CHECK:STDOUT:     %Self.ref: type = name_ref Self, constants.%FinalClass [concrete = constants.%FinalClass]
 // CHECK:STDOUT:     %self: %FinalClass = bind_name self, %self.param
 // CHECK:STDOUT:   }
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -139,6 +140,7 @@ base class BaseClass {
 // CHECK:STDOUT:     %Self.ref: type = name_ref Self, constants.%AbstractClass [concrete = constants.%AbstractClass]
 // CHECK:STDOUT:     %self: %AbstractClass = bind_name self, %self.param
 // CHECK:STDOUT:   }
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -157,6 +159,7 @@ base class BaseClass {
 // CHECK:STDOUT:     %Self.ref: type = name_ref Self, constants.%BaseClass [concrete = constants.%BaseClass]
 // CHECK:STDOUT:     %self: %BaseClass = bind_name self, %self.param
 // CHECK:STDOUT:   }
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 1 - 0
toolchain/check/testdata/class/fail_method_redefinition.carbon

@@ -51,6 +51,7 @@ class Class {
 // CHECK:STDOUT: class @Class {
 // CHECK:STDOUT:   %F.decl.loc12: %F.type.f1baa3.1 = fn_decl @F.1 [concrete = constants.%F.1f2582.1] {} {}
 // CHECK:STDOUT:   %F.decl.loc20: %F.type.f1baa3.2 = fn_decl @F.2 [concrete = constants.%F.1f2582.2] {} {}
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 5 - 0
toolchain/check/testdata/class/fail_modifiers.carbon

@@ -170,6 +170,7 @@ fn AbstractWithDefinition.G[self: Self]() {
 // CHECK:STDOUT: class @BaseDecl;
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @TwoAbstract {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type.357]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -178,6 +179,7 @@ fn AbstractWithDefinition.G[self: Self]() {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @Virtual {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type.357]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -186,6 +188,7 @@ fn AbstractWithDefinition.G[self: Self]() {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @WrongOrder {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type.357]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -194,6 +197,7 @@ fn AbstractWithDefinition.G[self: Self]() {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @AbstractAndBase {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type.357]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -219,6 +223,7 @@ fn AbstractWithDefinition.G[self: Self]() {
 // CHECK:STDOUT:     %self.loc91: %AbstractWithDefinition = bind_name self, %self.param.loc91
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.loc92: <vtable> = vtable (%F.decl, %G.decl) [concrete = constants.%.9a5]
+// CHECK:STDOUT:   %struct_type.vptr: type = struct_type {.<vptr>: %ptr.454} [concrete = constants.%struct_type.vptr]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.vptr [concrete = constants.%complete_type.513]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 1 - 0
toolchain/check/testdata/class/fail_out_of_line_decl.carbon

@@ -44,6 +44,7 @@ fn C.F() {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @C {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 6 - 0
toolchain/check/testdata/class/fail_redeclaration_scope.carbon

@@ -63,6 +63,7 @@ class Y {
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @A.1 {
 // CHECK:STDOUT:   %B.decl: type = class_decl @B.2 [concrete = constants.%B.29a] {} {}
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -74,6 +75,7 @@ class Y {
 // CHECK:STDOUT: class @X {
 // CHECK:STDOUT:   %A.decl: type = class_decl @A.2 [concrete = constants.%A.197] {} {}
 // CHECK:STDOUT:   %B.decl: type = class_decl @B.1 [concrete = constants.%B.d5b] {} {}
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -84,6 +86,7 @@ class Y {
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @A.2 {
 // CHECK:STDOUT:   %B.decl: type = class_decl @B.1 [concrete = constants.%B.d5b] {} {}
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -93,6 +96,7 @@ class Y {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @B.1 {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -104,6 +108,7 @@ class Y {
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @Y {
 // CHECK:STDOUT:   %B.decl: type = class_decl @B.3 [concrete = constants.%B.768] {} {}
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -112,6 +117,7 @@ class Y {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @B.3 {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 2 - 0
toolchain/check/testdata/class/fail_redefinition.carbon

@@ -93,6 +93,7 @@ fn Class.I() {}
 // CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {}
 // CHECK:STDOUT:   %H.decl: %H.type.91d18a.1 = fn_decl @H.1 [concrete = constants.%H.d38c33.1] {} {}
 // CHECK:STDOUT:   %I.decl: %I.type.2b6c8d.1 = fn_decl @I.1 [concrete = constants.%I.c9aec9.1] {} {}
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -108,6 +109,7 @@ fn Class.I() {}
 // CHECK:STDOUT:   %G.decl: %G.type.621c12.1 = fn_decl @G.1 [concrete = constants.%G.f0cac0.1] {} {}
 // CHECK:STDOUT:   %H.decl: %H.type.91d18a.2 = fn_decl @H.2 [concrete = constants.%H.d38c33.2] {} {}
 // CHECK:STDOUT:   %I.decl: %I.type.2b6c8d.2 = fn_decl @I.2 [concrete = constants.%I.c9aec9.2] {} {}
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 1 - 0
toolchain/check/testdata/class/fail_scope.carbon

@@ -87,6 +87,7 @@ fn G() -> i32 {
 // CHECK:STDOUT:     %return.param: ref %i32 = out_param call_param0
 // CHECK:STDOUT:     %return: ref %i32 = return_slot %return.param
 // CHECK:STDOUT:   }
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type.357]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 2 - 0
toolchain/check/testdata/class/fail_self.carbon

@@ -135,6 +135,7 @@ fn CallWrongSelf(ws: WrongSelf) {
 // CHECK:STDOUT:     %return.param.loc18: ref %Class = out_param call_param0
 // CHECK:STDOUT:     %return.loc18: ref %Class = return_slot %return.param.loc18
 // CHECK:STDOUT:   }
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:
@@ -153,6 +154,7 @@ fn CallWrongSelf(ws: WrongSelf) {
 // CHECK:STDOUT:     %Class.ref: type = name_ref Class, file.%Class.decl [concrete = constants.%Class]
 // CHECK:STDOUT:     %self: %Class = bind_name self, %self.param
 // CHECK:STDOUT:   }
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 1 - 0
toolchain/check/testdata/class/fail_self_param.carbon

@@ -61,6 +61,7 @@ var v: C(0);
 // CHECK:STDOUT: !definition:
 // CHECK:STDOUT:
 // CHECK:STDOUT:   class {
+// CHECK:STDOUT:     %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
 // CHECK:STDOUT:     %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
 // CHECK:STDOUT:     complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 1 - 0
toolchain/check/testdata/class/fail_self_type_member.carbon

@@ -43,6 +43,7 @@ fn F() -> bool {
 // CHECK:STDOUT:     %.loc12_3: %Class.elem = var_pattern %.loc12_8
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.var: ref %Class.elem = var <none>
+// CHECK:STDOUT:   %struct_type.b: type = struct_type {.b: bool} [concrete = constants.%struct_type.b]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.b [concrete = constants.%complete_type]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

+ 1 - 0
toolchain/check/testdata/class/fail_unbound_field.carbon

@@ -84,6 +84,7 @@ fn G() -> i32 {
 // CHECK:STDOUT:     %return.param: ref %i32 = out_param call_param0
 // CHECK:STDOUT:     %return: ref %i32 = return_slot %return.param
 // CHECK:STDOUT:   }
+// CHECK:STDOUT:   %struct_type.field: type = struct_type {.field: %i32} [concrete = constants.%struct_type.field]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.field [concrete = constants.%complete_type.d48]
 // CHECK:STDOUT:   complete_type_witness = %complete_type
 // CHECK:STDOUT:

Некоторые файлы не были показаны из-за большого количества измененных файлов