瀏覽代碼

Move type functions off Context (#4951)

This creates a new check/type.h for most logic, and also moves some
functions to TypeStore in sem_ir/type.h. My approach for TypeStore is to
focus on moving the read-only functions there.
Jon Ross-Perkins 1 年之前
父節點
當前提交
dc8f47e6ad
共有 39 個文件被更改,包括 506 次插入393 次删除
  1. 2 0
      toolchain/check/BUILD
  2. 7 5
      toolchain/check/call.cpp
  3. 2 1
      toolchain/check/check_unit.cpp
  4. 0 117
      toolchain/check/context.cpp
  5. 0 91
      toolchain/check/context.h
  6. 6 5
      toolchain/check/convert.cpp
  7. 2 1
      toolchain/check/deduce.cpp
  8. 9 7
      toolchain/check/eval.cpp
  9. 11 10
      toolchain/check/generic.cpp
  10. 2 1
      toolchain/check/handle_array.cpp
  11. 2 1
      toolchain/check/handle_binding_pattern.cpp
  12. 23 19
      toolchain/check/handle_class.cpp
  13. 6 4
      toolchain/check/handle_function.cpp
  14. 2 1
      toolchain/check/handle_impl.cpp
  15. 5 3
      toolchain/check/handle_index.cpp
  16. 6 4
      toolchain/check/handle_interface.cpp
  17. 6 5
      toolchain/check/handle_literal.cpp
  18. 3 2
      toolchain/check/handle_name.cpp
  19. 2 1
      toolchain/check/handle_namespace.cpp
  20. 2 1
      toolchain/check/handle_operator.cpp
  21. 3 2
      toolchain/check/handle_struct.cpp
  22. 2 1
      toolchain/check/handle_tuple_literal.cpp
  23. 2 2
      toolchain/check/handle_where.cpp
  24. 13 7
      toolchain/check/impl.cpp
  25. 2 1
      toolchain/check/import.cpp
  26. 2 1
      toolchain/check/import_cpp.cpp
  27. 96 66
      toolchain/check/import_ref.cpp
  28. 5 3
      toolchain/check/interface.cpp
  29. 3 2
      toolchain/check/literal.cpp
  30. 11 10
      toolchain/check/member_access.cpp
  31. 4 2
      toolchain/check/name_lookup.cpp
  32. 2 1
      toolchain/check/pattern_match.cpp
  33. 6 5
      toolchain/check/subst.cpp
  34. 116 0
      toolchain/check/type.cpp
  35. 83 0
      toolchain/check/type.h
  36. 11 9
      toolchain/check/type_completion.cpp
  37. 2 2
      toolchain/docs/adding_features.md
  38. 20 0
      toolchain/sem_ir/type.cpp
  39. 25 0
      toolchain/sem_ir/type.h

+ 2 - 0
toolchain/check/BUILD

@@ -42,6 +42,7 @@ cc_library(
         "return.cpp",
         "subpattern.cpp",
         "subst.cpp",
+        "type.cpp",
         "type_completion.cpp",
     ],
     hdrs = [
@@ -78,6 +79,7 @@ cc_library(
         "return.h",
         "subpattern.h",
         "subst.h",
+        "type.h",
         "type_completion.h",
     ],
     deps = [

+ 7 - 5
toolchain/check/call.cpp

@@ -9,6 +9,7 @@
 #include "toolchain/check/convert.h"
 #include "toolchain/check/deduce.h"
 #include "toolchain/check/function.h"
+#include "toolchain/check/type.h"
 #include "toolchain/diagnostics/format_providers.h"
 #include "toolchain/sem_ir/builtin_function_kind.h"
 #include "toolchain/sem_ir/entity_with_params_base.h"
@@ -118,8 +119,9 @@ static auto PerformCallToGenericInterface(
   if (!callee_specific_id) {
     return SemIR::ErrorInst::SingletonInstId;
   }
-  return context.GetOrAddInst(loc_id, context.FacetTypeFromInterface(
-                                          interface_id, *callee_specific_id));
+  return context.GetOrAddInst(
+      loc_id,
+      FacetTypeFromInterface(context, interface_id, *callee_specific_id));
 }
 
 auto PerformCall(Context& context, SemIR::LocId loc_id, SemIR::InstId callee_id,
@@ -164,8 +166,8 @@ auto PerformCall(Context& context, SemIR::LocId loc_id, SemIR::InstId callee_id,
     callee_id = context.GetOrAddInst(
         context.insts().GetLocId(callee_id),
         SemIR::SpecificFunction{
-            .type_id = context.GetSingletonType(
-                SemIR::SpecificFunctionType::SingletonInstId),
+            .type_id = GetSingletonType(
+                context, SemIR::SpecificFunctionType::SingletonInstId),
             .callee_id = callee_id,
             .specific_id = *callee_specific_id});
     if (callee_function.self_type_id.has_value()) {
@@ -201,7 +203,7 @@ auto PerformCall(Context& context, SemIR::LocId loc_id, SemIR::InstId callee_id,
       // For functions with an implicit return type, the return type is the
       // empty tuple type.
       if (!return_info.type_id.has_value()) {
-        return_info.type_id = context.GetTupleType({});
+        return_info.type_id = GetTupleType(context, {});
       }
       break;
     case SemIR::InitRepr::ByCopy:

+ 2 - 1
toolchain/check/check_unit.cpp

@@ -18,6 +18,7 @@
 #include "toolchain/check/import_cpp.h"
 #include "toolchain/check/import_ref.h"
 #include "toolchain/check/node_id_traversal.h"
+#include "toolchain/check/type.h"
 
 namespace Carbon::Check {
 
@@ -92,7 +93,7 @@ auto CheckUnit::Run() -> void {
 auto CheckUnit::InitPackageScopeAndImports() -> void {
   // Importing makes many namespaces, so only canonicalize the type once.
   auto namespace_type_id =
-      context_.GetSingletonType(SemIR::NamespaceType::SingletonInstId);
+      GetSingletonType(context_, SemIR::NamespaceType::SingletonInstId);
 
   // Define the package scope, with an instruction for `package` expressions to
   // reference.

+ 0 - 117
toolchain/check/context.cpp

@@ -227,123 +227,6 @@ auto Context::Finalize() -> void {
   global_init_.Finalize();
 }
 
-auto Context::GetTypeIdForTypeConstant(SemIR::ConstantId constant_id)
-    -> SemIR::TypeId {
-  CARBON_CHECK(constant_id.is_constant(),
-               "Canonicalizing non-constant type: {0}", constant_id);
-  auto type_id =
-      insts().Get(constant_values().GetInstId(constant_id)).type_id();
-  CARBON_CHECK(type_id == SemIR::TypeType::SingletonTypeId ||
-                   constant_id == SemIR::ErrorInst::SingletonConstantId,
-               "Forming type ID for non-type constant of type {0}",
-               types().GetAsInst(type_id));
-
-  return SemIR::TypeId::ForTypeConstant(constant_id);
-}
-
-auto Context::FacetTypeFromInterface(SemIR::InterfaceId interface_id,
-                                     SemIR::SpecificId specific_id)
-    -> SemIR::FacetType {
-  SemIR::FacetTypeId facet_type_id = facet_types().Add(
-      SemIR::FacetTypeInfo{.impls_constraints = {{interface_id, specific_id}},
-                           .other_requirements = false});
-  return {.type_id = SemIR::TypeType::SingletonTypeId,
-          .facet_type_id = facet_type_id};
-}
-
-// Gets or forms a type_id for a type, given the instruction kind and arguments.
-template <typename InstT, typename... EachArgT>
-static auto GetTypeImpl(Context& context, EachArgT... each_arg)
-    -> SemIR::TypeId {
-  // TODO: Remove inst_id parameter from TryEvalInst.
-  InstT inst = {SemIR::TypeType::SingletonTypeId, each_arg...};
-  return context.GetTypeIdForTypeConstant(
-      TryEvalInst(context, SemIR::InstId::None, inst));
-}
-
-// Gets or forms a type_id for a type, given the instruction kind and arguments,
-// and completes the type. This should only be used when type completion cannot
-// fail.
-template <typename InstT, typename... EachArgT>
-static auto GetCompleteTypeImpl(Context& context, EachArgT... each_arg)
-    -> SemIR::TypeId {
-  auto type_id = GetTypeImpl<InstT>(context, each_arg...);
-  CompleteTypeOrCheckFail(context, type_id);
-  return type_id;
-}
-
-auto Context::GetStructType(SemIR::StructTypeFieldsId fields_id)
-    -> SemIR::TypeId {
-  return GetTypeImpl<SemIR::StructType>(*this, fields_id);
-}
-
-auto Context::GetTupleType(llvm::ArrayRef<SemIR::TypeId> type_ids)
-    -> SemIR::TypeId {
-  return GetTypeImpl<SemIR::TupleType>(*this,
-                                       type_blocks().AddCanonical(type_ids));
-}
-
-auto Context::GetAssociatedEntityType(SemIR::TypeId interface_type_id)
-    -> SemIR::TypeId {
-  return GetTypeImpl<SemIR::AssociatedEntityType>(*this, interface_type_id);
-}
-
-auto Context::GetSingletonType(SemIR::InstId singleton_id) -> SemIR::TypeId {
-  CARBON_CHECK(SemIR::IsSingletonInstId(singleton_id));
-  auto type_id = GetTypeIdForTypeInst(singleton_id);
-  // To keep client code simpler, complete builtin types before returning them.
-  CompleteTypeOrCheckFail(*this, type_id);
-  return type_id;
-}
-
-auto Context::GetClassType(SemIR::ClassId class_id,
-                           SemIR::SpecificId specific_id) -> SemIR::TypeId {
-  return GetTypeImpl<SemIR::ClassType>(*this, class_id, specific_id);
-}
-
-auto Context::GetFunctionType(SemIR::FunctionId fn_id,
-                              SemIR::SpecificId specific_id) -> SemIR::TypeId {
-  return GetCompleteTypeImpl<SemIR::FunctionType>(*this, fn_id, specific_id);
-}
-
-auto Context::GetFunctionTypeWithSelfType(
-    SemIR::InstId interface_function_type_id, SemIR::InstId self_id)
-    -> SemIR::TypeId {
-  return GetCompleteTypeImpl<SemIR::FunctionTypeWithSelfType>(
-      *this, interface_function_type_id, self_id);
-}
-
-auto Context::GetGenericClassType(SemIR::ClassId class_id,
-                                  SemIR::SpecificId enclosing_specific_id)
-    -> SemIR::TypeId {
-  return GetCompleteTypeImpl<SemIR::GenericClassType>(*this, class_id,
-                                                      enclosing_specific_id);
-}
-
-auto Context::GetGenericInterfaceType(SemIR::InterfaceId interface_id,
-                                      SemIR::SpecificId enclosing_specific_id)
-    -> SemIR::TypeId {
-  return GetCompleteTypeImpl<SemIR::GenericInterfaceType>(
-      *this, interface_id, enclosing_specific_id);
-}
-
-auto Context::GetInterfaceType(SemIR::InterfaceId interface_id,
-                               SemIR::SpecificId specific_id) -> SemIR::TypeId {
-  return GetTypeImpl<SemIR::FacetType>(
-      *this, FacetTypeFromInterface(interface_id, specific_id).facet_type_id);
-}
-
-auto Context::GetPointerType(SemIR::TypeId pointee_type_id) -> SemIR::TypeId {
-  return GetTypeImpl<SemIR::PointerType>(*this, pointee_type_id);
-}
-
-auto Context::GetUnboundElementType(SemIR::TypeId class_type_id,
-                                    SemIR::TypeId element_type_id)
-    -> SemIR::TypeId {
-  return GetTypeImpl<SemIR::UnboundElementType>(*this, class_type_id,
-                                                element_type_id);
-}
-
 auto Context::PrintForStackDump(llvm::raw_ostream& output) const -> void {
   output << "Check::Context\n";
 

+ 0 - 91
toolchain/check/context.h

@@ -178,97 +178,6 @@ class Context {
     sem_ir().insts().SetLocId(inst_id, SemIR::LocId(node_id));
   }
 
-  // Returns the type ID for a constant that is a type value, i.e. it is a value
-  // of type `TypeType`.
-  //
-  // Facet values are of the same typishness as types, but are not themselves
-  // types, so they can not be passed here. They should be converted to a type
-  // through an `as type` conversion, that is, to a value of type `TypeType`.
-  auto GetTypeIdForTypeConstant(SemIR::ConstantId constant_id) -> SemIR::TypeId;
-
-  // Returns the type ID for an instruction whose constant value is a type
-  // value, i.e. it is a value of type `TypeType`.
-  //
-  // Instructions whose values are facet values (see `FacetValue`) produce a
-  // value of the same typishness as types, but which are themselves not types,
-  // so they can not be passed here. They should be converted to a type through
-  // an `as type` conversion, such as to a `FacetAccessType` instruction whose
-  // value is of type `TypeType`.
-  auto GetTypeIdForTypeInst(SemIR::InstId inst_id) -> SemIR::TypeId {
-    return GetTypeIdForTypeConstant(constant_values().Get(inst_id));
-  }
-
-  // Returns whether `type_id` represents a facet type.
-  auto IsFacetType(SemIR::TypeId type_id) -> bool {
-    return type_id == SemIR::TypeType::SingletonTypeId ||
-           types().Is<SemIR::FacetType>(type_id);
-  }
-
-  // Create a FacetType typed instruction object consisting of a single
-  // interface.
-  auto FacetTypeFromInterface(SemIR::InterfaceId interface_id,
-                              SemIR::SpecificId specific_id)
-      -> SemIR::FacetType;
-
-  // TODO: Consider moving these `Get*Type` functions to a separate class.
-
-  // Gets the type to use for an unbound associated entity declared in this
-  // interface. For example, this is the type of `I.T` after
-  // `interface I { let T:! type; }`.
-  // The name of the interface is used for diagnostics.
-  // TODO: Should we use a different type for each such entity, or the same type
-  // for all associated entities?
-  auto GetAssociatedEntityType(SemIR::TypeId interface_type_id)
-      -> SemIR::TypeId;
-
-  // Gets a singleton type. The returned type will be complete. Requires that
-  // `singleton_id` is already validated to be a singleton.
-  auto GetSingletonType(SemIR::InstId singleton_id) -> SemIR::TypeId;
-
-  // Gets a class type.
-  auto GetClassType(SemIR::ClassId class_id, SemIR::SpecificId specific_id)
-      -> SemIR::TypeId;
-
-  // Gets a function type. The returned type will be complete.
-  auto GetFunctionType(SemIR::FunctionId fn_id, SemIR::SpecificId specific_id)
-      -> SemIR::TypeId;
-
-  // Gets the type of an associated function with the `Self` parameter bound to
-  // a particular value. The returned type will be complete.
-  auto GetFunctionTypeWithSelfType(SemIR::InstId interface_function_type_id,
-                                   SemIR::InstId self_id) -> SemIR::TypeId;
-
-  // Gets a generic class type, which is the type of a name of a generic class,
-  // such as the type of `Vector` given `class Vector(T:! type)`. The returned
-  // type will be complete.
-  auto GetGenericClassType(SemIR::ClassId class_id,
-                           SemIR::SpecificId enclosing_specific_id)
-      -> SemIR::TypeId;
-
-  // Gets a generic interface type, which is the type of a name of a generic
-  // interface, such as the type of `AddWith` given
-  // `interface AddWith(T:! type)`. The returned type will be complete.
-  auto GetGenericInterfaceType(SemIR::InterfaceId interface_id,
-                               SemIR::SpecificId enclosing_specific_id)
-      -> SemIR::TypeId;
-
-  // Gets the facet type corresponding to a particular interface.
-  auto GetInterfaceType(SemIR::InterfaceId interface_id,
-                        SemIR::SpecificId specific_id) -> SemIR::TypeId;
-
-  // Returns a pointer type whose pointee type is `pointee_type_id`.
-  auto GetPointerType(SemIR::TypeId pointee_type_id) -> SemIR::TypeId;
-
-  // Returns a struct type with the given fields.
-  auto GetStructType(SemIR::StructTypeFieldsId fields_id) -> SemIR::TypeId;
-
-  // Returns a tuple type with the given element types.
-  auto GetTupleType(llvm::ArrayRef<SemIR::TypeId> type_ids) -> SemIR::TypeId;
-
-  // Returns an unbound element type.
-  auto GetUnboundElementType(SemIR::TypeId class_type_id,
-                             SemIR::TypeId element_type_id) -> SemIR::TypeId;
-
   // Adds an exported name.
   auto AddExport(SemIR::InstId inst_id) -> void { exports_.push_back(inst_id); }
 

+ 6 - 5
toolchain/check/convert.cpp

@@ -16,6 +16,7 @@
 #include "toolchain/check/impl_lookup.h"
 #include "toolchain/check/operator.h"
 #include "toolchain/check/pattern_match.h"
+#include "toolchain/check/type.h"
 #include "toolchain/check/type_completion.h"
 #include "toolchain/diagnostics/format_providers.h"
 #include "toolchain/sem_ir/copy_on_write_block.h"
@@ -159,8 +160,8 @@ static auto MakeElementAccessInst(Context& context, SemIR::LocId loc_id,
     // index so that we don't need an integer literal instruction here, and
     // remove this special case.
     auto index_id = block.template AddInst<SemIR::IntValue>(
-        loc_id, {.type_id = context.GetSingletonType(
-                     SemIR::IntLiteralType::SingletonInstId),
+        loc_id, {.type_id = GetSingletonType(
+                     context, SemIR::IntLiteralType::SingletonInstId),
                  .int_id = context.ints().Add(static_cast<int64_t>(i))});
     return block.template AddInst<AccessInstT>(
         loc_id, {elem_type_id, aggregate_id, index_id});
@@ -964,7 +965,7 @@ static auto PerformBuiltinConversion(Context& context, SemIR::LocId loc_id,
         // iterative approach.
         type_ids.push_back(ExprAsType(context, loc_id, tuple_inst_id).type_id);
       }
-      auto tuple_type_id = context.GetTupleType(type_ids);
+      auto tuple_type_id = GetTupleType(context, type_ids);
       return sem_ir.types().GetInstId(tuple_type_id);
     }
 
@@ -1298,7 +1299,7 @@ auto ConvertToBoolValue(Context& context, SemIR::LocId loc_id,
                         SemIR::InstId value_id) -> SemIR::InstId {
   return ConvertToValueOfType(
       context, loc_id, value_id,
-      context.GetSingletonType(SemIR::BoolType::SingletonInstId));
+      GetSingletonType(context, SemIR::BoolType::SingletonInstId));
 }
 
 auto ConvertForExplicitAs(Context& context, Parse::NodeId as_node,
@@ -1361,7 +1362,7 @@ auto ExprAsType(Context& context, SemIR::LocId loc_id, SemIR::InstId value_id)
   }
 
   return {.inst_id = type_inst_id,
-          .type_id = context.GetTypeIdForTypeConstant(type_const_id)};
+          .type_id = context.types().GetTypeIdForTypeConstantId(type_const_id)};
 }
 
 }  // namespace Carbon::Check

+ 2 - 1
toolchain/check/deduce.cpp

@@ -551,7 +551,8 @@ auto DeductionContext::CheckDeductionIsComplete() -> bool {
       auto param_type_const_id = SubstConstant(
           context(), binding_type_id.AsConstantId(), substitutions_);
       CARBON_CHECK(param_type_const_id.has_value());
-      binding_type_id = context().GetTypeIdForTypeConstant(param_type_const_id);
+      binding_type_id =
+          context().types().GetTypeIdForTypeConstantId(param_type_const_id);
 
       // TODO: Suppress diagnostics here if `diagnose_` is false.
       DiagnosticAnnotationScope annotate_diagnostics(

+ 9 - 7
toolchain/check/eval.cpp

@@ -8,6 +8,7 @@
 #include "toolchain/check/diagnostic_helpers.h"
 #include "toolchain/check/generic.h"
 #include "toolchain/check/import_ref.h"
+#include "toolchain/check/type.h"
 #include "toolchain/check/type_completion.h"
 #include "toolchain/diagnostics/diagnostic_emitter.h"
 #include "toolchain/diagnostics/format_providers.h"
@@ -124,7 +125,7 @@ class EvalContext {
 
   // Gets the constant value of the specified type in this context.
   auto GetConstantValueAsType(SemIR::TypeId id) -> SemIR::TypeId {
-    return context().GetTypeIdForTypeConstant(GetConstantValue(id));
+    return context().types().GetTypeIdForTypeConstantId(GetConstantValue(id));
   }
 
   // Gets the instruction describing the constant value of the specified type in
@@ -332,7 +333,7 @@ static auto GetConstantValue(EvalContext& eval_context, SemIR::TypeId type_id,
                              Phase* phase) -> SemIR::TypeId {
   auto const_id = eval_context.GetConstantValue(type_id);
   *phase = LatestPhase(*phase, GetPhase(eval_context, const_id));
-  return eval_context.context().GetTypeIdForTypeConstant(const_id);
+  return eval_context.context().types().GetTypeIdForTypeConstantId(const_id);
 }
 
 // If the given instruction block contains only constants, returns a
@@ -689,7 +690,7 @@ static auto MakeIntTypeResult(Context& context, SemIRLoc loc,
                               SemIR::IntKind int_kind, SemIR::InstId width_id,
                               Phase phase) -> SemIR::ConstantId {
   auto result = SemIR::IntType{
-      .type_id = context.GetSingletonType(SemIR::TypeType::SingletonInstId),
+      .type_id = GetSingletonType(context, SemIR::TypeType::SingletonInstId),
       .int_kind = int_kind,
       .bit_width_id = width_id};
   if (!ValidateIntType(context, loc, result)) {
@@ -1751,8 +1752,9 @@ static auto TryEvalInstInContext(EvalContext& eval_context,
       // A non-generic interface declaration evaluates to a facet type.
       return MakeConstantResult(
           eval_context.context(),
-          eval_context.context().FacetTypeFromInterface(
-              interface_decl.interface_id, SemIR::SpecificId::None),
+          FacetTypeFromInterface(eval_context.context(),
+                                 interface_decl.interface_id,
+                                 SemIR::SpecificId::None),
           Phase::Concrete);
     }
 
@@ -2074,8 +2076,8 @@ static auto TryEvalInstInContext(EvalContext& eval_context,
 
     case CARBON_KIND(SemIR::RequireCompleteType require_complete): {
       auto phase = Phase::Concrete;
-      auto witness_type_id = eval_context.context().GetSingletonType(
-          SemIR::WitnessType::SingletonInstId);
+      auto witness_type_id = GetSingletonType(
+          eval_context.context(), SemIR::WitnessType::SingletonInstId);
       auto complete_type_id = GetConstantValue(
           eval_context, require_complete.complete_type_id, &phase);
 

+ 11 - 10
toolchain/check/generic.cpp

@@ -9,6 +9,7 @@
 #include "toolchain/check/eval.h"
 #include "toolchain/check/generic_region_stack.h"
 #include "toolchain/check/subst.h"
+#include "toolchain/check/type.h"
 #include "toolchain/check/type_completion.h"
 #include "toolchain/sem_ir/generic.h"
 #include "toolchain/sem_ir/ids.h"
@@ -179,7 +180,7 @@ static auto AddGenericTypeToEvalBlock(
       SubstInst(context, context.types().GetInstId(type_id),
                 RebuildGenericConstantInEvalBlockCallbacks(
                     context, generic_id, region, loc_id, constants_in_generic));
-  return context.GetTypeIdForTypeInst(type_inst_id);
+  return context.types().GetTypeIdForTypeInstId(type_inst_id);
 }
 
 // Adds instructions to compute the substituted value of `inst_id` in each
@@ -500,20 +501,20 @@ auto GetInstForSpecific(Context& context, SemIR::SpecificId specific_id)
   CARBON_KIND_SWITCH(decl) {
     case CARBON_KIND(SemIR::ClassDecl class_decl): {
       return context.types().GetInstId(
-          context.GetClassType(class_decl.class_id, specific_id));
+          GetClassType(context, class_decl.class_id, specific_id));
     }
     case CARBON_KIND(SemIR::InterfaceDecl interface_decl): {
       return context.types().GetInstId(
-          context.GetInterfaceType(interface_decl.interface_id, specific_id));
+          GetInterfaceType(context, interface_decl.interface_id, specific_id));
     }
     case SemIR::FunctionDecl::Kind: {
-      return context.constant_values().GetInstId(
-          TryEvalInst(context, SemIR::InstId::None,
-                      SemIR::SpecificFunction{
-                          .type_id = context.GetSingletonType(
-                              SemIR::SpecificFunctionType::SingletonInstId),
-                          .callee_id = generic.decl_id,
-                          .specific_id = specific_id}));
+      return context.constant_values().GetInstId(TryEvalInst(
+          context, SemIR::InstId::None,
+          SemIR::SpecificFunction{
+              .type_id = GetSingletonType(
+                  context, SemIR::SpecificFunctionType::SingletonInstId),
+              .callee_id = generic.decl_id,
+              .specific_id = specific_id}));
     }
     case SemIR::AssociatedConstantDecl::Kind: {
       // TODO: We don't have a good instruction to use here.

+ 2 - 1
toolchain/check/handle_array.cpp

@@ -5,6 +5,7 @@
 #include "toolchain/check/context.h"
 #include "toolchain/check/convert.h"
 #include "toolchain/check/handle.h"
+#include "toolchain/check/type.h"
 #include "toolchain/parse/node_kind.h"
 
 namespace Carbon::Check {
@@ -51,7 +52,7 @@ auto HandleParseNode(Context& context, Parse::ArrayExprId node_id) -> bool {
 
   bound_inst_id = ConvertToValueOfType(
       context, context.insts().GetLocId(bound_inst_id), bound_inst_id,
-      context.GetSingletonType(SemIR::IntLiteralType::SingletonInstId));
+      GetSingletonType(context, SemIR::IntLiteralType::SingletonInstId));
   context.AddInstAndPush<SemIR::ArrayType>(
       node_id, {.type_id = SemIR::TypeType::SingletonTypeId,
                 .bound_id = bound_inst_id,

+ 2 - 1
toolchain/check/handle_binding_pattern.cpp

@@ -9,6 +9,7 @@
 #include "toolchain/check/name_lookup.h"
 #include "toolchain/check/return.h"
 #include "toolchain/check/subpattern.h"
+#include "toolchain/check/type.h"
 #include "toolchain/check/type_completion.h"
 #include "toolchain/diagnostics/format_providers.h"
 #include "toolchain/sem_ir/ids.h"
@@ -124,7 +125,7 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
         context.parse_tree().As<Parse::VarBindingPatternId>(node_id);
     auto& class_info = context.classes().Get(parent_class_decl->class_id);
     auto field_type_id =
-        context.GetUnboundElementType(class_info.self_type_id, cast_type_id);
+        GetUnboundElementType(context, class_info.self_type_id, cast_type_id);
     auto field_id = context.AddInst<SemIR::FieldDecl>(
         binding_id, {.type_id = field_type_id,
                      .name_id = name_id,

+ 23 - 19
toolchain/check/handle_class.cpp

@@ -16,6 +16,7 @@
 #include "toolchain/check/modifiers.h"
 #include "toolchain/check/name_component.h"
 #include "toolchain/check/name_lookup.h"
+#include "toolchain/check/type.h"
 #include "toolchain/check/type_completion.h"
 #include "toolchain/parse/node_ids.h"
 #include "toolchain/sem_ir/function.h"
@@ -243,8 +244,8 @@ static auto BuildClassDecl(Context& context, Parse::AnyClassDeclId node_id,
     class_info.generic_id = BuildGenericDecl(context, class_decl_id);
     class_decl.class_id = context.classes().Add(class_info);
     if (class_info.has_parameters()) {
-      class_decl.type_id = context.GetGenericClassType(
-          class_decl.class_id, context.scope_stack().PeekSpecificId());
+      class_decl.type_id = GetGenericClassType(
+          context, class_decl.class_id, context.scope_stack().PeekSpecificId());
     }
   } else {
     FinishGenericRedecl(context, class_decl_id, class_info.generic_id);
@@ -260,11 +261,12 @@ static auto BuildClassDecl(Context& context, Parse::AnyClassDeclId node_id,
     auto& class_info = context.classes().Get(class_decl.class_id);
     auto specific_id =
         context.generics().GetSelfSpecific(class_info.generic_id);
-    class_info.self_type_id = context.GetTypeIdForTypeConstant(TryEvalInst(
-        context, SemIR::InstId::None,
-        SemIR::ClassType{.type_id = SemIR::TypeType::SingletonTypeId,
-                         .class_id = class_decl.class_id,
-                         .specific_id = specific_id}));
+    class_info.self_type_id =
+        context.types().GetTypeIdForTypeConstantId(TryEvalInst(
+            context, SemIR::InstId::None,
+            SemIR::ClassType{.type_id = SemIR::TypeType::SingletonTypeId,
+                             .class_id = class_decl.class_id,
+                             .specific_id = specific_id}));
   }
 
   if (!is_definition && context.sem_ir().is_impl() && !is_extern) {
@@ -542,8 +544,8 @@ auto HandleParseNode(Context& context, Parse::BaseDeclId node_id) -> bool {
 
   // The `base` value in the class scope has an unbound element type. Instance
   // binding will be performed when it's found by name lookup into an instance.
-  auto field_type_id =
-      context.GetUnboundElementType(class_info.self_type_id, base_info.type_id);
+  auto field_type_id = GetUnboundElementType(context, class_info.self_type_id,
+                                             base_info.type_id);
   class_info.base_id = context.AddInst<SemIR::BaseDecl>(
       node_id, {.type_id = field_type_id,
                 .base_type_inst_id = base_info.inst_id,
@@ -628,9 +630,9 @@ static auto CheckCompleteAdapterClassType(Context& context,
   auto object_repr_id = context.types().GetObjectRepr(adapted_type_id);
 
   return context.AddInst<SemIR::CompleteTypeWitness>(
-      node_id,
-      {.type_id = context.GetSingletonType(SemIR::WitnessType::SingletonInstId),
-       .object_repr_id = object_repr_id});
+      node_id, {.type_id = GetSingletonType(
+                    context, SemIR::WitnessType::SingletonInstId),
+                .object_repr_id = object_repr_id});
 }
 
 static auto AddStructTypeFields(
@@ -689,8 +691,9 @@ static auto CheckCompleteClassType(Context& context, Parse::NodeId node_id,
   if (defining_vptr) {
     struct_type_fields.push_back(
         {.name_id = SemIR::NameId::Vptr,
-         .type_id = context.GetPointerType(
-             context.GetSingletonType(SemIR::VtableType::SingletonInstId))});
+         .type_id = GetPointerType(
+             context,
+             GetSingletonType(context, SemIR::VtableType::SingletonInstId))});
   }
   if (base_type_id.has_value()) {
     auto base_decl = context.insts().GetAs<SemIR::BaseDecl>(class_info.base_id);
@@ -745,16 +748,17 @@ static auto CheckCompleteClassType(Context& context, Parse::NodeId node_id,
       }
     }
     class_info.vtable_id = context.AddInst<SemIR::Vtable>(
-        node_id, {.type_id = context.GetSingletonType(
-                      SemIR::VtableType::SingletonInstId),
+        node_id, {.type_id = GetSingletonType(
+                      context, SemIR::VtableType::SingletonInstId),
                   .virtual_functions_id = context.inst_blocks().Add(vtable)});
   }
 
   return context.AddInst<SemIR::CompleteTypeWitness>(
       node_id,
-      {.type_id = context.GetSingletonType(SemIR::WitnessType::SingletonInstId),
-       .object_repr_id = context.GetStructType(
-           AddStructTypeFields(context, struct_type_fields))});
+      {.type_id =
+           GetSingletonType(context, SemIR::WitnessType::SingletonInstId),
+       .object_repr_id = GetStructType(
+           context, AddStructTypeFields(context, struct_type_fields))});
 }
 
 auto HandleParseNode(Context& context, Parse::ClassDefinitionId node_id)

+ 6 - 4
toolchain/check/handle_function.cpp

@@ -19,6 +19,7 @@
 #include "toolchain/check/modifiers.h"
 #include "toolchain/check/name_component.h"
 #include "toolchain/check/name_lookup.h"
+#include "toolchain/check/type.h"
 #include "toolchain/check/type_completion.h"
 #include "toolchain/sem_ir/builtin_function_kind.h"
 #include "toolchain/sem_ir/entry_point.h"
@@ -309,8 +310,9 @@ static auto BuildFunctionDecl(Context& context,
     FinishGenericRedecl(context, decl_id, function_info.generic_id);
     // TODO: Validate that the redeclaration doesn't set an access modifier.
   }
-  function_decl.type_id = context.GetFunctionType(
-      function_decl.function_id, context.scope_stack().PeekSpecificId());
+  function_decl.type_id =
+      GetFunctionType(context, function_decl.function_id,
+                      context.scope_stack().PeekSpecificId());
 
   // Write the function ID into the FunctionDecl.
   context.ReplaceInstBeforeConstantUse(decl_id, function_decl);
@@ -351,7 +353,7 @@ static auto BuildFunctionDecl(Context& context,
         !function_info.param_patterns_id.has_value() ||
         !context.inst_blocks().Get(function_info.param_patterns_id).empty() ||
         (return_type_id.has_value() &&
-         return_type_id != context.GetTupleType({}) &&
+         return_type_id != GetTupleType(context, {}) &&
          // TODO: Decide on valid return types for `Main.Run`. Perhaps we should
          // have an interface for this.
          return_type_id != MakeIntType(context, node_id, SemIR::IntKind::Signed,
@@ -548,7 +550,7 @@ static auto IsValidBuiltinDeclaration(Context& context,
   // Get the return type. This is `()` if none was specified.
   auto return_type_id = function.GetDeclaredReturnType(context.sem_ir());
   if (!return_type_id.has_value()) {
-    return_type_id = context.GetTupleType({});
+    return_type_id = GetTupleType(context, {});
   }
 
   return builtin_kind.IsValidType(context.sem_ir(), param_type_ids,

+ 2 - 1
toolchain/check/handle_impl.cpp

@@ -12,6 +12,7 @@
 #include "toolchain/check/modifiers.h"
 #include "toolchain/check/name_lookup.h"
 #include "toolchain/check/pattern_match.h"
+#include "toolchain/check/type.h"
 #include "toolchain/parse/typed_nodes.h"
 #include "toolchain/sem_ir/generic.h"
 #include "toolchain/sem_ir/ids.h"
@@ -307,7 +308,7 @@ static auto BuildImplDecl(Context& context, Parse::AnyImplDeclId node_id,
       context.node_stack().PopExprWithNodeId();
   auto [self_type_node, self_inst_id] =
       context.node_stack().PopWithNodeId<Parse::NodeCategory::ImplAs>();
-  auto self_type_id = context.GetTypeIdForTypeInst(self_inst_id);
+  auto self_type_id = context.types().GetTypeIdForTypeInstId(self_inst_id);
   // Pop the `impl` introducer and any `forall` parameters as a "name".
   auto name = PopImplIntroducerAndParamsAsNameComponent(context, node_id);
   auto decl_block_id = context.inst_block_stack().Pop();

+ 5 - 3
toolchain/check/handle_index.cpp

@@ -11,6 +11,7 @@
 #include "toolchain/check/literal.h"
 #include "toolchain/check/name_lookup.h"
 #include "toolchain/check/operator.h"
+#include "toolchain/check/type.h"
 #include "toolchain/diagnostics/diagnostic.h"
 #include "toolchain/sem_ir/inst.h"
 #include "toolchain/sem_ir/typed_insts.h"
@@ -53,9 +54,10 @@ static auto GetIndexWithArgs(Context& context, Parse::NodeId node_id,
   }
 
   for (const auto& impl : context.impls().array_ref()) {
-    auto impl_self_type_id = context.GetTypeIdForTypeInst(impl.self_id);
+    auto impl_self_type_id =
+        context.types().GetTypeIdForTypeInstId(impl.self_id);
     auto impl_constraint_type_id =
-        context.GetTypeIdForTypeInst(impl.constraint_id);
+        context.types().GetTypeIdForTypeInstId(impl.constraint_id);
 
     if (impl_self_type_id != self_id) {
       continue;
@@ -115,7 +117,7 @@ static auto PerformIndexWith(Context& context, Parse::NodeId node_id,
 
   // The first argument of the `IndexWith` interface corresponds to the
   // `SubscriptType`, so first cast `index_inst_id` to that type.
-  auto subscript_type_id = context.GetTypeIdForTypeInst((*args)[0]);
+  auto subscript_type_id = context.types().GetTypeIdForTypeInstId((*args)[0]);
   auto cast_index_id =
       ConvertToValueOfType(context, node_id, index_inst_id, subscript_type_id);
 

+ 6 - 4
toolchain/check/handle_interface.cpp

@@ -10,6 +10,7 @@
 #include "toolchain/check/modifiers.h"
 #include "toolchain/check/name_component.h"
 #include "toolchain/check/name_lookup.h"
+#include "toolchain/check/type.h"
 #include "toolchain/sem_ir/typed_insts.h"
 
 namespace Carbon::Check {
@@ -116,8 +117,9 @@ static auto BuildInterfaceDecl(Context& context,
     interface_info.generic_id = BuildGenericDecl(context, interface_decl_id);
     interface_decl.interface_id = context.interfaces().Add(interface_info);
     if (interface_info.has_parameters()) {
-      interface_decl.type_id = context.GetGenericInterfaceType(
-          interface_decl.interface_id, context.scope_stack().PeekSpecificId());
+      interface_decl.type_id =
+          GetGenericInterfaceType(context, interface_decl.interface_id,
+                                  context.scope_stack().PeekSpecificId());
     }
   } else {
     FinishGenericRedecl(
@@ -163,8 +165,8 @@ auto HandleParseNode(Context& context,
 
   // Declare and introduce `Self`.
   SemIR::FacetType facet_type =
-      context.FacetTypeFromInterface(interface_id, self_specific_id);
-  SemIR::TypeId self_type_id = context.GetTypeIdForTypeConstant(
+      FacetTypeFromInterface(context, interface_id, self_specific_id);
+  SemIR::TypeId self_type_id = context.types().GetTypeIdForTypeConstantId(
       TryEvalInst(context, SemIR::InstId::None, facet_type));
 
   // We model `Self` as a symbolic binding whose type is the interface.

+ 6 - 5
toolchain/check/handle_literal.cpp

@@ -7,6 +7,7 @@
 #include "toolchain/check/handle.h"
 #include "toolchain/check/literal.h"
 #include "toolchain/check/name_lookup.h"
+#include "toolchain/check/type.h"
 #include "toolchain/diagnostics/format_providers.h"
 #include "toolchain/sem_ir/typed_insts.h"
 
@@ -16,7 +17,7 @@ auto HandleParseNode(Context& context, Parse::BoolLiteralFalseId node_id)
     -> bool {
   context.AddInstAndPush<SemIR::BoolLiteral>(
       node_id,
-      {.type_id = context.GetSingletonType(SemIR::BoolType::SingletonInstId),
+      {.type_id = GetSingletonType(context, SemIR::BoolType::SingletonInstId),
        .value = SemIR::BoolValue::False});
   return true;
 }
@@ -25,7 +26,7 @@ auto HandleParseNode(Context& context, Parse::BoolLiteralTrueId node_id)
     -> bool {
   context.AddInstAndPush<SemIR::BoolLiteral>(
       node_id,
-      {.type_id = context.GetSingletonType(SemIR::BoolType::SingletonInstId),
+      {.type_id = GetSingletonType(context, SemIR::BoolType::SingletonInstId),
        .value = SemIR::BoolValue::True});
   return true;
 }
@@ -76,8 +77,8 @@ auto HandleParseNode(Context& context, Parse::RealLiteralId node_id) -> bool {
 
   auto float_id = context.sem_ir().floats().Add(llvm::APFloat(double_val));
   context.AddInstAndPush<SemIR::FloatLiteral>(
-      node_id, {.type_id = context.GetSingletonType(
-                    SemIR::LegacyFloatType::SingletonInstId),
+      node_id, {.type_id = GetSingletonType(
+                    context, SemIR::LegacyFloatType::SingletonInstId),
                 .float_id = float_id});
   return true;
 }
@@ -85,7 +86,7 @@ auto HandleParseNode(Context& context, Parse::RealLiteralId node_id) -> bool {
 auto HandleParseNode(Context& context, Parse::StringLiteralId node_id) -> bool {
   context.AddInstAndPush<SemIR::StringLiteral>(
       node_id,
-      {.type_id = context.GetSingletonType(SemIR::StringType::SingletonInstId),
+      {.type_id = GetSingletonType(context, SemIR::StringType::SingletonInstId),
        .string_literal_id = context.tokens().GetStringLiteralValue(
            context.parse_tree().node_token(node_id))});
   return true;

+ 3 - 2
toolchain/check/handle_name.cpp

@@ -9,6 +9,7 @@
 #include "toolchain/check/name_component.h"
 #include "toolchain/check/name_lookup.h"
 #include "toolchain/check/pointer_dereference.h"
+#include "toolchain/check/type.h"
 #include "toolchain/lex/token_kind.h"
 #include "toolchain/sem_ir/inst.h"
 #include "toolchain/sem_ir/typed_insts.h"
@@ -238,8 +239,8 @@ auto HandleParseNode(Context& context, Parse::DesignatorExprId node_id)
 
 auto HandleParseNode(Context& context, Parse::PackageExprId node_id) -> bool {
   context.AddInstAndPush<SemIR::NameRef>(
-      node_id, {.type_id = context.GetSingletonType(
-                    SemIR::NamespaceType::SingletonInstId),
+      node_id, {.type_id = GetSingletonType(
+                    context, SemIR::NamespaceType::SingletonInstId),
                 .name_id = SemIR::NameId::PackageNamespace,
                 .value_id = SemIR::Namespace::PackageInstId});
   return true;

+ 2 - 1
toolchain/check/handle_namespace.cpp

@@ -8,6 +8,7 @@
 #include "toolchain/check/modifiers.h"
 #include "toolchain/check/name_component.h"
 #include "toolchain/check/name_lookup.h"
+#include "toolchain/check/type.h"
 #include "toolchain/sem_ir/ids.h"
 #include "toolchain/sem_ir/inst.h"
 #include "toolchain/sem_ir/name_scope.h"
@@ -38,7 +39,7 @@ auto HandleParseNode(Context& context, Parse::NamespaceId node_id) -> bool {
   LimitModifiersOnDecl(context, introducer, KeywordModifierSet::None);
 
   auto namespace_inst = SemIR::Namespace{
-      context.GetSingletonType(SemIR::NamespaceType::SingletonInstId),
+      GetSingletonType(context, SemIR::NamespaceType::SingletonInstId),
       SemIR::NameScopeId::None, SemIR::InstId::None};
   auto namespace_id =
       context.AddPlaceholderInst(SemIR::LocIdAndInst(node_id, namespace_inst));

+ 2 - 1
toolchain/check/handle_operator.cpp

@@ -8,6 +8,7 @@
 #include "toolchain/check/handle.h"
 #include "toolchain/check/operator.h"
 #include "toolchain/check/pointer_dereference.h"
+#include "toolchain/check/type.h"
 #include "toolchain/diagnostics/diagnostic_emitter.h"
 
 namespace Carbon::Check {
@@ -244,7 +245,7 @@ auto HandleParseNode(Context& context, Parse::PrefixOperatorAmpId node_id)
       break;
   }
   context.AddInstAndPush<SemIR::AddrOf>(
-      node_id, SemIR::AddrOf{.type_id = context.GetPointerType(type_id),
+      node_id, SemIR::AddrOf{.type_id = GetPointerType(context, type_id),
                              .lvalue_id = value_id});
   return true;
 }

+ 3 - 2
toolchain/check/handle_struct.cpp

@@ -6,6 +6,7 @@
 #include "toolchain/check/context.h"
 #include "toolchain/check/convert.h"
 #include "toolchain/check/handle.h"
+#include "toolchain/check/type.h"
 #include "toolchain/diagnostics/format_providers.h"
 
 namespace Carbon::Check {
@@ -141,8 +142,8 @@ auto HandleParseNode(Context& context, Parse::StructLiteralId node_id) -> bool {
                              /*is_struct_type_literal=*/false)) {
     context.node_stack().Push(node_id, SemIR::ErrorInst::SingletonInstId);
   } else {
-    auto type_id = context.GetStructType(
-        context.struct_type_fields().AddCanonical(fields));
+    auto type_id = GetStructType(
+        context, context.struct_type_fields().AddCanonical(fields));
 
     auto value_id = context.AddInst<SemIR::StructLiteral>(
         node_id, {.type_id = type_id, .elements_id = elements_id});

+ 2 - 1
toolchain/check/handle_tuple_literal.cpp

@@ -4,6 +4,7 @@
 
 #include "toolchain/check/context.h"
 #include "toolchain/check/handle.h"
+#include "toolchain/check/type.h"
 
 namespace Carbon::Check {
 
@@ -32,7 +33,7 @@ auto HandleParseNode(Context& context, Parse::TupleLiteralId node_id) -> bool {
   for (auto inst : inst_block) {
     type_ids.push_back(context.insts().Get(inst).type_id());
   }
-  auto type_id = context.GetTupleType(type_ids);
+  auto type_id = GetTupleType(context, type_ids);
 
   auto value_id = context.AddInst<SemIR::TupleLiteral>(
       node_id, {.type_id = type_id, .elements_id = refs_id});

+ 2 - 2
toolchain/check/handle_where.cpp

@@ -17,7 +17,7 @@ auto HandleParseNode(Context& context, Parse::WhereOperandId node_id) -> bool {
   auto self_type_id = ExprAsType(context, self_node, self_id).type_id;
   // Only facet types may have `where` restrictions.
   if (self_type_id != SemIR::ErrorInst::SingletonTypeId &&
-      !context.IsFacetType(self_type_id)) {
+      !context.types().IsFacetType(self_type_id)) {
     CARBON_DIAGNOSTIC(WhereOnNonFacetType, Error,
                       "left argument of `where` operator must be a facet type");
     context.emitter().Emit(self_node, WhereOnNonFacetType);
@@ -107,7 +107,7 @@ auto HandleParseNode(Context& context, Parse::RequirementImplsId node_id)
   auto lhs_as_type = ExprAsType(context, lhs_node, lhs_id);
   auto rhs_as_type = ExprAsType(context, rhs_node, rhs_id);
   if (rhs_as_type.type_id != SemIR::ErrorInst::SingletonTypeId &&
-      !context.IsFacetType(rhs_as_type.type_id)) {
+      !context.types().IsFacetType(rhs_as_type.type_id)) {
     CARBON_DIAGNOSTIC(
         ImplsOnNonFacetType, Error,
         "right argument of `impls` requirement must be a facet type");

+ 13 - 7
toolchain/check/impl.cpp

@@ -13,6 +13,7 @@
 #include "toolchain/check/import_ref.h"
 #include "toolchain/check/interface.h"
 #include "toolchain/check/name_lookup.h"
+#include "toolchain/check/type.h"
 #include "toolchain/check/type_completion.h"
 #include "toolchain/diagnostics/diagnostic_emitter.h"
 #include "toolchain/sem_ir/generic.h"
@@ -85,7 +86,8 @@ auto ImplWitnessForDeclaration(Context& context, const SemIR::Impl& impl)
     -> SemIR::InstId {
   CARBON_CHECK(!impl.has_definition_started());
 
-  auto facet_type_id = context.GetTypeIdForTypeInst(impl.constraint_id);
+  auto facet_type_id =
+      context.types().GetTypeIdForTypeInstId(impl.constraint_id);
   if (facet_type_id == SemIR::ErrorInst::SingletonTypeId) {
     return SemIR::ErrorInst::SingletonInstId;
   }
@@ -133,7 +135,8 @@ auto ImplWitnessForDeclaration(Context& context, const SemIR::Impl& impl)
   auto table_id = context.inst_blocks().Add(table);
   return context.AddInst<SemIR::ImplWitness>(
       context.insts().GetLocId(impl.latest_decl_id()),
-      {.type_id = context.GetSingletonType(SemIR::WitnessType::SingletonInstId),
+      {.type_id =
+           GetSingletonType(context, SemIR::WitnessType::SingletonInstId),
        .elements_id = table_id,
        .specific_id = context.generics().GetSelfSpecific(impl.generic_id)});
 }
@@ -162,7 +165,8 @@ auto AddConstantsToImplWitnessFromConstraint(Context& context,
   if (witness_id == SemIR::ErrorInst::SingletonInstId) {
     return;
   }
-  auto facet_type_id = context.GetTypeIdForTypeInst(impl.constraint_id);
+  auto facet_type_id =
+      context.types().GetTypeIdForTypeInstId(impl.constraint_id);
   CARBON_CHECK(facet_type_id != SemIR::ErrorInst::SingletonTypeId);
   auto facet_type = context.types().GetAs<SemIR::FacetType>(facet_type_id);
   const SemIR::FacetTypeInfo& facet_type_info =
@@ -239,7 +243,7 @@ auto AddConstantsToImplWitnessFromConstraint(Context& context,
         // value for `Self`.
         assoc_const_type_id = GetTypeForSpecificAssociatedEntity(
             context, impl.constraint_id, interface_type->specific_id, decl_id,
-            context.GetTypeIdForTypeInst(impl.self_id), witness_id);
+            context.types().GetTypeIdForTypeInstId(impl.self_id), witness_id);
 
         // Perform the conversion of the value to the type. We skipped this when
         // forming the facet type because the type of the associated constant
@@ -283,7 +287,8 @@ auto ImplWitnessStartDefinition(Context& context, SemIR::Impl& impl) -> void {
     return;
   }
 
-  auto facet_type_id = context.GetTypeIdForTypeInst(impl.constraint_id);
+  auto facet_type_id =
+      context.types().GetTypeIdForTypeInstId(impl.constraint_id);
   CARBON_CHECK(facet_type_id != SemIR::ErrorInst::SingletonTypeId);
   auto facet_type = context.types().GetAs<SemIR::FacetType>(facet_type_id);
   const SemIR::FacetTypeInfo& facet_type_info =
@@ -340,7 +345,8 @@ auto FinishImplWitness(Context& context, SemIR::Impl& impl) -> void {
     return;
   }
 
-  auto facet_type_id = context.GetTypeIdForTypeInst(impl.constraint_id);
+  auto facet_type_id =
+      context.types().GetTypeIdForTypeInstId(impl.constraint_id);
   CARBON_CHECK(facet_type_id != SemIR::ErrorInst::SingletonTypeId);
   auto facet_type = context.types().GetAs<SemIR::FacetType>(facet_type_id);
   const SemIR::FacetTypeInfo& facet_type_info =
@@ -354,7 +360,7 @@ auto FinishImplWitness(Context& context, SemIR::Impl& impl) -> void {
   auto witness = context.insts().GetAs<SemIR::ImplWitness>(impl.witness_id);
   auto witness_block = context.inst_blocks().GetMutable(witness.elements_id);
   auto& impl_scope = context.name_scopes().Get(impl.scope_id);
-  auto self_type_id = context.GetTypeIdForTypeInst(impl.self_id);
+  auto self_type_id = context.types().GetTypeIdForTypeInstId(impl.self_id);
   auto assoc_entities =
       context.inst_blocks().Get(interface.associated_entities_id);
   llvm::SmallVector<SemIR::InstId> used_decl_ids;

+ 2 - 1
toolchain/check/import.cpp

@@ -11,6 +11,7 @@
 #include "toolchain/check/import_ref.h"
 #include "toolchain/check/merge.h"
 #include "toolchain/check/name_lookup.h"
+#include "toolchain/check/type.h"
 #include "toolchain/parse/node_ids.h"
 #include "toolchain/sem_ir/file.h"
 #include "toolchain/sem_ir/ids.h"
@@ -508,7 +509,7 @@ static auto AddNamespaceFromOtherPackage(Context& context,
                                          SemIR::NameId name_id)
     -> SemIR::InstId {
   auto namespace_type_id =
-      context.GetSingletonType(SemIR::NamespaceType::SingletonInstId);
+      GetSingletonType(context, SemIR::NamespaceType::SingletonInstId);
   AddImportNamespaceResult result = CopySingleNameScopeFromImportIR(
       context, namespace_type_id, /*copied_namespaces=*/nullptr, import_ir_id,
       import_inst_id, import_ns.name_scope_id, parent_scope_id, name_id);

+ 2 - 1
toolchain/check/import_cpp.cpp

@@ -17,6 +17,7 @@
 #include "toolchain/check/context.h"
 #include "toolchain/check/diagnostic_helpers.h"
 #include "toolchain/check/import.h"
+#include "toolchain/check/type.h"
 #include "toolchain/diagnostics/diagnostic.h"
 #include "toolchain/diagnostics/format_providers.h"
 #include "toolchain/sem_ir/name_scope.h"
@@ -99,7 +100,7 @@ static auto AddNamespace(Context& context, PackageNameId cpp_package_id,
 
   return AddImportNamespace(
              context,
-             context.GetSingletonType(SemIR::NamespaceType::SingletonInstId),
+             GetSingletonType(context, SemIR::NamespaceType::SingletonInstId),
              SemIR::NameId::ForPackageName(cpp_package_id),
              SemIR::NameScopeId::Package,
              /*diagnose_duplicate_namespace=*/false,

+ 96 - 66
toolchain/check/import_ref.cpp

@@ -10,6 +10,7 @@
 #include "toolchain/check/eval.h"
 #include "toolchain/check/generic.h"
 #include "toolchain/check/name_lookup.h"
+#include "toolchain/check/type.h"
 #include "toolchain/parse/node_ids.h"
 #include "toolchain/sem_ir/constant.h"
 #include "toolchain/sem_ir/file.h"
@@ -520,9 +521,9 @@ class ImportRefResolver : public ImportContext {
             import_ir().constant_values().GetInstId(import_type_const_id);
         SemIR::IsSingletonInstId(import_type_inst_id)) {
       // Builtins don't require constant resolution; we can use them directly.
-      return local_context().GetSingletonType(import_type_inst_id);
+      return GetSingletonType(local_context(), import_type_inst_id);
     } else {
-      return local_context().GetTypeIdForTypeConstant(
+      return local_context().types().GetTypeIdForTypeConstantId(
           ResolveConstant(import_type_id.AsConstantId()));
     }
   }
@@ -1023,7 +1024,7 @@ static auto GetLocalParamPatternsId(ImportContext& context,
     auto entity_name =
         context.import_entity_names().Get(binding.entity_name_id);
     auto name_id = GetLocalNameId(context, entity_name.name_id);
-    auto type_id = context.local_context().GetTypeIdForTypeConstant(
+    auto type_id = context.local_context().types().GetTypeIdForTypeConstantId(
         GetLocalConstantIdChecked(context, binding.type_id));
 
     auto new_param_id = SemIR::InstId::None;
@@ -1062,7 +1063,7 @@ static auto GetLocalParamPatternsId(ImportContext& context,
                  .subpattern_id = new_param_id,
                  .runtime_index = param_pattern.runtime_index}));
     if (addr_inst) {
-      type_id = context.local_context().GetTypeIdForTypeConstant(
+      type_id = context.local_context().types().GetTypeIdForTypeConstantId(
           GetLocalConstantIdChecked(context, addr_inst->type_id));
       new_param_id = context.local_context().AddInstInNoBlock(
           context.local_context().MakeImportedLocAndInst<SemIR::AddrPattern>(
@@ -1093,7 +1094,7 @@ static auto GetLocalReturnSlotPatternId(
   auto return_slot_pattern =
       context.import_insts().GetAs<SemIR::ReturnSlotPattern>(
           param_pattern.subpattern_id);
-  auto type_id = context.local_context().GetTypeIdForTypeConstant(
+  auto type_id = context.local_context().types().GetTypeIdForTypeConstantId(
       GetLocalConstantIdChecked(context, return_slot_pattern.type_id));
 
   auto new_return_slot_pattern_id = context.local_context().AddInstInNoBlock(
@@ -1357,7 +1358,8 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
   }
 
   auto element_type_id =
-      resolver.local_context().GetTypeIdForTypeConstant(element_type_const_id);
+      resolver.local_context().types().GetTypeIdForTypeConstantId(
+          element_type_const_id);
   return ResolveAs<SemIR::ArrayType>(
       resolver, {.type_id = SemIR::TypeType::SingletonTypeId,
                  .bound_id = bound_id,
@@ -1412,8 +1414,8 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
     }
 
     // In the second phase, create the associated constant and its declaration.
-    auto type_id =
-        resolver.local_context().GetTypeIdForTypeConstant(type_const_id);
+    auto type_id = resolver.local_context().types().GetTypeIdForTypeConstantId(
+        type_const_id);
     std::tie(assoc_const_id, const_id) =
         MakeAssociatedConstant(resolver, import_assoc_const, type_id);
   } else {
@@ -1456,10 +1458,11 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
   auto decl_id = AddImportRef(resolver, inst.decl_id);
 
   return ResolveAs<SemIR::AssociatedEntity>(
-      resolver, {.type_id = resolver.local_context().GetTypeIdForTypeConstant(
-                     type_const_id),
-                 .index = inst.index,
-                 .decl_id = decl_id});
+      resolver,
+      {.type_id = resolver.local_context().types().GetTypeIdForTypeConstantId(
+           type_const_id),
+       .index = inst.index,
+       .decl_id = decl_id});
 }
 
 static auto TryResolveTypedInst(ImportRefResolver& resolver,
@@ -1475,8 +1478,9 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
   return ResolveAs<SemIR::AssociatedEntityType>(
       resolver,
       {.type_id = SemIR::TypeType::SingletonTypeId,
-       .interface_type_id = resolver.local_context().GetTypeIdForTypeConstant(
-           interface_inst_id)});
+       .interface_type_id =
+           resolver.local_context().types().GetTypeIdForTypeConstantId(
+               interface_inst_id)});
 }
 
 static auto TryResolveTypedInst(ImportRefResolver& resolver,
@@ -1498,7 +1502,8 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
       resolver.local_context().MakeImportedLocAndInst<SemIR::BaseDecl>(
           AddImportIRInst(resolver, import_inst_id),
           {.type_id =
-               resolver.local_context().GetTypeIdForTypeConstant(type_const_id),
+               resolver.local_context().types().GetTypeIdForTypeConstantId(
+                   type_const_id),
            .base_type_inst_id = base_type_inst_id,
            .index = inst.index}));
   return ResolveResult::Done(resolver.local_constant_values().Get(inst_id),
@@ -1517,9 +1522,10 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::Vtable inst)
   auto virtual_functions_id = GetLocalCanonicalInstBlockId(
       resolver, inst.virtual_functions_id, virtual_functions);
   return ResolveAs<SemIR::Vtable>(
-      resolver, {.type_id = resolver.local_context().GetTypeIdForTypeConstant(
-                     type_const_id),
-                 .virtual_functions_id = virtual_functions_id});
+      resolver,
+      {.type_id = resolver.local_context().types().GetTypeIdForTypeConstantId(
+           type_const_id),
+       .virtual_functions_id = virtual_functions_id});
 }
 
 static auto TryResolveTypedInst(ImportRefResolver& resolver,
@@ -1545,7 +1551,8 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
       import_entity_name.is_template);
   return ResolveAs<SemIR::BindSymbolicName>(
       resolver,
-      {.type_id = resolver.local_context().GetTypeIdForTypeConstant(type_id),
+      {.type_id =
+           resolver.local_context().types().GetTypeIdForTypeConstantId(type_id),
        .entity_name_id = entity_name_id,
        .value_id = SemIR::InstId::None});
 }
@@ -1568,7 +1575,8 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
       import_entity_name.is_template);
   return ResolveAs<SemIR::SymbolicBindingPattern>(
       resolver,
-      {.type_id = resolver.local_context().GetTypeIdForTypeConstant(type_id),
+      {.type_id =
+           resolver.local_context().types().GetTypeIdForTypeConstantId(type_id),
        .entity_name_id = entity_name_id});
 }
 
@@ -1585,10 +1593,11 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
   }
 
   return ResolveAs<SemIR::BoundMethod>(
-      resolver, {.type_id = resolver.local_context().GetSingletonType(
-                     SemIR::BoundMethodType::SingletonInstId),
-                 .object_id = object_id,
-                 .function_decl_id = function_decl_id});
+      resolver,
+      {.type_id = GetSingletonType(resolver.local_context(),
+                                   SemIR::BoundMethodType::SingletonInstId),
+       .object_id = object_id,
+       .function_decl_id = function_decl_id});
 }
 
 static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::Call inst)
@@ -1603,7 +1612,8 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::Call inst)
 
   return ResolveAs<SemIR::Call>(
       resolver,
-      {.type_id = resolver.local_context().GetTypeIdForTypeConstant(type_id),
+      {.type_id =
+           resolver.local_context().types().GetTypeIdForTypeConstantId(type_id),
        .callee_id = callee_id,
        .args_id = GetLocalCanonicalInstBlockId(resolver, inst.args_id, args)});
 }
@@ -1630,8 +1640,8 @@ static auto MakeIncompleteClass(ImportContext& context,
         .is_dynamic = import_class.is_dynamic}});
 
   if (import_class.has_parameters()) {
-    class_decl.type_id = context.local_context().GetGenericClassType(
-        class_decl.class_id, enclosing_specific_id);
+    class_decl.type_id = GetGenericClassType(
+        context.local_context(), class_decl.class_id, enclosing_specific_id);
   }
 
   // Write the class ID into the ClassDecl.
@@ -1756,13 +1766,14 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
   SetGenericData(resolver, import_class.generic_id, new_class.generic_id,
                  generic_data);
   new_class.self_type_id =
-      resolver.local_context().GetTypeIdForTypeConstant(self_const_id);
+      resolver.local_context().types().GetTypeIdForTypeConstantId(
+          self_const_id);
 
   if (import_class.is_defined()) {
     auto complete_type_witness_id = AddLoadedImportRef(
         resolver,
-        resolver.local_context().GetSingletonType(
-            SemIR::WitnessType::SingletonInstId),
+        GetSingletonType(resolver.local_context(),
+                         SemIR::WitnessType::SingletonInstId),
         import_class.complete_type_witness_id, complete_type_witness_const_id);
     AddClassDefinition(resolver, import_class, new_class,
                        complete_type_witness_id, base_id, adapt_id, vtable_id);
@@ -1812,11 +1823,13 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
     return ResolveResult::Retry();
   }
   auto object_repr_id =
-      resolver.local_context().GetTypeIdForTypeConstant(object_repr_const_id);
+      resolver.local_context().types().GetTypeIdForTypeConstantId(
+          object_repr_const_id);
   return ResolveAs<SemIR::CompleteTypeWitness>(
-      resolver, {.type_id = resolver.local_context().GetSingletonType(
-                     SemIR::WitnessType::SingletonInstId),
-                 .object_repr_id = object_repr_id});
+      resolver,
+      {.type_id = GetSingletonType(resolver.local_context(),
+                                   SemIR::WitnessType::SingletonInstId),
+       .object_repr_id = object_repr_id});
 }
 
 static auto TryResolveTypedInst(ImportRefResolver& resolver,
@@ -1827,7 +1840,8 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
     return ResolveResult::Retry();
   }
   auto inner_type_id =
-      resolver.local_context().GetTypeIdForTypeConstant(inner_const_id);
+      resolver.local_context().types().GetTypeIdForTypeConstantId(
+          inner_const_id);
   return ResolveAs<SemIR::ConstType>(
       resolver,
       {.type_id = SemIR::TypeType::SingletonTypeId, .inner_id = inner_type_id});
@@ -1850,7 +1864,8 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
       resolver.local_context().MakeImportedLocAndInst<SemIR::FieldDecl>(
           AddImportIRInst(resolver, import_inst_id),
           {.type_id =
-               resolver.local_context().GetTypeIdForTypeConstant(const_id),
+               resolver.local_context().types().GetTypeIdForTypeConstantId(
+                   const_id),
            .name_id = GetLocalNameId(resolver, inst.name_id),
            .index = inst.index}));
   return ResolveResult::Done(resolver.local_constant_values().Get(inst_id),
@@ -1878,8 +1893,8 @@ static auto MakeFunctionDecl(ImportContext& context,
        {.return_slot_pattern_id = SemIR::InstId::None,
         .builtin_function_kind = import_function.builtin_function_kind}});
 
-  function_decl.type_id = context.local_context().GetFunctionType(
-      function_decl.function_id, specific_id);
+  function_decl.type_id = GetFunctionType(
+      context.local_context(), function_decl.function_id, specific_id);
 
   // Write the function ID and type into the FunctionDecl.
   context.local_context().ReplaceInstBeforeConstantUse(function_decl_id,
@@ -2199,8 +2214,9 @@ static auto MakeInterfaceDecl(ImportContext& context,
        {}});
 
   if (import_interface.has_parameters()) {
-    interface_decl.type_id = context.local_context().GetGenericInterfaceType(
-        interface_decl.interface_id, enclosing_specific_id);
+    interface_decl.type_id = GetGenericInterfaceType(
+        context.local_context(), interface_decl.interface_id,
+        enclosing_specific_id);
   }
 
   // Write the interface ID into the InterfaceDecl.
@@ -2341,9 +2357,10 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
   }
 
   return ResolveAs<SemIR::FacetAccessWitness>(
-      resolver, {.type_id = resolver.local_context().GetSingletonType(
-                     SemIR::WitnessType::SingletonInstId),
-                 .facet_value_inst_id = facet_value_inst_id});
+      resolver,
+      {.type_id = GetSingletonType(resolver.local_context(),
+                                   SemIR::WitnessType::SingletonInstId),
+       .facet_value_inst_id = facet_value_inst_id});
 }
 
 static auto TryResolveTypedInst(ImportRefResolver& resolver,
@@ -2423,7 +2440,8 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
 
   return ResolveAs<SemIR::FacetValue>(
       resolver,
-      {.type_id = resolver.local_context().GetTypeIdForTypeConstant(type_id),
+      {.type_id =
+           resolver.local_context().types().GetTypeIdForTypeConstantId(type_id),
        .type_inst_id = type_inst_id,
        .witness_inst_id = witness_inst_id});
 }
@@ -2439,10 +2457,11 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
   auto specific_id =
       GetOrAddLocalSpecific(resolver, inst.specific_id, specific_data);
   return ResolveAs<SemIR::ImplWitness>(
-      resolver, {.type_id = resolver.local_context().GetSingletonType(
-                     SemIR::WitnessType::SingletonInstId),
-                 .elements_id = elements_id,
-                 .specific_id = specific_id});
+      resolver,
+      {.type_id = GetSingletonType(resolver.local_context(),
+                                   SemIR::WitnessType::SingletonInstId),
+       .elements_id = elements_id,
+       .specific_id = specific_id});
 }
 
 static auto TryResolveTypedInst(ImportRefResolver& resolver,
@@ -2456,7 +2475,8 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
 
   return ResolveAs<SemIR::ImplWitnessAccess>(
       resolver,
-      {.type_id = resolver.local_context().GetTypeIdForTypeConstant(type_id),
+      {.type_id =
+           resolver.local_context().types().GetTypeIdForTypeConstantId(type_id),
        .witness_id = witness_id,
        .index = inst.index});
 }
@@ -2477,7 +2497,8 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
 
   return ResolveAs<SemIR::IntValue>(
       resolver,
-      {.type_id = resolver.local_context().GetTypeIdForTypeConstant(type_id),
+      {.type_id =
+           resolver.local_context().types().GetTypeIdForTypeConstantId(type_id),
        .int_id = int_id});
 }
 
@@ -2511,8 +2532,8 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
     return ResolveResult::Retry();
   }
 
-  auto namespace_type_id = resolver.local_context().GetSingletonType(
-      SemIR::NamespaceType::SingletonInstId);
+  auto namespace_type_id = GetSingletonType(
+      resolver.local_context(), SemIR::NamespaceType::SingletonInstId);
   auto namespace_decl =
       SemIR::Namespace{.type_id = namespace_type_id,
                        .name_scope_id = SemIR::NameScopeId::None,
@@ -2543,7 +2564,8 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
   }
 
   auto pointee_type_id =
-      resolver.local_context().GetTypeIdForTypeConstant(pointee_const_id);
+      resolver.local_context().types().GetTypeIdForTypeConstantId(
+          pointee_const_id);
   return ResolveAs<SemIR::PointerType>(
       resolver, {.type_id = SemIR::TypeType::SingletonTypeId,
                  .pointee_id = pointee_type_id});
@@ -2562,11 +2584,13 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
   }
 
   auto complete_type_id =
-      resolver.local_context().GetTypeIdForTypeConstant(complete_type_const_id);
+      resolver.local_context().types().GetTypeIdForTypeConstantId(
+          complete_type_const_id);
   return ResolveAs<SemIR::RequireCompleteType>(
-      resolver, {.type_id = resolver.local_context().GetSingletonType(
-                     SemIR::WitnessType::SingletonInstId),
-                 .complete_type_id = complete_type_id});
+      resolver,
+      {.type_id = GetSingletonType(resolver.local_context(),
+                                   SemIR::WitnessType::SingletonInstId),
+       .complete_type_id = complete_type_id});
 }
 
 static auto TryResolveTypedInst(ImportRefResolver& resolver,
@@ -2578,8 +2602,8 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
     return ResolveResult::Retry();
   }
 
-  auto type_id =
-      resolver.local_context().GetTypeIdForTypeConstant(type_const_id);
+  auto type_id = resolver.local_context().types().GetTypeIdForTypeConstantId(
+      type_const_id);
   auto specific_id =
       GetOrAddLocalSpecific(resolver, inst.specific_id, specific_data);
   return ResolveAs<SemIR::SpecificFunction>(
@@ -2607,7 +2631,8 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
        llvm::zip(orig_fields, field_const_ids)) {
     auto name_id = GetLocalNameId(resolver, orig_field.name_id);
     auto field_type_id =
-        resolver.local_context().GetTypeIdForTypeConstant(field_const_id);
+        resolver.local_context().types().GetTypeIdForTypeConstantId(
+            field_const_id);
     new_fields.push_back({.name_id = name_id, .type_id = field_type_id});
   }
 
@@ -2627,7 +2652,8 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
 
   return ResolveAs<SemIR::StructValue>(
       resolver,
-      {.type_id = resolver.local_context().GetTypeIdForTypeConstant(type_id),
+      {.type_id =
+           resolver.local_context().types().GetTypeIdForTypeConstantId(type_id),
        .elements_id =
            GetLocalCanonicalInstBlockId(resolver, inst.elements_id, elems)});
 }
@@ -2651,11 +2677,12 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
   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().GetTypeIdForTypeConstant(elem_const_id));
+        resolver.local_context().types().GetTypeIdForTypeConstantId(
+            elem_const_id));
   }
 
   return ResolveResult::Done(resolver.local_types().GetConstantId(
-      resolver.local_context().GetTupleType(elem_type_ids)));
+      GetTupleType(resolver.local_context(), elem_type_ids)));
 }
 
 static auto TryResolveTypedInst(ImportRefResolver& resolver,
@@ -2668,7 +2695,8 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
 
   return ResolveAs<SemIR::TupleValue>(
       resolver,
-      {.type_id = resolver.local_context().GetTypeIdForTypeConstant(type_id),
+      {.type_id =
+           resolver.local_context().types().GetTypeIdForTypeConstantId(type_id),
        .elements_id =
            GetLocalCanonicalInstBlockId(resolver, inst.elements_id, elems)});
 }
@@ -2687,9 +2715,11 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
       resolver,
       {.type_id = SemIR::TypeType::SingletonTypeId,
        .class_type_id =
-           resolver.local_context().GetTypeIdForTypeConstant(class_const_id),
+           resolver.local_context().types().GetTypeIdForTypeConstantId(
+               class_const_id),
        .element_type_id =
-           resolver.local_context().GetTypeIdForTypeConstant(elem_const_id)});
+           resolver.local_context().types().GetTypeIdForTypeConstantId(
+               elem_const_id)});
 }
 
 // Tries to resolve the InstId, returning a canonical constant when ready, or

+ 5 - 3
toolchain/check/interface.cpp

@@ -7,6 +7,7 @@
 #include "toolchain/check/context.h"
 #include "toolchain/check/eval.h"
 #include "toolchain/check/generic.h"
+#include "toolchain/check/type.h"
 #include "toolchain/sem_ir/ids.h"
 #include "toolchain/sem_ir/inst.h"
 #include "toolchain/sem_ir/typed_insts.h"
@@ -34,7 +35,7 @@ auto BuildAssociatedEntity(Context& context, SemIR::InterfaceId interface_id,
 
   // Name lookup for the declaration's name should name the associated entity,
   // not the declaration itself.
-  auto type_id = context.GetAssociatedEntityType(self_type_id);
+  auto type_id = GetAssociatedEntityType(context, self_type_id);
   return context.AddInst<SemIR::AssociatedEntity>(
       context.insts().GetLocId(decl_id),
       {.type_id = type_id, .index = index, .decl_id = decl_id});
@@ -173,8 +174,9 @@ auto GetTypeForSpecificAssociatedEntity(Context& context, SemIRLoc loc,
         GetSelfFacet(context, interface_specific_id,
                      context.functions().Get(fn->function_id).generic_id,
                      self_type_id, self_witness_id);
-    return context.GetFunctionTypeWithSelfType(
-        context.types().GetInstId(interface_fn_type_id), self_facet_id);
+    return GetFunctionTypeWithSelfType(
+        context, context.types().GetInstId(interface_fn_type_id),
+        self_facet_id);
   } else {
     CARBON_FATAL("Unexpected kind for associated constant {0}", decl);
   }

+ 3 - 2
toolchain/check/literal.cpp

@@ -8,14 +8,15 @@
 #include "toolchain/check/context.h"
 #include "toolchain/check/convert.h"
 #include "toolchain/check/name_lookup.h"
+#include "toolchain/check/type.h"
 
 namespace Carbon::Check {
 
 auto MakeIntLiteral(Context& context, Parse::NodeId node_id, IntId int_id)
     -> SemIR::InstId {
   return context.AddInst<SemIR::IntValue>(
-      node_id, {.type_id = context.GetSingletonType(
-                    SemIR::IntLiteralType::SingletonInstId),
+      node_id, {.type_id = GetSingletonType(
+                    context, SemIR::IntLiteralType::SingletonInstId),
                 .int_id = int_id});
 }
 

+ 11 - 10
toolchain/check/member_access.cpp

@@ -14,6 +14,7 @@
 #include "toolchain/check/import_ref.h"
 #include "toolchain/check/interface.h"
 #include "toolchain/check/name_lookup.h"
+#include "toolchain/check/type.h"
 #include "toolchain/check/type_completion.h"
 #include "toolchain/diagnostics/diagnostic_emitter.h"
 #include "toolchain/sem_ir/function.h"
@@ -194,13 +195,13 @@ static auto PerformImplLookup(
     return SemIR::ErrorInst::SingletonInstId;
   }
 
-  auto self_type_id = context.GetTypeIdForTypeConstant(type_const_id);
+  auto self_type_id = context.types().GetTypeIdForTypeConstantId(type_const_id);
   auto witness_id =
       LookupImplWitness(context, loc_id, type_const_id,
                         assoc_type.interface_type_id.AsConstantId());
   if (!witness_id.has_value()) {
-    auto interface_type_id = context.GetInterfaceType(
-        interface_type->interface_id, interface_type->specific_id);
+    auto interface_type_id = GetInterfaceType(
+        context, interface_type->interface_id, interface_type->specific_id);
     if (missing_impl_diagnoser) {
       // TODO: Pass in the expression whose type we are printing.
       CARBON_DIAGNOSTIC(MissingImplInMemberAccessNote, Note,
@@ -286,7 +287,7 @@ static auto LookupMemberNameInScope(Context& context, SemIR::LocId loc_id,
     if (lookup_in_type_of_base) {
       SemIR::TypeId base_type_id = context.insts().Get(base_id).type_id();
       if (base_type_id != SemIR::TypeType::SingletonTypeId &&
-          context.IsFacetType(base_type_id)) {
+          context.types().IsFacetType(base_type_id)) {
         // Handles `T.F` when `T` is a non-type facet.
         auto base_as_type = ExprAsType(context, loc_id, base_id);
 
@@ -308,8 +309,8 @@ static auto LookupMemberNameInScope(Context& context, SemIR::LocId loc_id,
           // Get the witness that `T` implements `base_type_id`.
           if (base_interface == *assoc_interface) {
             witness_inst_id = context.GetOrAddInst<SemIR::FacetAccessWitness>(
-                loc_id, {.type_id = context.GetSingletonType(
-                             SemIR::WitnessType::SingletonInstId),
+                loc_id, {.type_id = GetSingletonType(
+                             context, SemIR::WitnessType::SingletonInstId),
                          .facet_value_inst_id = base_id});
             // TODO: Result will eventually be a facet type witness instead of
             // an interface witness. Will need to use the index
@@ -366,8 +367,8 @@ static auto PerformInstanceBinding(Context& context, SemIR::LocId loc_id,
     }
 
     return context.GetOrAddInst<SemIR::BoundMethod>(
-        loc_id, {.type_id = context.GetSingletonType(
-                     SemIR::BoundMethodType::SingletonInstId),
+        loc_id, {.type_id = GetSingletonType(
+                     context, SemIR::BoundMethodType::SingletonInstId),
                  .object_id = base_id,
                  .function_decl_id = member_id});
   }
@@ -508,7 +509,7 @@ auto PerformMemberAccess(Context& context, SemIR::LocId loc_id,
   // TODO: According to the design, this should be a "lookup in base" lookup,
   // not a "lookup in type of base" lookup, and the facet itself should have
   // member names that directly name members of the `impl`.
-  if (context.IsFacetType(base_type_id)) {
+  if (context.types().IsFacetType(base_type_id)) {
     return member_id;
   }
 
@@ -591,7 +592,7 @@ auto PerformTupleAccess(Context& context, SemIR::LocId loc_id,
   auto index_node_id = context.insts().GetLocId(index_inst_id);
   index_inst_id = ConvertToValueOfType(
       context, index_node_id, index_inst_id,
-      context.GetSingletonType(SemIR::IntLiteralType::SingletonInstId));
+      GetSingletonType(context, SemIR::IntLiteralType::SingletonInstId));
   auto index_const_id = context.constant_values().Get(index_inst_id);
   if (index_const_id == SemIR::ErrorInst::SingletonConstantId) {
     return SemIR::ErrorInst::SingletonInstId;

+ 4 - 2
toolchain/check/name_lookup.cpp

@@ -240,7 +240,8 @@ auto AppendLookupScopesForConstant(Context& context, SemIR::LocId loc_id,
   }
   if (auto base_as_class = base.TryAs<SemIR::ClassType>()) {
     RequireDefinedType(
-        context, context.GetTypeIdForTypeConstant(base_const_id), loc_id, [&] {
+        context, context.types().GetTypeIdForTypeConstantId(base_const_id),
+        loc_id, [&] {
           CARBON_DIAGNOSTIC(QualifiedExprInIncompleteClassScope, Error,
                             "member access into incomplete class {0}",
                             InstIdAsType);
@@ -254,7 +255,8 @@ auto AppendLookupScopesForConstant(Context& context, SemIR::LocId loc_id,
   }
   if (auto base_as_facet_type = base.TryAs<SemIR::FacetType>()) {
     RequireDefinedType(
-        context, context.GetTypeIdForTypeConstant(base_const_id), loc_id, [&] {
+        context, context.types().GetTypeIdForTypeConstantId(base_const_id),
+        loc_id, [&] {
           CARBON_DIAGNOSTIC(QualifiedExprInUndefinedInterfaceScope, Error,
                             "member access into undefined interface {0}",
                             InstIdAsType);

+ 2 - 1
toolchain/check/pattern_match.cpp

@@ -13,6 +13,7 @@
 #include "toolchain/check/context.h"
 #include "toolchain/check/convert.h"
 #include "toolchain/check/subpattern.h"
+#include "toolchain/check/type.h"
 
 namespace Carbon::Check {
 
@@ -252,7 +253,7 @@ auto MatchContext::EmitPatternMatch(Context& context,
       auto scrutinee_ref = context.insts().Get(scrutinee_ref_id);
       auto new_scrutinee = context.AddInst<SemIR::AddrOf>(
           context.insts().GetLocId(scrutinee_ref_id),
-          {.type_id = context.GetPointerType(scrutinee_ref.type_id()),
+          {.type_id = GetPointerType(context, scrutinee_ref.type_id()),
            .lvalue_id = scrutinee_ref_id});
       AddWork(
           {.pattern_id = addr_pattern.inner_id, .scrutinee_id = new_scrutinee});

+ 6 - 5
toolchain/check/subst.cpp

@@ -172,7 +172,7 @@ static auto PopOperand(Context& context, Worklist& worklist, SemIR::IdKind kind,
       if (!type_id.has_value()) {
         return arg;
       }
-      return context.GetTypeIdForTypeInst(worklist.Pop()).index;
+      return context.types().GetTypeIdForTypeInstId(worklist.Pop()).index;
     }
     case SemIR::IdKind::For<SemIR::InstBlockId>: {
       return pop_block_id(SemIR::InstBlockId(arg)).index;
@@ -183,9 +183,9 @@ static auto PopOperand(Context& context, Worklist& worklist, SemIR::IdKind kind,
       SemIR::CopyOnWriteStructTypeFieldsBlock new_fields(context.sem_ir(),
                                                          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.GetTypeIdForTypeInst(worklist.Pop())});
+        new_fields.Set(i, {.name_id = old_fields[i].name_id,
+                           .type_id = context.types().GetTypeIdForTypeInstId(
+                               worklist.Pop())});
       }
       return new_fields.GetCanonical().index;
     }
@@ -195,7 +195,8 @@ static auto PopOperand(Context& context, Worklist& worklist, SemIR::IdKind kind,
       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.GetTypeIdForTypeInst(worklist.Pop()));
+        new_type_block.Set(
+            i, context.types().GetTypeIdForTypeInstId(worklist.Pop()));
       }
       return new_type_block.GetCanonical().index;
     }

+ 116 - 0
toolchain/check/type.cpp

@@ -0,0 +1,116 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#include "toolchain/check/type.h"
+
+#include "toolchain/check/eval.h"
+#include "toolchain/check/type_completion.h"
+
+namespace Carbon::Check {
+
+// Gets or forms a type_id for a type, given the instruction kind and arguments.
+template <typename InstT, typename... EachArgT>
+static auto GetTypeImpl(Context& context, EachArgT... each_arg)
+    -> SemIR::TypeId {
+  // TODO: Remove inst_id parameter from TryEvalInst.
+  InstT inst = {SemIR::TypeType::SingletonTypeId, each_arg...};
+  return context.types().GetTypeIdForTypeConstantId(
+      TryEvalInst(context, SemIR::InstId::None, inst));
+}
+
+auto FacetTypeFromInterface(Context& context, SemIR::InterfaceId interface_id,
+                            SemIR::SpecificId specific_id) -> SemIR::FacetType {
+  SemIR::FacetTypeId facet_type_id = context.facet_types().Add(
+      SemIR::FacetTypeInfo{.impls_constraints = {{interface_id, specific_id}},
+                           .other_requirements = false});
+  return {.type_id = SemIR::TypeType::SingletonTypeId,
+          .facet_type_id = facet_type_id};
+}
+
+// Gets or forms a type_id for a type, given the instruction kind and arguments,
+// and completes the type. This should only be used when type completion cannot
+// fail.
+template <typename InstT, typename... EachArgT>
+static auto GetCompleteTypeImpl(Context& context, EachArgT... each_arg)
+    -> SemIR::TypeId {
+  auto type_id = GetTypeImpl<InstT>(context, each_arg...);
+  CompleteTypeOrCheckFail(context, type_id);
+  return type_id;
+}
+
+auto GetStructType(Context& context, SemIR::StructTypeFieldsId fields_id)
+    -> SemIR::TypeId {
+  return GetTypeImpl<SemIR::StructType>(context, fields_id);
+}
+
+auto GetTupleType(Context& context, llvm::ArrayRef<SemIR::TypeId> type_ids)
+    -> SemIR::TypeId {
+  return GetTypeImpl<SemIR::TupleType>(
+      context, context.type_blocks().AddCanonical(type_ids));
+}
+
+auto GetAssociatedEntityType(Context& context, SemIR::TypeId interface_type_id)
+    -> SemIR::TypeId {
+  return GetTypeImpl<SemIR::AssociatedEntityType>(context, interface_type_id);
+}
+
+auto GetSingletonType(Context& context, SemIR::InstId singleton_id)
+    -> SemIR::TypeId {
+  CARBON_CHECK(SemIR::IsSingletonInstId(singleton_id));
+  auto type_id = context.types().GetTypeIdForTypeInstId(singleton_id);
+  // To keep client code simpler, complete builtin types before returning them.
+  CompleteTypeOrCheckFail(context, type_id);
+  return type_id;
+}
+
+auto GetClassType(Context& context, SemIR::ClassId class_id,
+                  SemIR::SpecificId specific_id) -> SemIR::TypeId {
+  return GetTypeImpl<SemIR::ClassType>(context, class_id, specific_id);
+}
+
+auto GetFunctionType(Context& context, SemIR::FunctionId fn_id,
+                     SemIR::SpecificId specific_id) -> SemIR::TypeId {
+  return GetCompleteTypeImpl<SemIR::FunctionType>(context, fn_id, specific_id);
+}
+
+auto GetFunctionTypeWithSelfType(Context& context,
+                                 SemIR::InstId interface_function_type_id,
+                                 SemIR::InstId self_id) -> SemIR::TypeId {
+  return GetCompleteTypeImpl<SemIR::FunctionTypeWithSelfType>(
+      context, interface_function_type_id, self_id);
+}
+
+auto GetGenericClassType(Context& context, SemIR::ClassId class_id,
+                         SemIR::SpecificId enclosing_specific_id)
+    -> SemIR::TypeId {
+  return GetCompleteTypeImpl<SemIR::GenericClassType>(context, class_id,
+                                                      enclosing_specific_id);
+}
+
+auto GetGenericInterfaceType(Context& context, SemIR::InterfaceId interface_id,
+                             SemIR::SpecificId enclosing_specific_id)
+    -> SemIR::TypeId {
+  return GetCompleteTypeImpl<SemIR::GenericInterfaceType>(
+      context, interface_id, enclosing_specific_id);
+}
+
+auto GetInterfaceType(Context& context, SemIR::InterfaceId interface_id,
+                      SemIR::SpecificId specific_id) -> SemIR::TypeId {
+  return GetTypeImpl<SemIR::FacetType>(
+      context,
+      FacetTypeFromInterface(context, interface_id, specific_id).facet_type_id);
+}
+
+auto GetPointerType(Context& context, SemIR::TypeId pointee_type_id)
+    -> SemIR::TypeId {
+  return GetTypeImpl<SemIR::PointerType>(context, pointee_type_id);
+}
+
+auto GetUnboundElementType(Context& context, SemIR::TypeId class_type_id,
+                           SemIR::TypeId element_type_id) -> SemIR::TypeId {
+  return GetTypeImpl<SemIR::UnboundElementType>(context, class_type_id,
+                                                element_type_id);
+}
+
+}  // namespace Carbon::Check

+ 83 - 0
toolchain/check/type.h

@@ -0,0 +1,83 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#ifndef CARBON_TOOLCHAIN_CHECK_TYPE_H_
+#define CARBON_TOOLCHAIN_CHECK_TYPE_H_
+
+#include "llvm/ADT/ArrayRef.h"
+#include "toolchain/check/context.h"
+#include "toolchain/sem_ir/ids.h"
+
+namespace Carbon::Check {
+
+// Create a FacetType typed instruction object consisting of a single
+// interface.
+auto FacetTypeFromInterface(Context& context, SemIR::InterfaceId interface_id,
+                            SemIR::SpecificId specific_id) -> SemIR::FacetType;
+
+// Gets the type to use for an unbound associated entity declared in this
+// interface. For example, this is the type of `I.T` after
+// `interface I { let T:! type; }`. The name of the interface is used for
+// diagnostics.
+// TODO: Should we use a different type for each such entity, or the same type
+// for all associated entities?
+auto GetAssociatedEntityType(Context& context, SemIR::TypeId interface_type_id)
+    -> SemIR::TypeId;
+
+// Gets a singleton type. The returned type will be complete. Requires that
+// `singleton_id` is already validated to be a singleton.
+auto GetSingletonType(Context& context, SemIR::InstId singleton_id)
+    -> SemIR::TypeId;
+
+// Gets a class type.
+auto GetClassType(Context& context, SemIR::ClassId class_id,
+                  SemIR::SpecificId specific_id) -> SemIR::TypeId;
+
+// Gets a function type. The returned type will be complete.
+auto GetFunctionType(Context& context, SemIR::FunctionId fn_id,
+                     SemIR::SpecificId specific_id) -> SemIR::TypeId;
+
+// Gets the type of an associated function with the `Self` parameter bound to
+// a particular value. The returned type will be complete.
+auto GetFunctionTypeWithSelfType(Context& context,
+                                 SemIR::InstId interface_function_type_id,
+                                 SemIR::InstId self_id) -> SemIR::TypeId;
+
+// Gets a generic class type, which is the type of a name of a generic class,
+// such as the type of `Vector` given `class Vector(T:! type)`. The returned
+// type will be complete.
+auto GetGenericClassType(Context& context, SemIR::ClassId class_id,
+                         SemIR::SpecificId enclosing_specific_id)
+    -> SemIR::TypeId;
+
+// Gets a generic interface type, which is the type of a name of a generic
+// interface, such as the type of `AddWith` given
+// `interface AddWith(T:! type)`. The returned type will be complete.
+auto GetGenericInterfaceType(Context& context, SemIR::InterfaceId interface_id,
+                             SemIR::SpecificId enclosing_specific_id)
+    -> SemIR::TypeId;
+
+// Gets the facet type corresponding to a particular interface.
+auto GetInterfaceType(Context& context, SemIR::InterfaceId interface_id,
+                      SemIR::SpecificId specific_id) -> SemIR::TypeId;
+
+// Returns a pointer type whose pointee type is `pointee_type_id`.
+auto GetPointerType(Context& context, SemIR::TypeId pointee_type_id)
+    -> SemIR::TypeId;
+
+// Returns a struct type with the given fields.
+auto GetStructType(Context& context, SemIR::StructTypeFieldsId fields_id)
+    -> SemIR::TypeId;
+
+// Returns a tuple type with the given element types.
+auto GetTupleType(Context& context, llvm::ArrayRef<SemIR::TypeId> type_ids)
+    -> SemIR::TypeId;
+
+// Returns an unbound element type.
+auto GetUnboundElementType(Context& context, SemIR::TypeId class_type_id,
+                           SemIR::TypeId element_type_id) -> SemIR::TypeId;
+
+}  // namespace Carbon::Check
+
+#endif  // CARBON_TOOLCHAIN_CHECK_TYPE_H_

+ 11 - 9
toolchain/check/type_completion.cpp

@@ -7,6 +7,7 @@
 #include "llvm/ADT/SmallVector.h"
 #include "toolchain/base/kind_switch.h"
 #include "toolchain/check/generic.h"
+#include "toolchain/check/type.h"
 
 namespace Carbon::Check {
 
@@ -290,7 +291,8 @@ auto TypeCompleter::AddNestedIncompleteTypes(SemIR::Inst type_inst) -> bool {
 }
 
 auto TypeCompleter::MakeEmptyValueRepr() const -> SemIR::ValueRepr {
-  return {.kind = SemIR::ValueRepr::None, .type_id = context_.GetTupleType({})};
+  return {.kind = SemIR::ValueRepr::None,
+          .type_id = GetTupleType(context_, {})};
 }
 
 auto TypeCompleter::MakeCopyValueRepr(
@@ -307,7 +309,7 @@ auto TypeCompleter::MakePointerValueRepr(
   // TODO: Should we add `const` qualification to `pointee_id`?
   return {.kind = SemIR::ValueRepr::Pointer,
           .aggregate_kind = aggregate_kind,
-          .type_id = context_.GetPointerType(pointee_id)};
+          .type_id = GetPointerType(context_, pointee_id)};
 }
 
 auto TypeCompleter::GetNestedValueRepr(SemIR::TypeId nested_type_id) const
@@ -375,8 +377,8 @@ auto TypeCompleter::BuildValueReprForInst(SemIR::TypeId type_id,
   auto value_rep =
       same_as_object_rep
           ? type_id
-          : context_.GetStructType(
-                context_.struct_type_fields().AddCanonical(value_rep_fields));
+          : GetStructType(context_, context_.struct_type_fields().AddCanonical(
+                                        value_rep_fields));
   return BuildStructOrTupleValueRepr(fields.size(), value_rep,
                                      same_as_object_rep);
 }
@@ -405,7 +407,7 @@ auto TypeCompleter::BuildValueReprForInst(SemIR::TypeId type_id,
   }
 
   auto value_rep =
-      same_as_object_rep ? type_id : context_.GetTupleType(value_rep_elements);
+      same_as_object_rep ? type_id : GetTupleType(context_, value_rep_elements);
   return BuildStructOrTupleValueRepr(elements.size(), value_rep,
                                      same_as_object_rep);
 }
@@ -488,10 +490,10 @@ auto RequireCompleteType(Context& context, SemIR::TypeId type_id,
   if (type_id.AsConstantId().is_symbolic()) {
     // TODO: Deduplicate these.
     context.AddInstInNoBlock(SemIR::LocIdAndInst(
-        loc_id,
-        SemIR::RequireCompleteType{.type_id = context.GetSingletonType(
-                                       SemIR::WitnessType::SingletonInstId),
-                                   .complete_type_id = type_id}));
+        loc_id, SemIR::RequireCompleteType{
+                    .type_id = GetSingletonType(
+                        context, SemIR::WitnessType::SingletonInstId),
+                    .complete_type_id = type_id}));
   }
 
   return true;

+ 2 - 2
toolchain/docs/adding_features.md

@@ -298,11 +298,11 @@ If the resulting SemIR needs a new instruction:
         constructed as a special-case in
         [`File` construction](/toolchain/sem_ir/file.cpp). To get a type id for
         one of these builtin types, use something like
-        `context.GetSingletonType(SemIR::WitnessType::SingletonInstId)`, as in:
+        `GetSingletonType(context,SemIR::WitnessType::SingletonInstId)`, as in:
 
         ```
         SemIR::TypeId witness_type_id =
-            context.GetSingletonType(SemIR::WitnessType::SingletonInstId);
+            GetSingletonType(context,SemIR::WitnessType::SingletonInstId);
         SemIR::InstId inst_id = context.AddInst<SemIR::NewInstKindName>(
             node_id, {.type_id = witness_type_id, ...});
         ```

+ 20 - 0
toolchain/sem_ir/type.cpp

@@ -8,6 +8,26 @@
 
 namespace Carbon::SemIR {
 
+auto TypeStore::GetTypeIdForTypeConstantId(SemIR::ConstantId constant_id) const
+    -> SemIR::TypeId {
+  CARBON_CHECK(constant_id.is_constant(),
+               "Canonicalizing non-constant type: {0}", constant_id);
+  auto type_id = file_->insts()
+                     .Get(file_->constant_values().GetInstId(constant_id))
+                     .type_id();
+  CARBON_CHECK(type_id == SemIR::TypeType::SingletonTypeId ||
+                   constant_id == SemIR::ErrorInst::SingletonConstantId,
+               "Forming type ID for non-type constant of type {0}",
+               GetAsInst(type_id));
+
+  return SemIR::TypeId::ForTypeConstant(constant_id);
+}
+
+auto TypeStore::GetTypeIdForTypeInstId(SemIR::InstId inst_id) const
+    -> SemIR::TypeId {
+  return GetTypeIdForTypeConstantId(file_->constant_values().Get(inst_id));
+}
+
 auto TypeStore::GetInstId(TypeId type_id) const -> InstId {
   return file_->constant_values().GetInstId(GetConstantId(type_id));
 }

+ 25 - 0
toolchain/sem_ir/type.h

@@ -33,6 +33,25 @@ class TypeStore : public Yaml::Printable<TypeStore> {
     return type_id.AsConstantId();
   }
 
+  // Returns the type ID for a constant that is a type value, i.e. it is a value
+  // of type `TypeType`.
+  //
+  // Facet values are of the same typishness as types, but are not themselves
+  // types, so they can not be passed here. They should be converted to a type
+  // through an `as type` conversion, that is, to a value of type `TypeType`.
+  auto GetTypeIdForTypeConstantId(SemIR::ConstantId constant_id) const
+      -> SemIR::TypeId;
+
+  // Returns the type ID for an instruction whose constant value is a type
+  // value, i.e. it is a value of type `TypeType`.
+  //
+  // Instructions whose values are facet values (see `FacetValue`) produce a
+  // value of the same typishness as types, but which are themselves not types,
+  // so they can not be passed here. They should be converted to a type through
+  // an `as type` conversion, such as to a `FacetAccessType` instruction whose
+  // value is of type `TypeType`.
+  auto GetTypeIdForTypeInstId(SemIR::InstId inst_id) const -> SemIR::TypeId;
+
   // Returns the ID of the instruction used to define the specified type.
   auto GetInstId(TypeId type_id) const -> InstId;
 
@@ -113,6 +132,12 @@ class TypeStore : public Yaml::Printable<TypeStore> {
   // non-constant width and for IntLiteral.
   auto GetIntTypeInfo(TypeId int_type_id) const -> IntTypeInfo;
 
+  // Returns whether `type_id` represents a facet type.
+  auto IsFacetType(SemIR::TypeId type_id) const -> bool {
+    return type_id == SemIR::TypeType::SingletonTypeId ||
+           Is<SemIR::FacetType>(type_id);
+  }
+
   // Returns a list of types that were completed in this file, in the order in
   // which they were completed. Earlier types in this list cannot contain
   // instances of later types.