Преглед на файлове

Convert remaining type-value InstId fields to TypeInstId (#5294)

After #5280 there are a few more typed instructions that have an `InstId
type_inst_id` that always holds a type value. These are converted to
`TypeInstId` to encode this fact in the type system. The
`ConvertAggregateElement()` function in convert.cpp is now able to
receive `TypeInstId` for a couple arguments as well.

Additionally, the `type_inst_id` field of `StructTypeField` is made into
a `TypeInstId`.

The `TupleType::elements_id` is renamed to `TupleType::type_elements_id`
to try record the fact that it's an InstBlock of type value
instructions. We don't introduce a TypeInstBlockId at this time, but it
might be nice to make blocks of TypeInstIds in the future.

To assist in working with a block of InstId that are type values, two
additional helpers are added to the TypeStore:
- GetBlockAsTypeInstIds which turns an `ArrayRef<InstId>` into a range
of `TypeInstId`
- GetBlockAsTypeIds which turns an `ArrayRef<InstId>` into a range of
`TypeId`

We use these helpers in places that iterate over the
`TupleType::type_elements_id`.
Dana Jansens преди 1 година
родител
ревизия
c34a8d0a3a

+ 12 - 1
toolchain/check/action.cpp

@@ -47,14 +47,25 @@ auto OperandIsDependent(Context& context, SemIR::InstId inst_id) -> bool {
          OperandIsDependent(context, context.constant_values().Get(inst_id));
 }
 
+auto OperandIsDependent(Context& context, SemIR::TypeInstId inst_id) -> bool {
+  // An instruction operand makes the instruction dependent if its type or
+  // constant value is dependent. TypeInstId has type `TypeType` which is
+  // concrete, so we only need to look at the constant value.
+  return OperandIsDependent(context, context.constant_values().Get(inst_id));
+}
+
 static auto OperandIsDependent(Context& context, SemIR::Inst::ArgAndKind arg)
     -> bool {
   CARBON_KIND_SWITCH(arg) {
+    case CARBON_KIND(SemIR::InstId inst_id): {
+      return OperandIsDependent(context, inst_id);
+    }
+
     case CARBON_KIND(SemIR::MetaInstId inst_id): {
       return OperandIsDependent(context, inst_id);
     }
 
-    case CARBON_KIND(SemIR::InstId inst_id): {
+    case CARBON_KIND(SemIR::TypeInstId inst_id): {
       return OperandIsDependent(context, inst_id);
     }
 

+ 4 - 0
toolchain/check/action.h

@@ -36,6 +36,10 @@ auto ActionIsDependent(Context& context, SemIR::Inst action_inst) -> bool;
 // in a way that means the action cannot be performed immediately.
 auto OperandIsDependent(Context& context, SemIR::InstId inst_id) -> bool;
 
+// Determines whether the given action operand depends on a template parameter
+// in a way that means the action cannot be performed immediately.
+auto OperandIsDependent(Context& context, SemIR::TypeInstId inst_id) -> bool;
+
 // Determines whether the given type depends on a template parameter
 // in a way that means the action cannot be performed immediately.
 auto OperandIsDependent(Context& context, SemIR::TypeId type_id) -> bool;

+ 2 - 2
toolchain/check/class.cpp

@@ -115,7 +115,7 @@ static auto AddStructTypeFields(
     if (field_decl.type_id == SemIR::ErrorInst::SingletonTypeId) {
       struct_type_fields.push_back(
           {.name_id = field_decl.name_id,
-           .type_inst_id = SemIR::ErrorInst::SingletonInstId});
+           .type_inst_id = SemIR::ErrorInst::SingletonTypeInstId});
       continue;
     }
     auto unbound_element_type =
@@ -240,7 +240,7 @@ static auto CheckCompleteClassType(
         vtable_contents);
   }
 
-  auto struct_type_inst_id = AddInst<SemIR::StructType>(
+  auto struct_type_inst_id = AddTypeInst<SemIR::StructType>(
       context, node_id,
       {.type_id = SemIR::TypeType::SingletonTypeId,
        .fields_id =

+ 16 - 14
toolchain/check/convert.cpp

@@ -152,10 +152,10 @@ static auto MakeElementAccessInst(Context& context, SemIR::LocId loc_id,
 template <typename SourceAccessInstT, typename TargetAccessInstT>
 static auto ConvertAggregateElement(
     Context& context, SemIR::LocId loc_id, SemIR::InstId src_id,
-    SemIR::InstId src_elem_type_inst,
+    SemIR::TypeInstId src_elem_type_inst,
     llvm::ArrayRef<SemIR::InstId> src_literal_elems,
     ConversionTarget::Kind kind, SemIR::InstId target_id,
-    SemIR::InstId target_elem_type_inst, PendingBlock* target_block,
+    SemIR::TypeInstId target_elem_type_inst, PendingBlock* target_block,
     size_t src_field_index, size_t target_field_index,
     SemIR::InstId vtable_id = SemIR::InstId::None) -> SemIR::InstId {
   auto src_elem_type =
@@ -196,7 +196,7 @@ static auto ConvertTupleToArray(Context& context, SemIR::TupleType tuple_type,
                                 SemIR::InstId value_id, ConversionTarget target)
     -> SemIR::InstId {
   auto& sem_ir = context.sem_ir();
-  auto tuple_elem_types = sem_ir.inst_blocks().Get(tuple_type.elements_id);
+  auto tuple_elem_types = sem_ir.inst_blocks().Get(tuple_type.type_elements_id);
 
   auto value = sem_ir.insts().Get(value_id);
   auto value_loc_id = sem_ir.insts().GetLocId(value_id);
@@ -263,7 +263,8 @@ static auto ConvertTupleToArray(Context& context, SemIR::TupleType tuple_type,
   // if initializing from a tuple literal.
   llvm::SmallVector<SemIR::InstId> inits;
   inits.reserve(*array_bound + 1);
-  for (auto [i, src_type_inst_id] : llvm::enumerate(tuple_elem_types)) {
+  for (auto [i, src_type_inst_id] : llvm::enumerate(
+           context.types().GetBlockAsTypeInstIds(tuple_elem_types))) {
     // TODO: This call recurses back into conversion. Switch to an iterative
     // approach.
     auto init_id =
@@ -294,8 +295,8 @@ static auto ConvertTupleToTuple(Context& context, SemIR::TupleType src_type,
                                 SemIR::InstId value_id, ConversionTarget target)
     -> SemIR::InstId {
   auto& sem_ir = context.sem_ir();
-  auto src_elem_types = sem_ir.inst_blocks().Get(src_type.elements_id);
-  auto dest_elem_types = sem_ir.inst_blocks().Get(dest_type.elements_id);
+  auto src_elem_types = sem_ir.inst_blocks().Get(src_type.type_elements_id);
+  auto dest_elem_types = sem_ir.inst_blocks().Get(dest_type.type_elements_id);
 
   auto value = sem_ir.insts().Get(value_id);
   auto value_loc_id = sem_ir.insts().GetLocId(value_id);
@@ -347,8 +348,9 @@ static auto ConvertTupleToTuple(Context& context, SemIR::TupleType src_type,
           : SemIR::CopyOnWriteInstBlock(
                 &sem_ir, SemIR::CopyOnWriteInstBlock::UninitializedBlock{
                              src_elem_types.size()});
-  for (auto [i, src_type_inst_id, dest_type_inst_id] :
-       llvm::enumerate(src_elem_types, dest_elem_types)) {
+  for (auto [i, src_type_inst_id, dest_type_inst_id] : llvm::enumerate(
+           context.types().GetBlockAsTypeInstIds(src_elem_types),
+           context.types().GetBlockAsTypeInstIds(dest_elem_types))) {
     // TODO: This call recurses back into conversion. Switch to an iterative
     // approach.
     auto init_id =
@@ -1078,12 +1080,12 @@ static auto PerformBuiltinConversion(
         // `FacetAccessType` instruction.
         auto type_inst_id = SemIR::TypeInstId::None;
         if (sem_ir.types().Is<SemIR::FacetType>(value_type_id)) {
-          type_inst_id = context.types().GetAsTypeInstId(
-              AddInst(context, loc_id,
-                      SemIR::FacetAccessType{
-                          .type_id = SemIR::TypeType::SingletonTypeId,
-                          .facet_value_inst_id = const_value_id,
-                      }));
+          type_inst_id =
+              AddTypeInst(context, loc_id,
+                          SemIR::FacetAccessType{
+                              .type_id = SemIR::TypeType::SingletonTypeId,
+                              .facet_value_inst_id = const_value_id,
+                          });
         } else {
           type_inst_id = context.types().GetAsTypeInstId(const_value_id);
         }

+ 8 - 9
toolchain/check/handle_impl.cpp

@@ -109,11 +109,11 @@ auto HandleParseNode(Context& context, Parse::DefaultSelfImplAsId node_id)
   // is a class and found its `Self`, so additionally performing an unqualified
   // name lookup would be redundant work, but would avoid duplicating the
   // handling of the `Self` expression.
-  auto self_inst_id = context.types().GetAsTypeInstId(AddInst(
+  auto self_inst_id = AddTypeInst(
       context, node_id,
       SemIR::NameRef{.type_id = SemIR::TypeType::SingletonTypeId,
                      .name_id = SemIR::NameId::SelfType,
-                     .value_id = context.types().GetInstId(self_type_id)}));
+                     .value_id = context.types().GetInstId(self_type_id)});
 
   // There's no need to push `Self` into scope here, because we can find it in
   // the parent class scope.
@@ -486,13 +486,12 @@ static auto BuildImplDecl(Context& context, Parse::AnyImplDeclId node_id,
       introducer.modifier_set.HasAnyOf(KeywordModifierSet::Extend)) {
     auto extend_node = introducer.modifier_node_id(ModifierOrder::Decl);
     if (impl_info.generic_id.has_value()) {
-      constraint_type_inst_id =
-          context.types().GetAsTypeInstId(AddInst<SemIR::SpecificConstant>(
-              context, context.insts().GetLocId(constraint_type_inst_id),
-              {.type_id = SemIR::TypeType::SingletonTypeId,
-               .inst_id = constraint_type_inst_id,
-               .specific_id =
-                   context.generics().GetSelfSpecific(impl_info.generic_id)}));
+      constraint_type_inst_id = AddTypeInst<SemIR::SpecificConstant>(
+          context, context.insts().GetLocId(constraint_type_inst_id),
+          {.type_id = SemIR::TypeType::SingletonTypeId,
+           .inst_id = constraint_type_inst_id,
+           .specific_id =
+               context.generics().GetSelfSpecific(impl_info.generic_id)});
     }
     if (!ExtendImpl(context, extend_node, node_id, impl_decl.impl_id,
                     self_type_node, self_type_id, name.implicit_params_loc_id,

+ 1 - 2
toolchain/check/handle_struct.cpp

@@ -67,8 +67,7 @@ auto HandleParseNode(Context& context, Parse::StructLiteralFieldId node_id)
 auto HandleParseNode(Context& context,
                      Parse::StructTypeLiteralFieldId /*node_id*/) -> bool {
   auto [type_node, type_id] = context.node_stack().PopExprWithNodeId();
-  SemIR::InstId cast_type_inst_id =
-      ExprAsType(context, type_node, type_id).inst_id;
+  auto cast_type_inst_id = ExprAsType(context, type_node, type_id).inst_id;
   // Get the name while leaving it on the stack.
   auto name_id = context.node_stack().Peek<Parse::NodeCategory::MemberName>();
 

+ 15 - 11
toolchain/check/import_ref.cpp

@@ -1897,7 +1897,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
   CARBON_CHECK(resolver.import_types().GetInstId(inst.type_id) ==
                SemIR::WitnessType::SingletonInstId);
   auto object_repr_type_inst_id =
-      GetLocalConstantInstId(resolver, inst.object_repr_type_inst_id);
+      GetLocalTypeInstId(resolver, inst.object_repr_type_inst_id);
   if (resolver.HasNewWork()) {
     return ResolveResult::Retry();
   }
@@ -2682,7 +2682,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
                SemIR::WitnessType::SingletonInstId);
 
   auto complete_type_inst_id =
-      GetLocalConstantInstId(resolver, inst.complete_type_inst_id);
+      GetLocalTypeInstId(resolver, inst.complete_type_inst_id);
   if (resolver.HasNewWork()) {
     return ResolveResult::Retry();
   }
@@ -2734,11 +2734,11 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
                                 SemIR::StructType inst) -> ResolveResult {
   CARBON_CHECK(inst.type_id == SemIR::TypeType::SingletonTypeId);
   auto orig_fields = resolver.import_struct_type_fields().Get(inst.fields_id);
-  llvm::SmallVector<SemIR::InstId> field_type_inst_ids;
+  llvm::SmallVector<SemIR::TypeInstId> field_type_inst_ids;
   field_type_inst_ids.reserve(orig_fields.size());
   for (auto field : orig_fields) {
     field_type_inst_ids.push_back(
-        GetLocalConstantInstId(resolver, field.type_inst_id));
+        GetLocalTypeInstId(resolver, field.type_inst_id));
   }
   if (resolver.HasNewWork()) {
     return ResolveResult::Retry();
@@ -2780,11 +2780,15 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
                                 SemIR::TupleType inst) -> ResolveResult {
   CARBON_CHECK(inst.type_id == SemIR::TypeType::SingletonTypeId);
 
-  auto orig_element_ids = resolver.import_inst_blocks().Get(inst.elements_id);
-  llvm::SmallVector<SemIR::InstId> element_ids;
-  element_ids.reserve(orig_element_ids.size());
-  for (auto elem_type_inst_id : orig_element_ids) {
-    element_ids.push_back(GetLocalConstantInstId(resolver, elem_type_inst_id));
+  auto orig_type_inst_ids =
+      resolver.import_inst_blocks().Get(inst.type_elements_id);
+  // TODO: It might be nice to make the `InstBlock` in `TupleType` record in the
+  // type system that its holding `TypeInstId` elements.
+  llvm::SmallVector<SemIR::InstId> type_inst_ids;
+  type_inst_ids.reserve(orig_type_inst_ids.size());
+  for (auto elem_type_inst_id :
+       resolver.import_ir().types().GetBlockAsTypeInstIds(orig_type_inst_ids)) {
+    type_inst_ids.push_back(GetLocalTypeInstId(resolver, elem_type_inst_id));
   }
   if (resolver.HasNewWork()) {
     return ResolveResult::Retry();
@@ -2792,8 +2796,8 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
 
   return ResolveAs<SemIR::TupleType>(
       resolver, {.type_id = SemIR::TypeType::SingletonTypeId,
-                 .elements_id = GetLocalCanonicalInstBlockId(
-                     resolver, inst.elements_id, element_ids)});
+                 .type_elements_id = GetLocalCanonicalInstBlockId(
+                     resolver, inst.type_elements_id, type_inst_ids)});
 }
 
 static auto TryResolveTypedInst(ImportRefResolver& resolver,

+ 9 - 0
toolchain/check/inst.h

@@ -24,6 +24,15 @@ auto AddInst(Context& context, LocT loc, InstT inst) -> SemIR::InstId {
   return AddInst(context, SemIR::LocIdAndInst(loc, inst));
 }
 
+// Like AddInst, but for instructions with a type_id of `TypeType`, which is
+// encoded in the return type of `TypeInstId`.
+template <typename InstT, typename LocT>
+  requires(!InstT::Kind.has_cleanup())
+auto AddTypeInst(Context& context, LocT loc, InstT inst) -> SemIR::TypeInstId {
+  return context.types().GetAsTypeInstId(
+      AddInst(context, SemIR::LocIdAndInst(loc, inst)));
+}
+
 // Pushes a parse tree node onto the stack, storing the SemIR::Inst as the
 // result.
 //

+ 1 - 1
toolchain/check/member_access.cpp

@@ -798,7 +798,7 @@ auto PerformTupleAccess(Context& context, SemIR::LocId loc_id,
 
   auto index_literal = context.insts().GetAs<SemIR::IntValue>(
       context.constant_values().GetInstId(index_const_id));
-  auto type_block = context.inst_blocks().Get(tuple_type->elements_id);
+  auto type_block = context.inst_blocks().Get(tuple_type->type_elements_id);
   std::optional<llvm::APInt> index_val = ValidateTupleIndex(
       context, loc_id, tuple_inst_id, index_literal, type_block.size());
   if (!index_val) {

+ 8 - 9
toolchain/check/pattern_match.cpp

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

+ 4 - 2
toolchain/check/subst.cpp

@@ -195,8 +195,10 @@ static auto PopOperand(Context& context, Worklist& worklist,
       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_inst_id = worklist.Pop()});
+        new_fields.Set(
+            i,
+            {.name_id = old_fields[i].name_id,
+             .type_inst_id = context.types().GetAsTypeInstId(worklist.Pop())});
       }
       return new_fields.GetCanonical().index;
     }

+ 3 - 5
toolchain/check/type_completion.cpp

@@ -258,7 +258,7 @@ auto TypeCompleter::AddNestedIncompleteTypes(SemIR::Inst type_inst) -> bool {
     }
     case CARBON_KIND(SemIR::TupleType inst): {
       for (auto element_type_id :
-           context_->inst_blocks().Get(inst.elements_id)) {
+           context_->inst_blocks().Get(inst.type_elements_id)) {
         Push(context_->types().GetTypeIdForTypeInstId(element_type_id));
       }
       break;
@@ -430,7 +430,7 @@ auto TypeCompleter::BuildInfoForInst(SemIR::TypeId type_id,
                                      SemIR::TupleType tuple_type) const
     -> SemIR::CompleteTypeInfo {
   // TODO: Share more code with structs.
-  auto elements = context_->inst_blocks().Get(tuple_type.elements_id);
+  auto elements = context_->inst_blocks().Get(tuple_type.type_elements_id);
   if (elements.empty()) {
     return {.value_repr = MakeEmptyValueRepr()};
   }
@@ -441,9 +441,7 @@ auto TypeCompleter::BuildInfoForInst(SemIR::TypeId type_id,
   value_rep_elements.reserve(elements.size());
   bool same_as_object_rep = true;
   SemIR::ClassId abstract_class_id = SemIR::ClassId::None;
-  for (auto element_type_inst_id : elements) {
-    auto element_type_id =
-        context_->types().GetTypeIdForTypeInstId(element_type_inst_id);
+  for (auto element_type_id : context_->types().GetBlockAsTypeIds(elements)) {
     auto element_info = GetNestedInfo(element_type_id);
     if (!element_info.value_repr.IsCopyOfObjectRepr(context_->sem_ir(),
                                                     element_type_id)) {

+ 2 - 2
toolchain/check/type_structure.cpp

@@ -280,13 +280,13 @@ class TypeStructureBuilder {
         }
         case CARBON_KIND(SemIR::TupleType tuple_type): {
           auto inner_types =
-              context_->inst_blocks().Get(tuple_type.elements_id);
+              context_->inst_blocks().Get(tuple_type.type_elements_id);
           if (inner_types.empty()) {
             AppendStructural(TypeStructure::Structural::Concrete);
           } else {
             AppendStructural(TypeStructure::Structural::ConcreteOpenParen);
             Push(CloseType());
-            PushArgs(context_->inst_blocks().Get(tuple_type.elements_id));
+            PushArgs(context_->inst_blocks().Get(tuple_type.type_elements_id));
           }
           break;
         }

+ 3 - 4
toolchain/lower/file_context.cpp

@@ -624,12 +624,11 @@ static auto BuildTypeForInst(FileContext& context, SemIR::TupleType inst)
   // can be collectively replaced with LLVM's void, particularly around
   // function returns. LLVM doesn't allow declaring variables with a void
   // type, so that may require significant special casing.
-  auto elements = context.sem_ir().inst_blocks().Get(inst.elements_id);
+  auto elements = context.sem_ir().inst_blocks().Get(inst.type_elements_id);
   llvm::SmallVector<llvm::Type*> subtypes;
   subtypes.reserve(elements.size());
-  for (auto element_id : elements) {
-    subtypes.push_back(context.GetType(
-        context.sem_ir().types().GetTypeIdForTypeInstId(element_id)));
+  for (auto type_id : context.sem_ir().types().GetBlockAsTypeIds(elements)) {
+    subtypes.push_back(context.GetType(type_id));
   }
   return llvm::StructType::get(context.llvm_context(), subtypes);
 }

+ 1 - 1
toolchain/sem_ir/builtin_function_kind.cpp

@@ -72,7 +72,7 @@ struct NoReturn {
     if (!tuple) {
       return false;
     }
-    return sem_ir.inst_blocks().Get(tuple->elements_id).empty();
+    return sem_ir.inst_blocks().Get(tuple->type_elements_id).empty();
   }
 };
 

+ 1 - 1
toolchain/sem_ir/inst_namer.cpp

@@ -909,7 +909,7 @@ auto InstNamer::CollectNamesInBlock(ScopeId top_scope_id,
         continue;
       }
       case CARBON_KIND(TupleType inst): {
-        if (inst.elements_id == InstBlockId::Empty) {
+        if (inst.type_elements_id == InstBlockId::Empty) {
           add_inst_name("empty_tuple.type");
         } else {
           add_inst_name("tuple.type");

+ 1 - 1
toolchain/sem_ir/stringify.cpp

@@ -587,7 +587,7 @@ class Stringifier {
   }
 
   auto StringifyInst(SemIR::InstId /*inst_id*/, TupleType inst) -> void {
-    auto refs = sem_ir_->inst_blocks().Get(inst.elements_id);
+    auto refs = sem_ir_->inst_blocks().Get(inst.type_elements_id);
     if (refs.empty()) {
       *out_ << "()";
       return;

+ 1 - 1
toolchain/sem_ir/struct_type_field.h

@@ -21,7 +21,7 @@ struct StructTypeField : Printable<StructTypeField> {
   }
 
   NameId name_id;
-  InstId type_inst_id;
+  TypeInstId type_inst_id;
 };
 
 using StructTypeFieldsStore = BlockValueStore<StructTypeFieldsId>;

+ 19 - 0
toolchain/sem_ir/type.h

@@ -5,6 +5,7 @@
 #ifndef CARBON_TOOLCHAIN_SEM_IR_TYPE_H_
 #define CARBON_TOOLCHAIN_SEM_IR_TYPE_H_
 
+#include "llvm/ADT/STLExtras.h"
 #include "toolchain/base/shared_value_stores.h"
 #include "toolchain/sem_ir/constant.h"
 #include "toolchain/sem_ir/ids.h"
@@ -63,6 +64,24 @@ class TypeStore : public Yaml::Printable<TypeStore> {
   // Returns the instruction used to define the specified type.
   auto GetAsInst(TypeId type_id) const -> Inst;
 
+  // Converts an ArrayRef of `InstId`s to a range of `TypeInstId`s via
+  // GetAsTypeInstId().
+  auto GetBlockAsTypeInstIds(llvm::ArrayRef<InstId> array
+                             [[clang::lifetimebound]]) const -> auto {
+    return llvm::map_range(array, [&](SemIR::InstId type_inst_id) {
+      return GetAsTypeInstId(type_inst_id);
+    });
+  }
+
+  // Converts an ArrayRef of `InstId`s to a range of `TypeId`s via
+  // GetTypeIdForTypeInstId().
+  auto GetBlockAsTypeIds(llvm::ArrayRef<InstId> array
+                         [[clang::lifetimebound]]) const -> auto {
+    return llvm::map_range(array, [&](SemIR::InstId type_inst_id) {
+      return GetTypeIdForTypeInstId(type_inst_id);
+    });
+  }
+
   // Returns whether the specified kind of instruction was used to define the
   // type.
   template <typename InstT>

+ 5 - 5
toolchain/sem_ir/typed_insts.h

@@ -599,7 +599,7 @@ struct CompleteTypeWitness {
   // Always the builtin witness type.
   TypeId type_id;
   // The type that is used as the object representation of this type.
-  InstId object_repr_type_inst_id;
+  TypeInstId object_repr_type_inst_id;
 };
 
 // Indicates `const` on a type, such as `var x: const i32`.
@@ -626,7 +626,7 @@ struct ConvertToValueAction {
 
   TypeId type_id;
   MetaInstId inst_id;
-  InstId target_type_inst_id;
+  TypeInstId target_type_inst_id;
 };
 
 // Records that a type conversion `original as new_type` was done, producing the
@@ -1327,7 +1327,7 @@ struct RefineTypeAction {
 
   TypeId type_id;
   MetaInstId inst_id;
-  InstId inst_type_inst_id;
+  TypeInstId inst_type_inst_id;
 };
 
 // Requires a type to be complete. This is only created for generic types and
@@ -1347,7 +1347,7 @@ struct RequireCompleteType {
   // Always the builtin witness type.
   TypeId type_id;
   // The type that is required to be complete.
-  InstId complete_type_inst_id;
+  TypeInstId complete_type_inst_id;
 };
 
 struct Return {
@@ -1716,7 +1716,7 @@ struct TupleType {
        .deduce_through = true});
 
   TypeId type_id;
-  InstBlockId elements_id;
+  InstBlockId type_elements_id;
 };
 
 // A tuple value.