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

Switch TypeId::TypeType to TypeType::SingletonTypeId, and similar (#4619)

`ids.h` and `ids.cpp` are the manual edits, everything else is
search-and-replace.

The full list of things moved is:

- `TypeId::TypeType`
- `TypeId::AutoType`
- `TypeId::Error`
- `ConstantId::Error`

This is to unblock removing `InstId::Builtin*`.
Jon Ross-Perkins 1 год назад
Родитель
Сommit
0e92e6cc5a

+ 2 - 2
toolchain/check/call.cpp

@@ -97,7 +97,7 @@ static auto PerformCallToGenericClass(Context& context, SemIR::LocId loc_id,
     return SemIR::InstId::BuiltinErrorInst;
   }
   return context.GetOrAddInst<SemIR::ClassType>(
-      loc_id, {.type_id = SemIR::TypeId::TypeType,
+      loc_id, {.type_id = SemIR::TypeType::SingletonTypeId,
                .class_id = class_id,
                .specific_id = *callee_specific_id});
 }
@@ -202,7 +202,7 @@ auto PerformCall(Context& context, SemIR::LocId loc_id, SemIR::InstId callee_id,
     case SemIR::InitRepr::Incomplete:
       // Don't form an initializing expression with an incomplete type.
       // CheckFunctionReturnType will have diagnosed this for us if needed.
-      return_info.type_id = SemIR::TypeId::Error;
+      return_info.type_id = SemIR::ErrorInst::SingletonTypeId;
       break;
   }
 

+ 11 - 9
toolchain/check/context.cpp

@@ -61,10 +61,10 @@ Context::Context(const Lex::TokenizedBuffer& tokens, DiagnosticEmitter& emitter,
   // special `TypeId` values.
   type_ids_for_type_constants_.Insert(
       SemIR::ConstantId::ForTemplateConstant(SemIR::InstId::BuiltinErrorInst),
-      SemIR::TypeId::Error);
+      SemIR::ErrorInst::SingletonTypeId);
   type_ids_for_type_constants_.Insert(
       SemIR::ConstantId::ForTemplateConstant(SemIR::InstId::BuiltinTypeType),
-      SemIR::TypeId::TypeType);
+      SemIR::TypeType::SingletonTypeId);
 
   // TODO: Remove this and add a `VerifyOnFinish` once we properly push and pop
   // in the right places.
@@ -492,7 +492,7 @@ auto Context::AppendLookupScopesForConstant(
     }
     return true;
   }
-  if (base_const_id == SemIR::ConstantId::Error) {
+  if (base_const_id == SemIR::ErrorInst::SingletonConstantId) {
     // Lookup into this scope should fail without producing an error.
     scopes->push_back(LookupScope{.name_scope_id = SemIR::NameScopeId::Invalid,
                                   .specific_id = SemIR::SpecificId::Invalid});
@@ -761,9 +761,9 @@ auto Context::SetBlockArgResultBeforeConstantUse(SemIR::InstId select_id,
     const_id = constant_values().Get(literal.value().value.ToBool() ? if_true
                                                                     : if_false);
   } else {
-    CARBON_CHECK(cond_const_id == SemIR::ConstantId::Error,
+    CARBON_CHECK(cond_const_id == SemIR::ErrorInst::SingletonConstantId,
                  "Unexpected constant branch condition.");
-    const_id = SemIR::ConstantId::Error;
+    const_id = SemIR::ErrorInst::SingletonConstantId;
   }
 
   if (const_id.is_constant()) {
@@ -908,7 +908,7 @@ class TypeCompleter {
         }
         // For a pointer representation, the pointee also needs to be complete.
         if (value_rep.kind == SemIR::ValueRepr::Pointer) {
-          if (value_rep.type_id == SemIR::TypeId::Error) {
+          if (value_rep.type_id == SemIR::ErrorInst::SingletonTypeId) {
             break;
           }
           auto pointee_type_id =
@@ -1295,7 +1295,8 @@ auto Context::GetTypeIdForTypeConstant(SemIR::ConstantId constant_id)
   auto type_id =
       insts().Get(constant_values().GetInstId(constant_id)).type_id();
   // TODO: For now, we allow values of facet type to be used as types.
-  CARBON_CHECK(IsFacetType(type_id) || constant_id == SemIR::ConstantId::Error,
+  CARBON_CHECK(IsFacetType(type_id) ||
+                   constant_id == SemIR::ErrorInst::SingletonConstantId,
                "Forming type ID for non-type constant of type {0}",
                types().GetAsInst(type_id));
 
@@ -1308,7 +1309,8 @@ auto Context::FacetTypeFromInterface(SemIR::InterfaceId interface_id,
   SemIR::FacetTypeId facet_type_id = facet_types().Add(
       SemIR::FacetTypeInfo{.impls_constraints = {{interface_id, specific_id}},
                            .other_requirements = false});
-  return {.type_id = SemIR::TypeId::TypeType, .facet_type_id = facet_type_id};
+  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.
@@ -1316,7 +1318,7 @@ 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::TypeId::TypeType, each_arg...};
+  InstT inst = {SemIR::TypeType::SingletonTypeId, each_arg...};
   return context.GetTypeIdForTypeConstant(
       TryEvalInst(context, SemIR::InstId::Invalid, inst));
 }

+ 2 - 2
toolchain/check/context.h

@@ -362,12 +362,12 @@ class Context {
       -> SemIR::TypeId {
     return TryToCompleteType(type_id, diagnoser, abstract_diagnoser)
                ? type_id
-               : SemIR::TypeId::Error;
+               : SemIR::ErrorInst::SingletonTypeId;
   }
 
   // Returns whether `type_id` represents a facet type.
   auto IsFacetType(SemIR::TypeId type_id) -> bool {
-    return type_id == SemIR::TypeId::TypeType ||
+    return type_id == SemIR::TypeType::SingletonTypeId ||
            types().Is<SemIR::FacetType>(type_id);
   }
 

+ 11 - 9
toolchain/check/convert.cpp

@@ -550,7 +550,7 @@ static auto ConvertStructToClass(Context& context, SemIR::StructType src_type,
   CARBON_CHECK(dest_class_info.inheritance_kind != SemIR::Class::Abstract);
   auto object_repr_id =
       dest_class_info.GetObjectRepr(context.sem_ir(), dest_type.specific_id);
-  if (object_repr_id == SemIR::TypeId::Error) {
+  if (object_repr_id == SemIR::ErrorInst::SingletonTypeId) {
     return SemIR::InstId::BuiltinErrorInst;
   }
   auto dest_struct_type =
@@ -866,7 +866,7 @@ static auto PerformBuiltinConversion(Context& context, SemIR::LocId loc_id,
     }
   }
 
-  if (target.type_id == SemIR::TypeId::TypeType) {
+  if (target.type_id == SemIR::TypeType::SingletonTypeId) {
     // A tuple of types converts to type `type`.
     // TODO: This should apply even for non-literal tuples.
     if (auto tuple_literal = value.TryAs<SemIR::TupleLiteral>()) {
@@ -913,7 +913,7 @@ static auto PerformCopy(Context& context, SemIR::InstId expr_id)
     -> SemIR::InstId {
   auto expr = context.insts().Get(expr_id);
   auto type_id = expr.type_id();
-  if (type_id == SemIR::TypeId::Error) {
+  if (type_id == SemIR::ErrorInst::SingletonTypeId) {
     return SemIR::InstId::BuiltinErrorInst;
   }
 
@@ -943,8 +943,9 @@ auto Convert(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id,
 
   // Start by making sure both sides are valid. If any part is invalid, the
   // result is invalid and we shouldn't error.
-  if (sem_ir.insts().Get(expr_id).type_id() == SemIR::TypeId::Error ||
-      target.type_id == SemIR::TypeId::Error) {
+  if (sem_ir.insts().Get(expr_id).type_id() ==
+          SemIR::ErrorInst::SingletonTypeId ||
+      target.type_id == SemIR::ErrorInst::SingletonTypeId) {
     return SemIR::InstId::BuiltinErrorInst;
   }
 
@@ -1215,10 +1216,11 @@ auto ConvertCallArgs(Context& context, SemIR::LocId call_loc_id,
 
 auto ExprAsType(Context& context, SemIR::LocId loc_id, SemIR::InstId value_id)
     -> TypeExpr {
-  auto type_inst_id =
-      ConvertToValueOfType(context, loc_id, value_id, SemIR::TypeId::TypeType);
+  auto type_inst_id = ConvertToValueOfType(context, loc_id, value_id,
+                                           SemIR::TypeType::SingletonTypeId);
   if (type_inst_id == SemIR::InstId::BuiltinErrorInst) {
-    return {.inst_id = type_inst_id, .type_id = SemIR::TypeId::Error};
+    return {.inst_id = type_inst_id,
+            .type_id = SemIR::ErrorInst::SingletonTypeId};
   }
 
   auto type_const_id = context.constant_values().Get(type_inst_id);
@@ -1227,7 +1229,7 @@ auto ExprAsType(Context& context, SemIR::LocId loc_id, SemIR::InstId value_id)
                       "cannot evaluate type expression");
     context.emitter().Emit(loc_id, TypeExprEvaluationFailure);
     return {.inst_id = SemIR::InstId::BuiltinErrorInst,
-            .type_id = SemIR::TypeId::Error};
+            .type_id = SemIR::ErrorInst::SingletonTypeId};
   }
 
   return {.inst_id = type_inst_id,

+ 1 - 1
toolchain/check/convert.h

@@ -104,7 +104,7 @@ auto ConvertCallArgs(Context& context, SemIR::LocId call_loc_id,
 struct TypeExpr {
   // The converted expression of type `type`, or `InstId::BuiltinErrorInst`.
   SemIR::InstId inst_id;
-  // The corresponding type, or `TypeId::Error`.
+  // The corresponding type, or `ErrorInst::SingletonTypeId`.
   SemIR::TypeId type_id;
 };
 

+ 25 - 23
toolchain/check/eval.cpp

@@ -198,7 +198,7 @@ static auto GetPhase(EvalContext& eval_context, SemIR::ConstantId constant_id)
     -> Phase {
   if (!constant_id.is_constant()) {
     return Phase::Runtime;
-  } else if (constant_id == SemIR::ConstantId::Error) {
+  } else if (constant_id == SemIR::ErrorInst::SingletonConstantId) {
     return Phase::UnknownDueToError;
   } else if (constant_id.is_template()) {
     return Phase::Template;
@@ -246,7 +246,7 @@ static auto MakeConstantResult(Context& context, SemIR::Inst inst, Phase phase)
       return context.constants().GetOrAdd(inst,
                                           SemIR::ConstantStore::IsSymbolic);
     case Phase::UnknownDueToError:
-      return SemIR::ConstantId::Error;
+      return SemIR::ErrorInst::SingletonConstantId;
     case Phase::Runtime:
       return SemIR::ConstantId::NotConstant;
   }
@@ -254,8 +254,9 @@ static auto MakeConstantResult(Context& context, SemIR::Inst inst, Phase phase)
 
 // Forms a `constant_id` describing why an evaluation was not constant.
 static auto MakeNonConstantResult(Phase phase) -> SemIR::ConstantId {
-  return phase == Phase::UnknownDueToError ? SemIR::ConstantId::Error
-                                           : SemIR::ConstantId::NotConstant;
+  return phase == Phase::UnknownDueToError
+             ? SemIR::ErrorInst::SingletonConstantId
+             : SemIR::ConstantId::NotConstant;
 }
 
 // Converts a bool value into a ConstantId.
@@ -460,14 +461,14 @@ static auto ReplaceFieldWithConstantValue(EvalContext& eval_context,
 // If the specified fields of the given typed instruction have constant values,
 // replaces the fields with their constant values and builds a corresponding
 // constant value. Otherwise returns `ConstantId::NotConstant`. Returns
-// `ConstantId::Error` if any subexpression is an error.
+// `ErrorInst::SingletonConstantId` if any subexpression is an error.
 //
 // The constant value is then checked by calling `validate_fn(typed_inst)`,
 // which should return a `bool` indicating whether the new constant is valid. If
 // validation passes, `transform_fn(typed_inst)` is called to produce the final
 // constant instruction, and a corresponding ConstantId for the new constant is
 // returned. If validation fails, it should produce a suitable error message.
-// `ConstantId::Error` is returned.
+// `ErrorInst::SingletonConstantId` is returned.
 template <typename InstT, typename ValidateFn, typename TransformFn,
           typename... EachFieldIdT>
 static auto RebuildIfFieldsAreConstantImpl(
@@ -482,7 +483,7 @@ static auto RebuildIfFieldsAreConstantImpl(
                                      &phase) &&
        ...)) {
     if (phase == Phase::UnknownDueToError || !validate_fn(typed_inst)) {
-      return SemIR::ConstantId::Error;
+      return SemIR::ErrorInst::SingletonConstantId;
     }
     return MakeConstantResult(eval_context.context(), transform_fn(typed_inst),
                               phase);
@@ -603,7 +604,7 @@ static auto PerformArrayIndex(EvalContext& eval_context, SemIR::ArrayIndex inst)
         eval_context.emitter().Emit(
             inst.index_id, ArrayIndexOutOfBounds,
             {.type = index->type_id, .value = index_val}, aggregate_type_id);
-        return SemIR::ConstantId::Error;
+        return SemIR::ErrorInst::SingletonConstantId;
       }
     }
   }
@@ -670,7 +671,7 @@ static auto MakeIntTypeResult(Context& context, SemIRLoc loc,
       .int_kind = int_kind,
       .bit_width_id = width_id};
   if (!ValidateIntType(context, loc, result)) {
-    return SemIR::ConstantId::Error;
+    return SemIR::ErrorInst::SingletonConstantId;
   }
   return MakeConstantResult(context, result, phase);
 }
@@ -798,7 +799,7 @@ static auto PerformBuiltinBinaryIntOp(Context& context, SemIRLoc loc,
     case SemIR::BuiltinFunctionKind::IntUMod:
       if (context.ints().Get(rhs.int_id).isZero()) {
         DiagnoseDivisionByZero(context, loc);
-        return SemIR::ConstantId::Error;
+        return SemIR::ErrorInst::SingletonConstantId;
       }
       break;
     default:
@@ -830,7 +831,7 @@ static auto PerformBuiltinBinaryIntOp(Context& context, SemIRLoc loc,
             builtin_kind == SemIR::BuiltinFunctionKind::IntLeftShift,
             {.type = rhs.type_id, .value = rhs_orig_val});
         // TODO: Is it useful to recover by returning 0 or -1?
-        return SemIR::ConstantId::Error;
+        return SemIR::ErrorInst::SingletonConstantId;
       }
 
       if (builtin_kind == SemIR::BuiltinFunctionKind::IntLeftShift) {
@@ -1110,7 +1111,7 @@ static auto MakeConstantForBuiltinCall(Context& context, SemIRLoc loc,
         break;
       }
       if (!ValidateFloatBitWidth(context, loc, arg_ids[0])) {
-        return SemIR::ConstantId::Error;
+        return SemIR::ErrorInst::SingletonConstantId;
       }
       return context.constant_values().Get(
           SemIR::InstId::BuiltinLegacyFloatType);
@@ -1224,7 +1225,7 @@ static auto MakeConstantForCall(EvalContext& eval_context, SemIRLoc loc,
   //
   // TODO: Use a better representation for this.
   if (call.args_id == SemIR::InstBlockId::Invalid) {
-    return SemIR::ConstantId::Error;
+    return SemIR::ErrorInst::SingletonConstantId;
   }
 
   // Find the constant value of the callee.
@@ -1257,7 +1258,7 @@ static auto MakeConstantForCall(EvalContext& eval_context, SemIRLoc loc,
       ReplaceFieldWithConstantValue(eval_context, &call, &SemIR::Call::args_id,
                                     &phase);
   if (phase == Phase::UnknownDueToError) {
-    return SemIR::ConstantId::Error;
+    return SemIR::ErrorInst::SingletonConstantId;
   }
 
   // If any operand of the call is non-constant, the call is non-constant.
@@ -1294,10 +1295,11 @@ static auto MakeFacetTypeResult(Context& context,
                                 const SemIR::FacetTypeInfo& info, Phase phase)
     -> SemIR::ConstantId {
   SemIR::FacetTypeId facet_type_id = context.facet_types().Add(info);
-  return MakeConstantResult(context,
-                            SemIR::FacetType{.type_id = SemIR::TypeId::TypeType,
-                                             .facet_type_id = facet_type_id},
-                            phase);
+  return MakeConstantResult(
+      context,
+      SemIR::FacetType{.type_id = SemIR::TypeType::SingletonTypeId,
+                       .facet_type_id = facet_type_id},
+      phase);
 }
 
 // Implementation for `TryEvalInst`, wrapping `Context` with `EvalContext`.
@@ -1485,7 +1487,7 @@ static auto TryEvalInstInContext(EvalContext& eval_context,
       // A non-generic class declaration evaluates to the class type.
       return MakeConstantResult(
           eval_context.context(),
-          SemIR::ClassType{.type_id = SemIR::TypeId::TypeType,
+          SemIR::ClassType{.type_id = SemIR::TypeType::SingletonTypeId,
                            .class_id = class_decl.class_id,
                            .specific_id = SemIR::SpecificId::Invalid},
           Phase::Template);
@@ -1702,10 +1704,10 @@ static auto TryEvalInstInContext(EvalContext& eval_context,
       if (auto facet_type = base_facet_inst.TryAs<SemIR::FacetType>()) {
         info = GetConstantFacetTypeInfo(eval_context, facet_type->facet_type_id,
                                         &phase);
-      } else if (base_facet_type_id == SemIR::TypeId::Error) {
-        return SemIR::ConstantId::Error;
+      } else if (base_facet_type_id == SemIR::ErrorInst::SingletonTypeId) {
+        return SemIR::ErrorInst::SingletonConstantId;
       } else {
-        CARBON_CHECK(base_facet_type_id == SemIR::TypeId::TypeType,
+        CARBON_CHECK(base_facet_type_id == SemIR::TypeType::SingletonTypeId,
                      "Unexpected type_id: {0}, inst: {1}", base_facet_type_id,
                      base_facet_inst);
       }
@@ -1747,7 +1749,7 @@ static auto TryEvalInstInContext(EvalContext& eval_context,
                               !value.value.ToBool());
       }
       if (phase == Phase::UnknownDueToError) {
-        return SemIR::ConstantId::Error;
+        return SemIR::ErrorInst::SingletonConstantId;
       }
       break;
     }

+ 2 - 2
toolchain/check/function.cpp

@@ -26,8 +26,8 @@ auto CheckFunctionTypeMatches(Context& context,
       new_function.GetDeclaredReturnType(context.sem_ir());
   auto prev_return_type_id =
       prev_function.GetDeclaredReturnType(context.sem_ir(), prev_specific_id);
-  if (new_return_type_id == SemIR::TypeId::Error ||
-      prev_return_type_id == SemIR::TypeId::Error) {
+  if (new_return_type_id == SemIR::ErrorInst::SingletonTypeId ||
+      prev_return_type_id == SemIR::ErrorInst::SingletonTypeId) {
     return false;
   }
   if (!context.types().AreEqualAcrossDeclarations(new_return_type_id,

+ 1 - 1
toolchain/check/handle_alias.cpp

@@ -61,7 +61,7 @@ auto HandleParseNode(Context& context, Parse::AliasId /*node_id*/) -> bool {
     CARBON_DIAGNOSTIC(AliasRequiresNameRef, Error,
                       "alias initializer must be a name reference");
     context.emitter().Emit(expr_node, AliasRequiresNameRef);
-    alias_type_id = SemIR::TypeId::Error;
+    alias_type_id = SemIR::ErrorInst::SingletonTypeId;
     alias_value_id = SemIR::InstId::BuiltinErrorInst;
   }
   auto alias_id = context.AddInst<SemIR::BindAlias>(

+ 1 - 1
toolchain/check/handle_array.cpp

@@ -54,7 +54,7 @@ auto HandleParseNode(Context& context, Parse::ArrayExprId node_id) -> bool {
       context, context.insts().GetLocId(bound_inst_id), bound_inst_id,
       context.GetBuiltinType(SemIR::BuiltinInstKind::IntLiteralType));
   context.AddInstAndPush<SemIR::ArrayType>(
-      node_id, {.type_id = SemIR::TypeId::TypeType,
+      node_id, {.type_id = SemIR::TypeType::SingletonTypeId,
                 .bound_id = bound_inst_id,
                 .element_type_id = element_type_id});
   return true;

+ 2 - 2
toolchain/check/handle_binding_pattern.cpp

@@ -307,8 +307,8 @@ auto HandleParseNode(Context& context, Parse::AddrId node_id) -> bool {
         context.insts().Get(param_pattern_id).type_id());
     if (pointer_type) {
       auto addr_pattern_id = context.AddPatternInst<SemIR::AddrPattern>(
-          node_id,
-          {.type_id = SemIR::TypeId::AutoType, .inner_id = param_pattern_id});
+          node_id, {.type_id = SemIR::AutoType::SingletonTypeId,
+                    .inner_id = param_pattern_id});
       context.node_stack().Push(node_id, addr_pattern_id);
     } else {
       CARBON_DIAGNOSTIC(

+ 16 - 14
toolchain/check/handle_class.cpp

@@ -206,9 +206,10 @@ static auto BuildClassDecl(Context& context, Parse::AnyClassDeclId node_id,
   auto decl_block_id = context.inst_block_stack().Pop();
 
   // Add the class declaration.
-  auto class_decl = SemIR::ClassDecl{.type_id = SemIR::TypeId::TypeType,
-                                     .class_id = SemIR::ClassId::Invalid,
-                                     .decl_block_id = decl_block_id};
+  auto class_decl =
+      SemIR::ClassDecl{.type_id = SemIR::TypeType::SingletonTypeId,
+                       .class_id = SemIR::ClassId::Invalid,
+                       .decl_block_id = decl_block_id};
   auto class_decl_id =
       context.AddPlaceholderInst(SemIR::LocIdAndInst(node_id, class_decl));
 
@@ -250,11 +251,11 @@ 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::Invalid,
-                    SemIR::ClassType{.type_id = SemIR::TypeId::TypeType,
-                                     .class_id = class_decl.class_id,
-                                     .specific_id = specific_id}));
+    class_info.self_type_id = context.GetTypeIdForTypeConstant(TryEvalInst(
+        context, SemIR::InstId::Invalid,
+        SemIR::ClassType{.type_id = SemIR::TypeType::SingletonTypeId,
+                         .class_id = class_decl.class_id,
+                         .specific_id = specific_id}));
   }
 
   if (!is_definition && context.IsImplFile() && !is_extern) {
@@ -398,7 +399,7 @@ auto HandleParseNode(Context& context, Parse::AdaptDeclId node_id) -> bool {
         return context.emitter().Build(node_id, AbstractTypeInAdaptDecl,
                                        adapted_inst_id);
       });
-  if (adapted_type_id == SemIR::TypeId::Error) {
+  if (adapted_type_id == SemIR::ErrorInst::SingletonTypeId) {
     adapted_inst_id = SemIR::InstId::BuiltinErrorInst;
   }
 
@@ -436,7 +437,7 @@ struct BaseInfo {
   SemIR::InstId inst_id;
 };
 constexpr BaseInfo BaseInfo::Error = {
-    .type_id = SemIR::TypeId::Error,
+    .type_id = SemIR::ErrorInst::SingletonTypeId,
     .scope_id = SemIR::NameScopeId::Invalid,
     .inst_id = SemIR::InstId::BuiltinErrorInst};
 }  // namespace
@@ -463,7 +464,7 @@ static auto CheckBaseType(Context& context, Parse::NodeId node_id,
                                    base_type_inst_id);
   });
 
-  if (base_type_id == SemIR::TypeId::Error) {
+  if (base_type_id == SemIR::ErrorInst::SingletonTypeId) {
     return BaseInfo::Error;
   }
 
@@ -538,7 +539,7 @@ auto HandleParseNode(Context& context, Parse::BaseDeclId node_id) -> bool {
                 .base_type_inst_id = base_info.inst_id,
                 .index = SemIR::ElementIndex::Invalid});
 
-  if (base_info.type_id != SemIR::TypeId::Error) {
+  if (base_info.type_id != SemIR::ErrorInst::SingletonTypeId) {
     auto base_class_info = context.classes().Get(
         context.types().GetAs<SemIR::ClassType>(base_info.type_id).class_id);
     class_info.is_dynamic |= base_class_info.is_dynamic;
@@ -631,9 +632,10 @@ static auto AddStructTypeFields(
     field_decl.index =
         SemIR::ElementIndex{static_cast<int>(struct_type_fields.size())};
     context.ReplaceInstPreservingConstantValue(field_decl_id, field_decl);
-    if (field_decl.type_id == SemIR::TypeId::Error) {
+    if (field_decl.type_id == SemIR::ErrorInst::SingletonTypeId) {
       struct_type_fields.push_back(
-          {.name_id = field_decl.name_id, .type_id = SemIR::TypeId::Error});
+          {.name_id = field_decl.name_id,
+           .type_id = SemIR::ErrorInst::SingletonTypeId});
       continue;
     }
     auto unbound_element_type =

+ 2 - 2
toolchain/check/handle_impl.cpp

@@ -101,7 +101,7 @@ auto HandleParseNode(Context& context, Parse::DefaultSelfImplAsId node_id)
     CARBON_DIAGNOSTIC(ImplAsOutsideClass, Error,
                       "`impl as` can only be used in a class");
     context.emitter().Emit(node_id, ImplAsOutsideClass);
-    self_type_id = SemIR::TypeId::Error;
+    self_type_id = SemIR::ErrorInst::SingletonTypeId;
   }
 
   // Build the implicit access to the enclosing `Self`.
@@ -112,7 +112,7 @@ auto HandleParseNode(Context& context, Parse::DefaultSelfImplAsId node_id)
   // handling of the `Self` expression.
   auto self_inst_id = context.AddInst(
       node_id,
-      SemIR::NameRef{.type_id = SemIR::TypeId::TypeType,
+      SemIR::NameRef{.type_id = SemIR::TypeType::SingletonTypeId,
                      .name_id = SemIR::NameId::SelfType,
                      .value_id = context.types().GetInstId(self_type_id)});
 

+ 1 - 1
toolchain/check/handle_index.cpp

@@ -165,7 +165,7 @@ auto HandleParseNode(Context& context, Parse::IndexExprId node_id) -> bool {
 
     default: {
       auto elem_id = SemIR::InstId::BuiltinErrorInst;
-      if (operand_type_id != SemIR::TypeId::Error) {
+      if (operand_type_id != SemIR::ErrorInst::SingletonTypeId) {
         elem_id = PerformIndexWith(context, node_id, operand_inst_id,
                                    operand_type_id, index_inst_id);
       }

+ 3 - 2
toolchain/check/handle_interface.cpp

@@ -52,8 +52,9 @@ static auto BuildInterfaceDecl(Context& context,
   auto decl_block_id = context.inst_block_stack().Pop();
 
   // Add the interface declaration.
-  auto interface_decl = SemIR::InterfaceDecl{
-      SemIR::TypeId::TypeType, SemIR::InterfaceId::Invalid, decl_block_id};
+  auto interface_decl =
+      SemIR::InterfaceDecl{SemIR::TypeType::SingletonTypeId,
+                           SemIR::InterfaceId::Invalid, decl_block_id};
   auto interface_decl_id =
       context.AddPlaceholderInst(SemIR::LocIdAndInst(node_id, interface_decl));
 

+ 5 - 4
toolchain/check/handle_operator.cpp

@@ -215,8 +215,8 @@ auto HandleParseNode(Context& context, Parse::PostfixOperatorStarId node_id)
   auto value_id = context.node_stack().PopExpr();
   auto inner_type_id = ExprAsType(context, node_id, value_id).type_id;
   context.AddInstAndPush<SemIR::PointerType>(
-      node_id,
-      {.type_id = SemIR::TypeId::TypeType, .pointee_id = inner_type_id});
+      node_id, {.type_id = SemIR::TypeType::SingletonTypeId,
+                .pointee_id = inner_type_id});
   return true;
 }
 
@@ -268,7 +268,8 @@ auto HandleParseNode(Context& context, Parse::PrefixOperatorConstId node_id)
   }
   auto inner_type_id = ExprAsType(context, node_id, value_id).type_id;
   context.AddInstAndPush<SemIR::ConstType>(
-      node_id, {.type_id = SemIR::TypeId::TypeType, .inner_id = inner_type_id});
+      node_id,
+      {.type_id = SemIR::TypeType::SingletonTypeId, .inner_id = inner_type_id});
   return true;
 }
 
@@ -314,7 +315,7 @@ auto HandleParseNode(Context& context, Parse::PrefixOperatorStarId node_id)
             TokenOnly(node_id), DerefOfNonPointer, not_pointer_type_id);
 
         // TODO: Check for any facet here, rather than only a type.
-        if (not_pointer_type_id == SemIR::TypeId::TypeType) {
+        if (not_pointer_type_id == SemIR::TypeType::SingletonTypeId) {
           CARBON_DIAGNOSTIC(
               DerefOfType, Note,
               "to form a pointer type, write the `*` after the pointee type");

+ 2 - 1
toolchain/check/handle_struct.cpp

@@ -169,7 +169,8 @@ auto HandleParseNode(Context& context, Parse::StructTypeLiteralId node_id)
   } else {
     auto fields_id = context.struct_type_fields().AddCanonical(fields);
     context.AddInstAndPush<SemIR::StructType>(
-        node_id, {.type_id = SemIR::TypeId::TypeType, .fields_id = fields_id});
+        node_id,
+        {.type_id = SemIR::TypeType::SingletonTypeId, .fields_id = fields_id});
   }
 
   context.struct_type_fields_stack().PopArray();

+ 4 - 4
toolchain/check/handle_where.cpp

@@ -16,12 +16,12 @@ auto HandleParseNode(Context& context, Parse::WhereOperandId node_id) -> bool {
   auto [self_node, self_id] = context.node_stack().PopExprWithNodeId();
   auto self_type_id = ExprAsType(context, self_node, self_id).type_id;
   // Only facet types may have `where` restrictions.
-  if (self_type_id != SemIR::TypeId::Error &&
+  if (self_type_id != SemIR::ErrorInst::SingletonTypeId &&
       !context.IsFacetType(self_type_id)) {
     CARBON_DIAGNOSTIC(WhereOnNonFacetType, Error,
                       "left argument of `where` operator must be a facet type");
     context.emitter().Emit(self_node, WhereOnNonFacetType);
-    self_type_id = SemIR::TypeId::Error;
+    self_type_id = SemIR::ErrorInst::SingletonTypeId;
   }
 
   // Introduce a name scope so that we can remove the `.Self` entry we are
@@ -94,7 +94,7 @@ auto HandleParseNode(Context& context, Parse::RequirementImplsId node_id)
   // Check lhs is a facet and rhs is a facet type.
   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::TypeId::Error &&
+  if (rhs_as_type.type_id != SemIR::ErrorInst::SingletonTypeId &&
       !context.IsFacetType(rhs_as_type.type_id)) {
     CARBON_DIAGNOSTIC(
         ImplsOnNonFacetType, Error,
@@ -126,7 +126,7 @@ auto HandleParseNode(Context& context, Parse::WhereExprId node_id) -> bool {
       context.node_stack().Pop<Parse::NodeKind::WhereOperand>();
   SemIR::InstBlockId requirements_id = context.args_type_info_stack().Pop();
   context.AddInstAndPush<SemIR::WhereExpr>(
-      node_id, {.type_id = SemIR::TypeId::TypeType,
+      node_id, {.type_id = SemIR::TypeType::SingletonTypeId,
                 .period_self_id = period_self_id,
                 .requirements_id = requirements_id});
   return true;

+ 2 - 2
toolchain/check/impl.cpp

@@ -170,7 +170,7 @@ static auto BuildInterfaceWitness(
     auto decl = context.insts().Get(decl_id);
     CARBON_KIND_SWITCH(decl) {
       case CARBON_KIND(SemIR::StructValue struct_value): {
-        if (struct_value.type_id == SemIR::TypeId::Error) {
+        if (struct_value.type_id == SemIR::ErrorInst::SingletonTypeId) {
           return SemIR::InstId::BuiltinErrorInst;
         }
         auto type_inst = context.types().GetAsInst(struct_value.type_id);
@@ -229,7 +229,7 @@ auto BuildImplWitness(Context& context, SemIR::ImplId impl_id)
   CARBON_CHECK(impl.is_being_defined());
 
   auto facet_type_id = context.GetTypeIdForTypeInst(impl.constraint_id);
-  if (facet_type_id == SemIR::TypeId::Error) {
+  if (facet_type_id == SemIR::ErrorInst::SingletonTypeId) {
     return SemIR::InstId::BuiltinErrorInst;
   }
   auto facet_type = context.types().TryGetAs<SemIR::FacetType>(facet_type_id);

+ 38 - 37
toolchain/check/import_ref.cpp

@@ -1287,7 +1287,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
   }
 
   auto adapted_type_inst_id =
-      AddLoadedImportRef(resolver, SemIR::TypeId::TypeType,
+      AddLoadedImportRef(resolver, SemIR::TypeType::SingletonTypeId,
                          inst.adapted_type_inst_id, adapted_type_const_id);
 
   // Create a corresponding instruction to represent the declaration.
@@ -1318,7 +1318,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
 static auto TryResolveTypedInst(ImportRefResolver& resolver,
                                 SemIR::AssociatedEntityType inst)
     -> ResolveResult {
-  CARBON_CHECK(inst.type_id == SemIR::TypeId::TypeType);
+  CARBON_CHECK(inst.type_id == SemIR::TypeType::SingletonTypeId);
 
   auto entity_type_const_id = GetLocalConstantId(resolver, inst.entity_type_id);
   auto interface_inst_id = GetLocalConstantId(resolver, inst.interface_type_id);
@@ -1328,7 +1328,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
 
   return ResolveAs<SemIR::AssociatedEntityType>(
       resolver,
-      {.type_id = SemIR::TypeId::TypeType,
+      {.type_id = SemIR::TypeType::SingletonTypeId,
        .interface_type_id =
            resolver.local_context().GetTypeIdForTypeConstant(interface_inst_id),
        .entity_type_id = resolver.local_context().GetTypeIdForTypeConstant(
@@ -1346,7 +1346,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
   }
 
   auto base_type_inst_id =
-      AddLoadedImportRef(resolver, SemIR::TypeId::TypeType,
+      AddLoadedImportRef(resolver, SemIR::TypeType::SingletonTypeId,
                          inst.base_type_inst_id, base_type_const_id);
 
   // Create a corresponding instruction to represent the declaration.
@@ -1415,7 +1415,7 @@ static auto MakeIncompleteClass(ImportContext& context,
                                 const SemIR::Class& import_class,
                                 SemIR::SpecificId enclosing_specific_id)
     -> std::pair<SemIR::ClassId, SemIR::ConstantId> {
-  SemIR::ClassDecl class_decl = {.type_id = SemIR::TypeId::TypeType,
+  SemIR::ClassDecl class_decl = {.type_id = SemIR::TypeType::SingletonTypeId,
                                  .class_id = SemIR::ClassId::Invalid,
                                  .decl_block_id = SemIR::InstBlockId::Empty};
   auto class_decl_id = context.local_context().AddPlaceholderInstInNoBlock(
@@ -1566,7 +1566,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
 
 static auto TryResolveTypedInst(ImportRefResolver& resolver,
                                 SemIR::ClassType inst) -> ResolveResult {
-  CARBON_CHECK(inst.type_id == SemIR::TypeId::TypeType);
+  CARBON_CHECK(inst.type_id == SemIR::TypeType::SingletonTypeId);
   auto class_const_id = GetLocalConstantId(
       resolver,
       resolver.import_classes().Get(inst.class_id).first_owning_decl_id);
@@ -1588,10 +1588,10 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
             class_const_inst.type_id());
     auto specific_id =
         GetOrAddLocalSpecific(resolver, inst.specific_id, specific_data);
-    return ResolveAs<SemIR::ClassType>(resolver,
-                                       {.type_id = SemIR::TypeId::TypeType,
-                                        .class_id = generic_class_type.class_id,
-                                        .specific_id = specific_id});
+    return ResolveAs<SemIR::ClassType>(
+        resolver, {.type_id = SemIR::TypeType::SingletonTypeId,
+                   .class_id = generic_class_type.class_id,
+                   .specific_id = specific_id});
   }
 }
 
@@ -1614,7 +1614,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
 
 static auto TryResolveTypedInst(ImportRefResolver& resolver,
                                 SemIR::ConstType inst) -> ResolveResult {
-  CARBON_CHECK(inst.type_id == SemIR::TypeId::TypeType);
+  CARBON_CHECK(inst.type_id == SemIR::TypeType::SingletonTypeId);
   auto inner_const_id = GetLocalConstantId(resolver, inst.inner_id);
   if (resolver.HasNewWork()) {
     return ResolveResult::Retry();
@@ -1623,7 +1623,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
       resolver.local_context().GetTypeIdForTypeConstant(inner_const_id);
   return ResolveAs<SemIR::ConstType>(
       resolver,
-      {.type_id = SemIR::TypeId::TypeType, .inner_id = inner_type_id});
+      {.type_id = SemIR::TypeType::SingletonTypeId, .inner_id = inner_type_id});
 }
 
 static auto TryResolveTypedInst(ImportRefResolver& resolver,
@@ -1753,7 +1753,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
 
 static auto TryResolveTypedInst(ImportRefResolver& resolver,
                                 SemIR::FunctionType inst) -> ResolveResult {
-  CARBON_CHECK(inst.type_id == SemIR::TypeId::TypeType);
+  CARBON_CHECK(inst.type_id == SemIR::TypeType::SingletonTypeId);
   auto fn_val_id = GetLocalConstantInstId(
       resolver,
       resolver.import_functions().Get(inst.function_id).first_decl_id());
@@ -1763,7 +1763,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
   }
   auto fn_type_id = resolver.local_insts().Get(fn_val_id).type_id();
   return ResolveAs<SemIR::FunctionType>(
-      resolver, {.type_id = SemIR::TypeId::TypeType,
+      resolver, {.type_id = SemIR::TypeType::SingletonTypeId,
                  .function_id = resolver.local_types()
                                     .GetAs<SemIR::FunctionType>(fn_type_id)
                                     .function_id,
@@ -1773,7 +1773,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
 
 static auto TryResolveTypedInst(ImportRefResolver& resolver,
                                 SemIR::GenericClassType inst) -> ResolveResult {
-  CARBON_CHECK(inst.type_id == SemIR::TypeId::TypeType);
+  CARBON_CHECK(inst.type_id == SemIR::TypeType::SingletonTypeId);
   auto class_val_id = GetLocalConstantInstId(
       resolver,
       resolver.import_classes().Get(inst.class_id).first_owning_decl_id);
@@ -1790,7 +1790,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
 static auto TryResolveTypedInst(ImportRefResolver& resolver,
                                 SemIR::GenericInterfaceType inst)
     -> ResolveResult {
-  CARBON_CHECK(inst.type_id == SemIR::TypeId::TypeType);
+  CARBON_CHECK(inst.type_id == SemIR::TypeType::SingletonTypeId);
   auto interface_val_id = GetLocalConstantInstId(
       resolver,
       resolver.import_interfaces().Get(inst.interface_id).first_owning_decl_id);
@@ -1910,10 +1910,11 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
 
   // Create instructions for self and constraint to hold the symbolic constant
   // value for a generic impl.
-  new_impl.self_id = AddLoadedImportRef(resolver, SemIR::TypeId::TypeType,
-                                        import_impl.self_id, self_const_id);
+  new_impl.self_id =
+      AddLoadedImportRef(resolver, SemIR::TypeType::SingletonTypeId,
+                         import_impl.self_id, self_const_id);
   new_impl.constraint_id =
-      AddLoadedImportRef(resolver, SemIR::TypeId::TypeType,
+      AddLoadedImportRef(resolver, SemIR::TypeType::SingletonTypeId,
                          import_impl.constraint_id, constraint_const_id);
 
   if (import_impl.is_defined()) {
@@ -1937,12 +1938,12 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
   // Return the constant for the instruction of the imported constant.
   auto constant_id = resolver.import_constant_values().Get(inst_id);
   if (!constant_id.is_valid()) {
-    return ResolveResult::Done(SemIR::ConstantId::Error);
+    return ResolveResult::Done(SemIR::ErrorInst::SingletonConstantId);
   }
   if (!constant_id.is_constant()) {
     resolver.local_context().TODO(
         inst_id, "Non-constant ImportRefLoaded (comes up with var)");
-    return ResolveResult::Done(SemIR::ConstantId::Error);
+    return ResolveResult::Done(SemIR::ErrorInst::SingletonConstantId);
   }
 
   auto new_constant_id = GetLocalConstantId(
@@ -1957,7 +1958,7 @@ static auto MakeInterfaceDecl(ImportContext& context,
                               SemIR::SpecificId enclosing_specific_id)
     -> std::pair<SemIR::InterfaceId, SemIR::ConstantId> {
   SemIR::InterfaceDecl interface_decl = {
-      .type_id = SemIR::TypeId::TypeType,
+      .type_id = SemIR::TypeType::SingletonTypeId,
       .interface_id = SemIR::InterfaceId::Invalid,
       .decl_block_id = SemIR::InstBlockId::Empty};
   auto interface_decl_id = context.local_context().AddPlaceholderInstInNoBlock(
@@ -2099,7 +2100,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
   }
 
   return ResolveAs<SemIR::FacetAccessType>(
-      resolver, {.type_id = SemIR::TypeId::TypeType,
+      resolver, {.type_id = SemIR::TypeType::SingletonTypeId,
                  .facet_value_inst_id = facet_value_inst_id});
 }
 
@@ -2120,7 +2121,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
 
 static auto TryResolveTypedInst(ImportRefResolver& resolver,
                                 SemIR::FacetType inst) -> ResolveResult {
-  CARBON_CHECK(inst.type_id == SemIR::TypeId::TypeType);
+  CARBON_CHECK(inst.type_id == SemIR::TypeType::SingletonTypeId);
 
   const SemIR::FacetTypeInfo& facet_type_info =
       resolver.import_facet_types().Get(inst.facet_type_id);
@@ -2180,8 +2181,8 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
           .rewrite_constraints = rewrite_constraints,
           .other_requirements = facet_type_info.other_requirements});
   return ResolveAs<SemIR::FacetType>(
-      resolver,
-      {.type_id = SemIR::TypeId::TypeType, .facet_type_id = facet_type_id});
+      resolver, {.type_id = SemIR::TypeType::SingletonTypeId,
+                 .facet_type_id = facet_type_id});
 }
 
 static auto TryResolveTypedInst(ImportRefResolver& resolver,
@@ -2253,21 +2254,21 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
 
 static auto TryResolveTypedInst(ImportRefResolver& resolver,
                                 SemIR::IntType inst) -> ResolveResult {
-  CARBON_CHECK(inst.type_id == SemIR::TypeId::TypeType);
+  CARBON_CHECK(inst.type_id == SemIR::TypeType::SingletonTypeId);
   auto bit_width_id = GetLocalConstantInstId(resolver, inst.bit_width_id);
   if (resolver.HasNewWork()) {
     return ResolveResult::Retry();
   }
 
   return ResolveAs<SemIR::IntType>(resolver,
-                                   {.type_id = SemIR::TypeId::TypeType,
+                                   {.type_id = SemIR::TypeType::SingletonTypeId,
                                     .int_kind = inst.int_kind,
                                     .bit_width_id = bit_width_id});
 }
 
 static auto TryResolveTypedInst(ImportRefResolver& resolver,
                                 SemIR::PointerType inst) -> ResolveResult {
-  CARBON_CHECK(inst.type_id == SemIR::TypeId::TypeType);
+  CARBON_CHECK(inst.type_id == SemIR::TypeType::SingletonTypeId);
   auto pointee_const_id = GetLocalConstantId(resolver, inst.pointee_id);
   if (resolver.HasNewWork()) {
     return ResolveResult::Retry();
@@ -2276,8 +2277,8 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
   auto pointee_type_id =
       resolver.local_context().GetTypeIdForTypeConstant(pointee_const_id);
   return ResolveAs<SemIR::PointerType>(
-      resolver,
-      {.type_id = SemIR::TypeId::TypeType, .pointee_id = pointee_type_id});
+      resolver, {.type_id = SemIR::TypeType::SingletonTypeId,
+                 .pointee_id = pointee_type_id});
 }
 
 static auto TryResolveTypedInst(ImportRefResolver& resolver,
@@ -2300,7 +2301,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
 
 static auto TryResolveTypedInst(ImportRefResolver& resolver,
                                 SemIR::StructType inst) -> ResolveResult {
-  CARBON_CHECK(inst.type_id == SemIR::TypeId::TypeType);
+  CARBON_CHECK(inst.type_id == SemIR::TypeType::SingletonTypeId);
   auto orig_fields = resolver.import_struct_type_fields().Get(inst.fields_id);
   llvm::SmallVector<SemIR::ConstantId> field_const_ids;
   field_const_ids.reserve(orig_fields.size());
@@ -2323,7 +2324,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
   }
 
   return ResolveAs<SemIR::StructType>(
-      resolver, {.type_id = SemIR::TypeId::TypeType,
+      resolver, {.type_id = SemIR::TypeType::SingletonTypeId,
                  .fields_id = resolver.local_struct_type_fields().AddCanonical(
                      new_fields)});
 }
@@ -2345,7 +2346,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
 
 static auto TryResolveTypedInst(ImportRefResolver& resolver,
                                 SemIR::TupleType inst) -> ResolveResult {
-  CARBON_CHECK(inst.type_id == SemIR::TypeId::TypeType);
+  CARBON_CHECK(inst.type_id == SemIR::TypeType::SingletonTypeId);
 
   auto orig_elem_type_ids = resolver.import_type_blocks().Get(inst.elements_id);
   llvm::SmallVector<SemIR::ConstantId> elem_const_ids;
@@ -2387,7 +2388,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
 static auto TryResolveTypedInst(ImportRefResolver& resolver,
                                 SemIR::UnboundElementType inst)
     -> ResolveResult {
-  CARBON_CHECK(inst.type_id == SemIR::TypeId::TypeType);
+  CARBON_CHECK(inst.type_id == SemIR::TypeType::SingletonTypeId);
   auto class_const_id = GetLocalConstantId(resolver, inst.class_type_id);
   auto elem_const_id = GetLocalConstantId(resolver, inst.element_type_id);
   if (resolver.HasNewWork()) {
@@ -2396,7 +2397,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
 
   return ResolveAs<SemIR::UnboundElementType>(
       resolver,
-      {.type_id = SemIR::TypeId::TypeType,
+      {.type_id = SemIR::TypeType::SingletonTypeId,
        .class_type_id =
            resolver.local_context().GetTypeIdForTypeConstant(class_const_id),
        .element_type_id =
@@ -2536,7 +2537,7 @@ static auto TryResolveInstCanonical(ImportRefResolver& resolver,
         resolver.local_context().TODO(
             SemIR::LocId(AddImportIRInst(resolver, inst_id)),
             llvm::formatv("TryResolveInst on {0}", untyped_inst.kind()).str());
-        return {.const_id = SemIR::ConstantId::Error};
+        return {.const_id = SemIR::ErrorInst::SingletonConstantId};
       }
       // Try to resolve the constant value instead. Note that this can only
       // retry once.

+ 6 - 5
toolchain/check/member_access.cpp

@@ -271,7 +271,7 @@ static auto LookupMemberNameInScope(Context& context, SemIR::LocId loc_id,
           context.types().TryGetAs<SemIR::AssociatedEntityType>(type_id)) {
     if (lookup_in_type_of_base) {
       SemIR::TypeId base_type_id = context.insts().Get(base_id).type_id();
-      if (base_type_id != SemIR::TypeId::TypeType &&
+      if (base_type_id != SemIR::TypeType::SingletonTypeId &&
           context.IsFacetType(base_type_id)) {
         // Handles `T.F` when `T` is a non-type facet.
 
@@ -467,7 +467,7 @@ auto PerformMemberAccess(Context& context, SemIR::LocId loc_id,
       return SemIR::InstId::BuiltinErrorInst;
     }
 
-    if (base_type_id != SemIR::TypeId::Error) {
+    if (base_type_id != SemIR::ErrorInst::SingletonTypeId) {
       CARBON_DIAGNOSTIC(QualifiedExprUnsupported, Error,
                         "type {0} does not support qualified expressions",
                         TypeOfInstId);
@@ -514,7 +514,8 @@ auto PerformCompoundMemberAccess(
 
   // If we didn't perform impl lookup or instance binding, that's an error
   // because the base expression is not used for anything.
-  if (member_id == member_expr_id && member.type_id() != SemIR::TypeId::Error) {
+  if (member_id == member_expr_id &&
+      member.type_id() != SemIR::ErrorInst::SingletonTypeId) {
     CARBON_DIAGNOSTIC(CompoundMemberAccessDoesNotUseBase, Error,
                       "member name of type {0} in compound member access is "
                       "not an instance member or an interface member",
@@ -555,13 +556,13 @@ auto PerformTupleAccess(Context& context, SemIR::LocId loc_id,
     return diag_non_constant_index();
   }
 
-  SemIR::TypeId element_type_id = SemIR::TypeId::Error;
+  SemIR::TypeId element_type_id = SemIR::ErrorInst::SingletonTypeId;
   auto index_node_id = context.insts().GetLocId(index_inst_id);
   index_inst_id = ConvertToValueOfType(
       context, index_node_id, index_inst_id,
       context.GetBuiltinType(SemIR::BuiltinInstKind::IntLiteralType));
   auto index_const_id = context.constant_values().Get(index_inst_id);
-  if (index_const_id == SemIR::ConstantId::Error) {
+  if (index_const_id == SemIR::ErrorInst::SingletonConstantId) {
     return SemIR::InstId::BuiltinErrorInst;
   } else if (!index_const_id.is_template()) {
     return diag_non_constant_index();

+ 2 - 1
toolchain/check/merge.cpp

@@ -183,7 +183,8 @@ static auto EntityHasParamError(Context& context, const DeclParams& info)
     if (param_patterns_id.is_valid() &&
         param_patterns_id != SemIR::InstBlockId::Empty) {
       for (auto param_id : context.inst_blocks().Get(param_patterns_id)) {
-        if (context.insts().Get(param_id).type_id() == SemIR::TypeId::Error) {
+        if (context.insts().Get(param_id).type_id() ==
+            SemIR::ErrorInst::SingletonTypeId) {
           return true;
         }
       }

+ 2 - 2
toolchain/check/pointer_dereference.cpp

@@ -25,11 +25,11 @@ auto PerformPointerDereference(
   base_id = ConvertToValueExpr(context, base_id);
   auto type_id = context.types().GetUnqualifiedType(
       context.insts().Get(base_id).type_id());
-  auto result_type_id = SemIR::TypeId::Error;
+  auto result_type_id = SemIR::ErrorInst::SingletonTypeId;
   if (auto pointer_type =
           context.types().TryGetAs<SemIR::PointerType>(type_id)) {
     result_type_id = pointer_type->pointee_id;
-  } else if (type_id != SemIR::TypeId::Error) {
+  } else if (type_id != SemIR::ErrorInst::SingletonTypeId) {
     diagnose_not_pointer(type_id);
   }
   return context.AddInst<SemIR::Deref>(

+ 3 - 3
toolchain/docs/adding_features.md

@@ -280,12 +280,12 @@ If the resulting SemIR needs a new instruction:
 
         -   Set `.is_type = InstIsType::Always` in its `Kind` definition.
         -   When constructing instructions of this kind, pass
-            `SemIR::TypeId::TypeType` in as the value of the `type_id` field, as
-            in:
+            `SemIR::TypeType::SingletonTypeId` in as the value of the `type_id`
+            field, as in:
 
             ```
             SemIR::InstId inst_id = context.AddInst<SemIR::NewInstKindName>(
-                node_id, {.type_id = SemIR::TypeId::TypeType, ...});
+                node_id, {.type_id = SemIR::TypeType::SingletonTypeId, ...});
             ```
 
     -   Although most instructions have distinct types represented by

+ 3 - 3
toolchain/sem_ir/class.cpp

@@ -17,7 +17,7 @@ static auto GetFoundationType(const File& file, SpecificId specific_id,
     return TypeId::Invalid;
   }
   if (inst_id == SemIR::InstId::BuiltinErrorInst) {
-    return TypeId::Error;
+    return ErrorInst::SingletonTypeId;
   }
   return TypeId::ForTypeConstant(GetConstantValueInSpecific(
       file, specific_id,
@@ -41,8 +41,8 @@ auto Class::GetObjectRepr(const File& file, SpecificId specific_id) const
   }
   auto witness_id =
       GetConstantValueInSpecific(file, specific_id, complete_type_witness_id);
-  if (witness_id == ConstantId::Error) {
-    return TypeId::Error;
+  if (witness_id == ErrorInst::SingletonConstantId) {
+    return ErrorInst::SingletonTypeId;
   }
   return file.insts()
       .GetAs<CompleteTypeWitness>(file.constant_values().GetInstId(witness_id))

+ 6 - 4
toolchain/sem_ir/file.cpp

@@ -36,10 +36,12 @@ File::File(CheckIRId check_ir_id,
       inst_blocks_(allocator_),
       constants_(this) {
   // `type` and the error type are both complete types.
-  types_.SetValueRepr(TypeId::TypeType,
-                      {.kind = ValueRepr::Copy, .type_id = TypeId::TypeType});
-  types_.SetValueRepr(TypeId::Error,
-                      {.kind = ValueRepr::Copy, .type_id = TypeId::Error});
+  types_.SetValueRepr(
+      TypeType::SingletonTypeId,
+      {.kind = ValueRepr::Copy, .type_id = TypeType::SingletonTypeId});
+  types_.SetValueRepr(
+      ErrorInst::SingletonTypeId,
+      {.kind = ValueRepr::Copy, .type_id = ErrorInst::SingletonTypeId});
 
   insts_.Reserve(SingletonInstKinds.size());
   for (auto kind : SingletonInstKinds) {

+ 1 - 1
toolchain/sem_ir/function.cpp

@@ -36,7 +36,7 @@ auto GetCalleeFunction(const File& sem_ir, InstId callee_id) -> CalleeFunction {
   auto val_inst = sem_ir.insts().Get(val_id);
   auto struct_val = val_inst.TryAs<StructValue>();
   if (!struct_val) {
-    result.is_error = val_inst.type_id() == SemIR::TypeId::Error;
+    result.is_error = val_inst.type_id() == SemIR::ErrorInst::SingletonTypeId;
     return result;
   }
   auto fn_type = sem_ir.types().TryGetAs<FunctionType>(struct_val->type_id);

+ 4 - 3
toolchain/sem_ir/ids.cpp

@@ -5,6 +5,7 @@
 #include "toolchain/sem_ir/ids.h"
 
 #include "toolchain/sem_ir/singleton_insts.h"
+#include "toolchain/sem_ir/typed_insts.h"
 
 namespace Carbon::SemIR {
 
@@ -127,11 +128,11 @@ auto InstBlockId::Print(llvm::raw_ostream& out) const -> void {
 
 auto TypeId::Print(llvm::raw_ostream& out) const -> void {
   out << "type";
-  if (*this == TypeType) {
+  if (*this == TypeType::SingletonTypeId) {
     out << "TypeType";
-  } else if (*this == AutoType) {
+  } else if (*this == AutoType::SingletonTypeId) {
     out << "AutoType";
-  } else if (*this == Error) {
+  } else if (*this == ErrorInst::SingletonTypeId) {
     out << "Error";
   } else {
     out << "(";

+ 0 - 19
toolchain/sem_ir/ids.h

@@ -113,9 +113,6 @@ constexpr InstId InstId::PackageNamespace = InstId(BuiltinInstKind::ValidCount);
 struct ConstantId : public IdBase, public Printable<ConstantId> {
   // An ID for an expression that is not constant.
   static const ConstantId NotConstant;
-  // An ID for an expression whose phase cannot be determined because it
-  // contains an error. This is always modeled as a template constant.
-  static const ConstantId Error;
   // An explicitly invalid ID.
   static const ConstantId Invalid;
 
@@ -183,8 +180,6 @@ struct ConstantId : public IdBase, public Printable<ConstantId> {
 };
 
 constexpr ConstantId ConstantId::NotConstant = ConstantId(NotConstantIndex);
-constexpr ConstantId ConstantId::Error =
-    ConstantId::ForTemplateConstant(InstId::BuiltinErrorInst);
 constexpr ConstantId ConstantId::Invalid = ConstantId(InvalidIndex);
 
 // The ID of a EntityName.
@@ -636,15 +631,6 @@ struct TypeId : public IdBase, public Printable<TypeId> {
   // `InstIdAsType` or `TypeOfInstId` as the diagnostic argument type.
   using DiagnosticType = DiagnosticTypeInfo<std::string>;
 
-  // The builtin TypeType.
-  static const TypeId TypeType;
-
-  // The builtin placeholder type for patterns with deduced types.
-  static const TypeId AutoType;
-
-  // The builtin Error.
-  static const TypeId Error;
-
   // An explicitly invalid ID.
   static const TypeId Invalid;
 
@@ -663,11 +649,6 @@ struct TypeId : public IdBase, public Printable<TypeId> {
   auto Print(llvm::raw_ostream& out) const -> void;
 };
 
-constexpr TypeId TypeId::TypeType = TypeId::ForTypeConstant(
-    ConstantId::ForTemplateConstant(InstId::BuiltinTypeType));
-constexpr TypeId TypeId::AutoType = TypeId::ForTypeConstant(
-    ConstantId::ForTemplateConstant(InstId::BuiltinAutoType));
-constexpr TypeId TypeId::Error = TypeId::ForTypeConstant(ConstantId::Error);
 constexpr TypeId TypeId::Invalid = TypeId(InvalidIndex);
 
 // The ID of a type block.

+ 2 - 2
toolchain/sem_ir/inst.h

@@ -134,8 +134,8 @@ class Inst : public Printable<Inst> {
     // Error uses a self-referential type so that it's not accidentally treated
     // as a normal type. Every other builtin is a type, including the
     // self-referential TypeType.
-    auto type_id =
-        kind == InstKind::ErrorInst ? TypeId::Error : TypeId::TypeType;
+    auto type_id = kind == InstKind::ErrorInst ? ErrorInst::SingletonTypeId
+                                               : TypeType::SingletonTypeId;
     return Inst(kind, type_id, InstId::InvalidIndex, InstId::InvalidIndex);
   }
 

+ 9 - 0
toolchain/sem_ir/typed_insts.h

@@ -19,6 +19,7 @@
 // - Either a `Kind` constant, or a `Kinds` constant and an `InstKind kind;`
 //   member. These are described below.
 // - Optionally, a `SingletonInstId` if it is a singleton instruction.
+//   Similarly, there may be `SingletonConstantId` and `SingletonTypeId`.
 // - Optionally, a `TypeId type_id;` member, for instructions that produce a
 //   value. This includes instructions that produce an abstract value, such as a
 //   `Namespace`, for which a placeholder type should be used.
@@ -55,6 +56,8 @@ struct AutoType {
        .is_type = InstIsType::Always,
        .constant_kind = InstConstantKind::Always});
   static constexpr auto SingletonInstId = MakeSingletonInstId<Kind>();
+  static constexpr auto SingletonTypeId =
+      TypeId::ForTypeConstant(ConstantId::ForTemplateConstant(SingletonInstId));
 
   TypeId type_id;
 };
@@ -587,6 +590,10 @@ struct ErrorInst {
        .is_type = InstIsType::Always,
        .constant_kind = InstConstantKind::Always});
   static constexpr auto SingletonInstId = MakeSingletonInstId<Kind>();
+  static constexpr auto SingletonConstantId =
+      ConstantId::ForTemplateConstant(SingletonInstId);
+  static constexpr auto SingletonTypeId =
+      TypeId::ForTypeConstant(SingletonConstantId);
 
   TypeId type_id;
 };
@@ -1372,6 +1379,8 @@ struct TypeType {
        .is_type = InstIsType::Always,
        .constant_kind = InstConstantKind::Always});
   static constexpr auto SingletonInstId = MakeSingletonInstId<Kind>();
+  static constexpr auto SingletonTypeId =
+      TypeId::ForTypeConstant(ConstantId::ForTemplateConstant(SingletonInstId));
 
   TypeId type_id;
 };