Przeglądaj źródła

Switch AddInst struct init style. (#4012)

Trying to conform with #4009. Changes SemIR::LocIdAndInst construction
to root out struct init cases with AddInst and related functions. I'm
using templating of AddInst functions in order to avoid `AddInst(loc_id,
InstName{...})` and instead have `AddInst<InstName>(loc_id, {...})` with
I think similar readability results. There are a couple cases where inst
construction is templated and so designated initializers couldn't be
used, so this may be better for those in particular due to the extra
type enforcement.

This probably doesn't clean up every last case, but I was trying to get
the bulk at once without bleeding over into less related changes.

---------

Co-authored-by: Richard Smith <richard@metafoo.co.uk>
Jon Ross-Perkins 1 rok temu
rodzic
commit
5bb318cae6

+ 10 - 7
toolchain/check/call.cpp

@@ -25,9 +25,10 @@ static auto PerformCallToGenericClass(Context& context, Parse::NodeId node_id,
       context, node_id, /*self_id=*/SemIR::InstId::Invalid, arg_ids,
       context, node_id, /*self_id=*/SemIR::InstId::Invalid, arg_ids,
       /*return_storage_id=*/SemIR::InstId::Invalid, class_info.decl_id,
       /*return_storage_id=*/SemIR::InstId::Invalid, class_info.decl_id,
       class_info.implicit_param_refs_id, class_info.param_refs_id);
       class_info.implicit_param_refs_id, class_info.param_refs_id);
-  return context.AddInst(
-      {node_id,
-       SemIR::ClassType{SemIR::TypeId::TypeType, class_id, converted_args_id}});
+  return context.AddInst<SemIR::ClassType>(node_id,
+                                           {.type_id = SemIR::TypeId::TypeType,
+                                            .class_id = class_id,
+                                            .args_id = converted_args_id});
 }
 }
 
 
 auto PerformCall(Context& context, Parse::NodeId node_id,
 auto PerformCall(Context& context, Parse::NodeId node_id,
@@ -73,8 +74,8 @@ auto PerformCall(Context& context, Parse::NodeId node_id,
     case SemIR::Function::ReturnSlot::Present:
     case SemIR::Function::ReturnSlot::Present:
       // Tentatively put storage for a temporary in the function's return slot.
       // Tentatively put storage for a temporary in the function's return slot.
       // This will be replaced if necessary when we perform initialization.
       // This will be replaced if necessary when we perform initialization.
-      return_storage_id = context.AddInst(
-          {node_id, SemIR::TemporaryStorage{callable.return_type_id}});
+      return_storage_id = context.AddInst<SemIR::TemporaryStorage>(
+          node_id, {.type_id = callable.return_type_id});
       break;
       break;
     case SemIR::Function::ReturnSlot::Absent:
     case SemIR::Function::ReturnSlot::Absent:
       break;
       break;
@@ -91,8 +92,10 @@ auto PerformCall(Context& context, Parse::NodeId node_id,
       ConvertCallArgs(context, node_id, callee_function.self_id, arg_ids,
       ConvertCallArgs(context, node_id, callee_function.self_id, arg_ids,
                       return_storage_id, callable.decl_id,
                       return_storage_id, callable.decl_id,
                       callable.implicit_param_refs_id, callable.param_refs_id);
                       callable.implicit_param_refs_id, callable.param_refs_id);
-  auto call_inst_id = context.AddInst(
-      {node_id, SemIR::Call{type_id, callee_id, converted_args_id}});
+  auto call_inst_id =
+      context.AddInst<SemIR::Call>(node_id, {.type_id = type_id,
+                                             .callee_id = callee_id,
+                                             .args_id = converted_args_id});
 
 
   return call_inst_id;
   return call_inst_id;
 }
 }

+ 4 - 4
toolchain/check/check.cpp

@@ -323,10 +323,10 @@ static auto InitPackageScopeAndImports(Context& context, UnitInfo& unit_info,
       SemIR::NameScopeId::Invalid);
       SemIR::NameScopeId::Invalid);
   CARBON_CHECK(package_scope_id == SemIR::NameScopeId::Package);
   CARBON_CHECK(package_scope_id == SemIR::NameScopeId::Package);
 
 
-  auto package_inst_id = context.AddInst(
-      {Parse::NodeId::Invalid,
-       SemIR::Namespace{namespace_type_id, SemIR::NameScopeId::Package,
-                        SemIR::InstId::Invalid}});
+  auto package_inst_id = context.AddInst<SemIR::Namespace>(
+      Parse::NodeId::Invalid, {.type_id = namespace_type_id,
+                               .name_scope_id = SemIR::NameScopeId::Package,
+                               .import_id = SemIR::InstId::Invalid});
   CARBON_CHECK(package_inst_id == SemIR::InstId::PackageNamespace);
   CARBON_CHECK(package_inst_id == SemIR::InstId::PackageNamespace);
 
 
   // If there is an implicit `api` import, set it first so that it uses the
   // If there is an implicit `api` import, set it first so that it uses the

+ 7 - 10
toolchain/check/context.cpp

@@ -114,11 +114,6 @@ auto Context::AddConstant(SemIR::Inst inst, bool is_symbolic)
   return const_id;
   return const_id;
 }
 }
 
 
-auto Context::AddInstAndPush(SemIR::LocIdAndInst loc_id_and_inst) -> void {
-  auto inst_id = AddInst(loc_id_and_inst);
-  node_stack_.Push(loc_id_and_inst.loc_id.node_id(), inst_id);
-}
-
 auto Context::ReplaceLocIdAndInstBeforeConstantUse(
 auto Context::ReplaceLocIdAndInstBeforeConstantUse(
     SemIR::InstId inst_id, SemIR::LocIdAndInst loc_id_and_inst) -> void {
     SemIR::InstId inst_id, SemIR::LocIdAndInst loc_id_and_inst) -> void {
   sem_ir().insts().SetLocIdAndInst(inst_id, loc_id_and_inst);
   sem_ir().insts().SetLocIdAndInst(inst_id, loc_id_and_inst);
@@ -479,7 +474,7 @@ static auto AddDominatedBlockAndBranchImpl(Context& context,
     return SemIR::InstBlockId::Unreachable;
     return SemIR::InstBlockId::Unreachable;
   }
   }
   auto block_id = context.inst_blocks().AddDefaultValue();
   auto block_id = context.inst_blocks().AddDefaultValue();
-  context.AddInst({node_id, BranchNode{block_id, args...}});
+  context.AddInst<BranchNode>(node_id, {block_id, args...});
   return block_id;
   return block_id;
 }
 }
 
 
@@ -512,7 +507,7 @@ auto Context::AddConvergenceBlockAndPush(Parse::NodeId node_id, int num_blocks)
       if (new_block_id == SemIR::InstBlockId::Unreachable) {
       if (new_block_id == SemIR::InstBlockId::Unreachable) {
         new_block_id = inst_blocks().AddDefaultValue();
         new_block_id = inst_blocks().AddDefaultValue();
       }
       }
-      AddInst({node_id, SemIR::Branch{new_block_id}});
+      AddInst<SemIR::Branch>(node_id, {.target_id = new_block_id});
     }
     }
     inst_block_stack().Pop();
     inst_block_stack().Pop();
   }
   }
@@ -530,7 +525,8 @@ auto Context::AddConvergenceBlockWithArgAndPush(
       if (new_block_id == SemIR::InstBlockId::Unreachable) {
       if (new_block_id == SemIR::InstBlockId::Unreachable) {
         new_block_id = inst_blocks().AddDefaultValue();
         new_block_id = inst_blocks().AddDefaultValue();
       }
       }
-      AddInst({node_id, SemIR::BranchWithArg{new_block_id, arg_id}});
+      AddInst<SemIR::BranchWithArg>(
+          node_id, {.target_id = new_block_id, .arg_id = arg_id});
     }
     }
     inst_block_stack().Pop();
     inst_block_stack().Pop();
   }
   }
@@ -538,7 +534,8 @@ auto Context::AddConvergenceBlockWithArgAndPush(
 
 
   // Acquire the result value.
   // Acquire the result value.
   SemIR::TypeId result_type_id = insts().Get(*block_args.begin()).type_id();
   SemIR::TypeId result_type_id = insts().Get(*block_args.begin()).type_id();
-  return AddInst({node_id, SemIR::BlockArg{result_type_id, new_block_id}});
+  return AddInst<SemIR::BlockArg>(
+      node_id, {.type_id = result_type_id, .block_id = new_block_id});
 }
 }
 
 
 auto Context::SetBlockArgResultBeforeConstantUse(SemIR::InstId select_id,
 auto Context::SetBlockArgResultBeforeConstantUse(SemIR::InstId select_id,
@@ -616,7 +613,7 @@ auto Context::is_current_position_reachable() -> bool {
 auto Context::FinalizeGlobalInit() -> void {
 auto Context::FinalizeGlobalInit() -> void {
   inst_block_stack().PushGlobalInit();
   inst_block_stack().PushGlobalInit();
   if (!inst_block_stack().PeekCurrentBlockContents().empty()) {
   if (!inst_block_stack().PeekCurrentBlockContents().empty()) {
-    AddInst({Parse::NodeId::Invalid, SemIR::Return{}});
+    AddInst<SemIR::Return>(Parse::NodeId::Invalid, {});
     // Pop the GlobalInit block here to finalize it.
     // Pop the GlobalInit block here to finalize it.
     inst_block_stack().Pop();
     inst_block_stack().Pop();
 
 

+ 18 - 1
toolchain/check/context.h

@@ -44,10 +44,22 @@ class Context {
   // Adds an instruction to the current block, returning the produced ID.
   // Adds an instruction to the current block, returning the produced ID.
   auto AddInst(SemIR::LocIdAndInst loc_id_and_inst) -> SemIR::InstId;
   auto AddInst(SemIR::LocIdAndInst loc_id_and_inst) -> SemIR::InstId;
 
 
+  // Convenience for AddInst on specific instruction types.
+  template <typename InstT, typename LocT>
+  auto AddInst(LocT loc_id, InstT inst) -> SemIR::InstId {
+    return AddInst(SemIR::LocIdAndInst(loc_id, inst));
+  }
+
   // Adds an instruction in no block, returning the produced ID. Should be used
   // Adds an instruction in no block, returning the produced ID. Should be used
   // rarely.
   // rarely.
   auto AddInstInNoBlock(SemIR::LocIdAndInst loc_id_and_inst) -> SemIR::InstId;
   auto AddInstInNoBlock(SemIR::LocIdAndInst loc_id_and_inst) -> SemIR::InstId;
 
 
+  // Convenience for AddInstInNoBlock on specific instruction types.
+  template <typename InstT, typename LocT>
+  auto AddInstInNoBlock(LocT loc_id, InstT inst) -> SemIR::InstId {
+    return AddInstInNoBlock(SemIR::LocIdAndInst(loc_id, inst));
+  }
+
   // Adds an instruction to the current block, returning the produced ID. The
   // Adds an instruction to the current block, returning the produced ID. The
   // instruction is a placeholder that is expected to be replaced by
   // instruction is a placeholder that is expected to be replaced by
   // `ReplaceInstBeforeConstantUse`.
   // `ReplaceInstBeforeConstantUse`.
@@ -64,7 +76,12 @@ class Context {
 
 
   // Pushes a parse tree node onto the stack, storing the SemIR::Inst as the
   // Pushes a parse tree node onto the stack, storing the SemIR::Inst as the
   // result. Only valid if the LocId is for a NodeId.
   // result. Only valid if the LocId is for a NodeId.
-  auto AddInstAndPush(SemIR::LocIdAndInst loc_id_and_inst) -> void;
+  template <typename InstT, typename LocT>
+  auto AddInstAndPush(LocT loc_id, InstT inst) -> void {
+    SemIR::LocIdAndInst arg(loc_id, inst);
+    auto inst_id = AddInst(arg);
+    node_stack_.Push(arg.loc_id.node_id(), inst_id);
+  }
 
 
   // Replaces the instruction `inst_id` with `loc_id_and_inst`. The instruction
   // Replaces the instruction `inst_id` with `loc_id_and_inst`. The instruction
   // is required to not have been used in any constant evaluation, either
   // is required to not have been used in any constant evaluation, either

+ 74 - 62
toolchain/check/convert.cpp

@@ -100,9 +100,10 @@ static auto FinalizeTemporary(Context& context, SemIR::InstId init_id,
         << "initialized multiple times? Have "
         << "initialized multiple times? Have "
         << sem_ir.insts().Get(return_slot_id);
         << sem_ir.insts().Get(return_slot_id);
     auto init = sem_ir.insts().Get(init_id);
     auto init = sem_ir.insts().Get(init_id);
-    return context.AddInst(
-        {sem_ir.insts().GetLocId(init_id),
-         SemIR::Temporary{init.type_id(), return_slot_id, init_id}});
+    return context.AddInst<SemIR::Temporary>(sem_ir.insts().GetLocId(init_id),
+                                             {.type_id = init.type_id(),
+                                              .storage_id = return_slot_id,
+                                              .init_id = init_id});
   }
   }
 
 
   if (discarded) {
   if (discarded) {
@@ -117,10 +118,11 @@ static auto FinalizeTemporary(Context& context, SemIR::InstId init_id,
   // instructions.
   // instructions.
   auto init = sem_ir.insts().Get(init_id);
   auto init = sem_ir.insts().Get(init_id);
   auto loc_id = sem_ir.insts().GetLocId(init_id);
   auto loc_id = sem_ir.insts().GetLocId(init_id);
-  auto temporary_id =
-      context.AddInst({loc_id, SemIR::TemporaryStorage{init.type_id()}});
-  return context.AddInst(
-      {loc_id, SemIR::Temporary{init.type_id(), temporary_id, init_id}});
+  auto temporary_id = context.AddInst<SemIR::TemporaryStorage>(
+      loc_id, {.type_id = init.type_id()});
+  return context.AddInst<SemIR::Temporary>(loc_id, {.type_id = init.type_id(),
+                                                    .storage_id = temporary_id,
+                                                    .init_id = init_id});
 }
 }
 
 
 // Materialize a temporary to hold the result of the given expression if it is
 // Materialize a temporary to hold the result of the given expression if it is
@@ -144,15 +146,14 @@ static auto MakeElementAccessInst(Context& context, SemIR::LocId loc_id,
     // TODO: Add a new instruction kind for indexing an array at a constant
     // TODO: Add a new instruction kind for indexing an array at a constant
     // index so that we don't need an integer literal instruction here, and
     // index so that we don't need an integer literal instruction here, and
     // remove this special case.
     // remove this special case.
-    auto index_id = block.AddInst(
-        {loc_id,
-         SemIR::IntLiteral{context.GetBuiltinType(SemIR::BuiltinKind::IntType),
-                           context.ints().Add(llvm::APInt(32, i))}});
-    return block.AddInst(
-        {loc_id, AccessInstT{elem_type_id, aggregate_id, index_id}});
+    auto index_id = block.template AddInst<SemIR::IntLiteral>(
+        loc_id, {.type_id = context.GetBuiltinType(SemIR::BuiltinKind::IntType),
+                 .int_id = context.ints().Add(llvm::APInt(32, i))});
+    return block.template AddInst<AccessInstT>(
+        loc_id, {elem_type_id, aggregate_id, index_id});
   } else {
   } else {
-    return block.AddInst({loc_id, AccessInstT{elem_type_id, aggregate_id,
-                                              SemIR::ElementIndex(i)}});
+    return block.template AddInst<AccessInstT>(
+        loc_id, {elem_type_id, aggregate_id, SemIR::ElementIndex(i)});
   }
   }
 }
 }
 
 
@@ -250,8 +251,8 @@ static auto ConvertTupleToArray(Context& context, SemIR::TupleType tuple_type,
   // destination for the array initialization if we weren't given one.
   // destination for the array initialization if we weren't given one.
   SemIR::InstId return_slot_id = target.init_id;
   SemIR::InstId return_slot_id = target.init_id;
   if (!target.init_id.is_valid()) {
   if (!target.init_id.is_valid()) {
-    return_slot_id = target_block->AddInst(
-        {value_loc_id, SemIR::TemporaryStorage{target.type_id}});
+    return_slot_id = target_block->AddInst<SemIR::TemporaryStorage>(
+        value_loc_id, {.type_id = target.type_id});
   }
   }
 
 
   // Initialize each element of the array from the corresponding element of the
   // Initialize each element of the array from the corresponding element of the
@@ -277,10 +278,10 @@ static auto ConvertTupleToArray(Context& context, SemIR::TupleType tuple_type,
   // Flush the temporary here if we didn't insert it earlier, so we can add a
   // Flush the temporary here if we didn't insert it earlier, so we can add a
   // reference to the return slot.
   // reference to the return slot.
   target_block->InsertHere();
   target_block->InsertHere();
-  return context.AddInst(
-      {value_loc_id,
-       SemIR::ArrayInit{target.type_id, sem_ir.inst_blocks().Add(inits),
-                        return_slot_id}});
+  return context.AddInst<SemIR::ArrayInit>(
+      value_loc_id, {.type_id = target.type_id,
+                     .inits_id = sem_ir.inst_blocks().Add(inits),
+                     .dest_id = return_slot_id});
 }
 }
 
 
 // Performs a conversion from a tuple to a tuple type. This function only
 // Performs a conversion from a tuple to a tuple type. This function only
@@ -357,12 +358,14 @@ static auto ConvertTupleToTuple(Context& context, SemIR::TupleType src_type,
 
 
   if (is_init) {
   if (is_init) {
     target.init_block->InsertHere();
     target.init_block->InsertHere();
-    return context.AddInst(
-        {value_loc_id,
-         SemIR::TupleInit{target.type_id, new_block.id(), target.init_id}});
+    return context.AddInst<SemIR::TupleInit>(value_loc_id,
+                                             {.type_id = target.type_id,
+                                              .elements_id = new_block.id(),
+                                              .dest_id = target.init_id});
   } else {
   } else {
-    return context.AddInst(
-        {value_loc_id, SemIR::TupleValue{target.type_id, new_block.id()}});
+    return context.AddInst<SemIR::TupleValue>(
+        value_loc_id,
+        {.type_id = target.type_id, .elements_id = new_block.id()});
   }
   }
 }
 }
 
 
@@ -488,17 +491,20 @@ static auto ConvertStructToStructOrClass(Context& context,
     target.init_block->InsertHere();
     target.init_block->InsertHere();
     CARBON_CHECK(is_init)
     CARBON_CHECK(is_init)
         << "Converting directly to a class value is not supported";
         << "Converting directly to a class value is not supported";
-    return context.AddInst(
-        {value_loc_id,
-         SemIR::ClassInit{target.type_id, new_block.id(), target.init_id}});
+    return context.AddInst<SemIR::ClassInit>(value_loc_id,
+                                             {.type_id = target.type_id,
+                                              .elements_id = new_block.id(),
+                                              .dest_id = target.init_id});
   } else if (is_init) {
   } else if (is_init) {
     target.init_block->InsertHere();
     target.init_block->InsertHere();
-    return context.AddInst(
-        {value_loc_id,
-         SemIR::StructInit{target.type_id, new_block.id(), target.init_id}});
+    return context.AddInst<SemIR::StructInit>(value_loc_id,
+                                              {.type_id = target.type_id,
+                                               .elements_id = new_block.id(),
+                                               .dest_id = target.init_id});
   } else {
   } else {
-    return context.AddInst(
-        {value_loc_id, SemIR::StructValue{target.type_id, new_block.id()}});
+    return context.AddInst<SemIR::StructValue>(
+        value_loc_id,
+        {.type_id = target.type_id, .elements_id = new_block.id()});
   }
   }
 }
 }
 
 
@@ -543,9 +549,8 @@ static auto ConvertStructToClass(Context& context, SemIR::StructType src_type,
   if (need_temporary) {
   if (need_temporary) {
     target.kind = ConversionTarget::Initializer;
     target.kind = ConversionTarget::Initializer;
     target.init_block = &target_block;
     target.init_block = &target_block;
-    target.init_id =
-        target_block.AddInst({context.insts().GetLocId(value_id),
-                              SemIR::TemporaryStorage{target.type_id}});
+    target.init_id = target_block.AddInst<SemIR::TemporaryStorage>(
+        context.insts().GetLocId(value_id), {.type_id = target.type_id});
   }
   }
 
 
   auto result_id = ConvertStructToStructOrClass<SemIR::ClassElementAccess>(
   auto result_id = ConvertStructToStructOrClass<SemIR::ClassElementAccess>(
@@ -553,9 +558,10 @@ static auto ConvertStructToClass(Context& context, SemIR::StructType src_type,
 
 
   if (need_temporary) {
   if (need_temporary) {
     target_block.InsertHere();
     target_block.InsertHere();
-    result_id = context.AddInst(
-        {context.insts().GetLocId(value_id),
-         SemIR::Temporary{target.type_id, target.init_id, result_id}});
+    result_id = context.AddInst<SemIR::Temporary>(
+        context.insts().GetLocId(value_id), {.type_id = target.type_id,
+                                             .storage_id = target.init_id,
+                                             .init_id = result_id});
   }
   }
   return result_id;
   return result_id;
 }
 }
@@ -610,9 +616,10 @@ static auto ConvertDerivedToBase(Context& context, SemIR::LocId loc_id,
   // Add a series of `.base` accesses.
   // Add a series of `.base` accesses.
   for (auto base_id : path) {
   for (auto base_id : path) {
     auto base_decl = context.insts().GetAs<SemIR::BaseDecl>(base_id);
     auto base_decl = context.insts().GetAs<SemIR::BaseDecl>(base_id);
-    value_id = context.AddInst(
-        {loc_id, SemIR::ClassElementAccess{base_decl.base_type_id, value_id,
-                                           base_decl.index}});
+    value_id = context.AddInst<SemIR::ClassElementAccess>(
+        loc_id, {.type_id = base_decl.base_type_id,
+                 .base_id = value_id,
+                 .index = base_decl.index});
   }
   }
   return value_id;
   return value_id;
 }
 }
@@ -624,14 +631,15 @@ static auto ConvertDerivedPointerToBasePointer(
     const InheritancePath& path) -> SemIR::InstId {
     const InheritancePath& path) -> SemIR::InstId {
   // Form `*p`.
   // Form `*p`.
   ptr_id = ConvertToValueExpr(context, ptr_id);
   ptr_id = ConvertToValueExpr(context, ptr_id);
-  auto ref_id =
-      context.AddInst({loc_id, SemIR::Deref{src_ptr_type.pointee_id, ptr_id}});
+  auto ref_id = context.AddInst<SemIR::Deref>(
+      loc_id, {.type_id = src_ptr_type.pointee_id, .pointer_id = ptr_id});
 
 
   // Convert as a reference expression.
   // Convert as a reference expression.
   ref_id = ConvertDerivedToBase(context, loc_id, ref_id, path);
   ref_id = ConvertDerivedToBase(context, loc_id, ref_id, path);
 
 
   // Take the address.
   // Take the address.
-  return context.AddInst({loc_id, SemIR::AddrOf{dest_ptr_type_id, ref_id}});
+  return context.AddInst<SemIR::AddrOf>(
+      loc_id, {.type_id = dest_ptr_type_id, .lvalue_id = ref_id});
 }
 }
 
 
 // Returns whether `category` is a valid expression category to produce as a
 // Returns whether `category` is a valid expression category to produce as a
@@ -729,8 +737,8 @@ static auto PerformBuiltinConversion(Context& context, SemIR::LocId loc_id,
         // The initializer produces an object representation by copy, and the
         // The initializer produces an object representation by copy, and the
         // value representation is a copy of the object representation, so we
         // value representation is a copy of the object representation, so we
         // already have a value of the right form.
         // already have a value of the right form.
-        return context.AddInst(
-            {loc_id, SemIR::ValueOfInitializer{value_type_id, value_id}});
+        return context.AddInst<SemIR::ValueOfInitializer>(
+            loc_id, {.type_id = value_type_id, .init_id = value_id});
       }
       }
     }
     }
   }
   }
@@ -750,8 +758,8 @@ static auto PerformBuiltinConversion(Context& context, SemIR::LocId loc_id,
             ConversionTarget{.kind = ConversionTarget::Value,
             ConversionTarget{.kind = ConversionTarget::Value,
                              .type_id = value_type_id});
                              .type_id = value_type_id});
       }
       }
-      return context.AddInst(
-          {loc_id, SemIR::AsCompatible{target.type_id, value_id}});
+      return context.AddInst<SemIR::AsCompatible>(
+          loc_id, {.type_id = target.type_id, .source_id = value_id});
     }
     }
   }
   }
 
 
@@ -856,8 +864,8 @@ static auto PerformBuiltinConversion(Context& context, SemIR::LocId loc_id,
     // TODO: Support converting tuple and struct values to facet types,
     // TODO: Support converting tuple and struct values to facet types,
     // combining the above conversions and this one in a single conversion.
     // combining the above conversions and this one in a single conversion.
     if (sem_ir.types().Is<SemIR::InterfaceType>(value_type_id)) {
     if (sem_ir.types().Is<SemIR::InterfaceType>(value_type_id)) {
-      return context.AddInst(
-          {loc_id, SemIR::FacetTypeAccess{target.type_id, value_id}});
+      return context.AddInst<SemIR::FacetTypeAccess>(
+          loc_id, {.type_id = target.type_id, .facet_id = value_id});
     }
     }
   }
   }
 
 
@@ -967,8 +975,10 @@ auto Convert(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id,
 
 
   // Track that we performed a type conversion, if we did so.
   // Track that we performed a type conversion, if we did so.
   if (orig_expr_id != expr_id) {
   if (orig_expr_id != expr_id) {
-    expr_id = context.AddInst(
-        {loc_id, SemIR::Converted{target.type_id, orig_expr_id, expr_id}});
+    expr_id =
+        context.AddInst<SemIR::Converted>(loc_id, {.type_id = target.type_id,
+                                                   .original_id = orig_expr_id,
+                                                   .result_id = expr_id});
   }
   }
 
 
   // For `as`, don't perform any value category conversions. In particular, an
   // For `as`, don't perform any value category conversions. In particular, an
@@ -1018,8 +1028,9 @@ auto Convert(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id,
 
 
       // If we have a reference and don't want one, form a value binding.
       // If we have a reference and don't want one, form a value binding.
       // TODO: Support types with custom value representations.
       // TODO: Support types with custom value representations.
-      expr_id = context.AddInst({context.insts().GetLocId(expr_id),
-                                 SemIR::BindValue{expr.type_id(), expr_id}});
+      expr_id = context.AddInst<SemIR::BindValue>(
+          context.insts().GetLocId(expr_id),
+          {.type_id = expr.type_id(), .value_id = expr_id});
       // We now have a value expression.
       // We now have a value expression.
       [[fallthrough]];
       [[fallthrough]];
 
 
@@ -1036,9 +1047,10 @@ auto Convert(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id,
     if (auto init_rep = SemIR::GetInitRepr(sem_ir, target.type_id);
     if (auto init_rep = SemIR::GetInitRepr(sem_ir, target.type_id);
         init_rep.kind == SemIR::InitRepr::ByCopy) {
         init_rep.kind == SemIR::InitRepr::ByCopy) {
       target.init_block->InsertHere();
       target.init_block->InsertHere();
-      expr_id = context.AddInst(
-          {loc_id,
-           SemIR::InitializeFrom{target.type_id, expr_id, target.init_id}});
+      expr_id = context.AddInst<SemIR::InitializeFrom>(
+          loc_id, {.type_id = target.type_id,
+                   .src_id = expr_id,
+                   .dest_id = target.init_id});
     }
     }
   }
   }
 
 
@@ -1143,9 +1155,9 @@ static auto ConvertSelf(Context& context, SemIR::LocId call_loc_id,
         return SemIR::InstId::BuiltinError;
         return SemIR::InstId::BuiltinError;
     }
     }
     auto loc_id = context.insts().GetLocId(self_or_addr_id);
     auto loc_id = context.insts().GetLocId(self_or_addr_id);
-    self_or_addr_id = context.AddInst(
-        {loc_id, SemIR::AddrOf{context.GetPointerType(self.type_id()),
-                               self_or_addr_id}});
+    self_or_addr_id = context.AddInst<SemIR::AddrOf>(
+        loc_id, {.type_id = context.GetPointerType(self.type_id()),
+                 .lvalue_id = self_or_addr_id});
   }
   }
 
 
   return ConvertToValueOfType(context, call_loc_id, self_or_addr_id,
   return ConvertToValueOfType(context, call_loc_id, self_or_addr_id,

+ 12 - 12
toolchain/check/handle_alias.cpp

@@ -44,30 +44,30 @@ auto HandleAlias(Context& context, Parse::AliasId /*node_id*/) -> bool {
        .enclosing_scope_id = name_context.enclosing_scope_id_for_new_inst(),
        .enclosing_scope_id = name_context.enclosing_scope_id_for_new_inst(),
        .bind_index = SemIR::CompileTimeBindIndex::Invalid});
        .bind_index = SemIR::CompileTimeBindIndex::Invalid});
 
 
-  auto alias_id = SemIR::InstId::Invalid;
+  auto alias_type_id = SemIR::TypeId::Invalid;
+  auto alias_value_id = SemIR::InstId::Invalid;
   if (expr_id.is_builtin()) {
   if (expr_id.is_builtin()) {
     // Type (`bool`) and value (`false`) literals provided by the builtin
     // Type (`bool`) and value (`false`) literals provided by the builtin
     // structure should be turned into name references.
     // structure should be turned into name references.
     // TODO: Look into handling `false`, this doesn't do it right now because it
     // TODO: Look into handling `false`, this doesn't do it right now because it
     // sees a value instruction instead of a builtin.
     // sees a value instruction instead of a builtin.
-    alias_id = context.AddInst(
-        {name_context.loc_id,
-         SemIR::BindAlias{context.insts().Get(expr_id).type_id(), bind_name_id,
-                          expr_id}});
+    alias_type_id = context.insts().Get(expr_id).type_id();
+    alias_value_id = expr_id;
   } else if (auto inst = context.insts().TryGetAs<SemIR::NameRef>(expr_id)) {
   } else if (auto inst = context.insts().TryGetAs<SemIR::NameRef>(expr_id)) {
     // Pass through name references, albeit changing the name in use.
     // Pass through name references, albeit changing the name in use.
-    alias_id = context.AddInst(
-        {name_context.loc_id,
-         SemIR::BindAlias{inst->type_id, bind_name_id, inst->value_id}});
+    alias_type_id = inst->type_id;
+    alias_value_id = inst->value_id;
   } else {
   } else {
     CARBON_DIAGNOSTIC(AliasRequiresNameRef, Error,
     CARBON_DIAGNOSTIC(AliasRequiresNameRef, Error,
                       "Alias initializer must be a name reference.");
                       "Alias initializer must be a name reference.");
     context.emitter().Emit(expr_node, AliasRequiresNameRef);
     context.emitter().Emit(expr_node, AliasRequiresNameRef);
-    alias_id =
-        context.AddInst({name_context.loc_id,
-                         SemIR::BindAlias{SemIR::TypeId::Error, bind_name_id,
-                                          SemIR::InstId::BuiltinError}});
+    alias_type_id = SemIR::TypeId::Error;
+    alias_value_id = SemIR::InstId::BuiltinError;
   }
   }
+  auto alias_id = context.AddInst<SemIR::BindAlias>(
+      name_context.loc_id, {.type_id = alias_type_id,
+                            .bind_name_id = bind_name_id,
+                            .value_id = alias_value_id});
 
 
   // Add the name of the binding to the current scope.
   // Add the name of the binding to the current scope.
   context.decl_name_stack().PopScope();
   context.decl_name_stack().PopScope();

+ 5 - 4
toolchain/check/handle_array.cpp

@@ -46,10 +46,11 @@ auto HandleArrayExpr(Context& context, Parse::ArrayExprId node_id) -> bool {
     return true;
     return true;
   }
   }
 
 
-  context.AddInstAndPush(
-      {node_id, SemIR::ArrayType{SemIR::TypeId::TypeType, bound_inst_id,
-                                 ExprAsType(context, element_type_node_id,
-                                            element_type_inst_id)}});
+  context.AddInstAndPush<SemIR::ArrayType>(
+      node_id, {.type_id = SemIR::TypeId::TypeType,
+                .bound_id = bound_inst_id,
+                .element_type_id = ExprAsType(context, element_type_node_id,
+                                              element_type_inst_id)});
   return true;
   return true;
 }
 }
 
 

+ 26 - 17
toolchain/check/handle_binding_pattern.cpp

@@ -6,6 +6,7 @@
 #include "toolchain/check/convert.h"
 #include "toolchain/check/convert.h"
 #include "toolchain/check/return.h"
 #include "toolchain/check/return.h"
 #include "toolchain/sem_ir/ids.h"
 #include "toolchain/sem_ir/ids.h"
+#include "toolchain/sem_ir/inst.h"
 
 
 namespace Carbon::Check {
 namespace Carbon::Check {
 
 
@@ -45,10 +46,15 @@ auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
                            : SemIR::CompileTimeBindIndex::Invalid});
                            : SemIR::CompileTimeBindIndex::Invalid});
     if (is_generic) {
     if (is_generic) {
       // TODO: Create a `BindTemplateName` instead inside a `template` pattern.
       // TODO: Create a `BindTemplateName` instead inside a `template` pattern.
-      return {name_node,
-              SemIR::BindSymbolicName{type_id, bind_name_id, value_id}};
+      return SemIR::LocIdAndInst(
+          name_node, SemIR::BindSymbolicName{.type_id = type_id,
+                                             .bind_name_id = bind_name_id,
+                                             .value_id = value_id});
     } else {
     } else {
-      return {name_node, SemIR::BindName{type_id, bind_name_id, value_id}};
+      return SemIR::LocIdAndInst(name_node,
+                                 SemIR::BindName{.type_id = type_id,
+                                                 .bind_name_id = bind_name_id,
+                                                 .value_id = value_id});
     }
     }
   };
   };
 
 
@@ -98,16 +104,19 @@ auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
             context.classes().Get(enclosing_class_decl->class_id);
             context.classes().Get(enclosing_class_decl->class_id);
         auto field_type_id = context.GetUnboundElementType(
         auto field_type_id = context.GetUnboundElementType(
             class_info.self_type_id, cast_type_id);
             class_info.self_type_id, cast_type_id);
-        auto field_id = context.AddInst(
-            {binding_id, SemIR::FieldDecl{
-                             field_type_id, name_id,
-                             SemIR::ElementIndex(context.args_type_info_stack()
-                                                     .PeekCurrentBlockContents()
-                                                     .size())}});
+        auto field_id = context.AddInst<SemIR::FieldDecl>(
+            binding_id,
+            {.type_id = field_type_id,
+             .name_id = name_id,
+             .index = SemIR::ElementIndex(context.args_type_info_stack()
+                                              .PeekCurrentBlockContents()
+                                              .size())});
 
 
         // Add a corresponding field to the object representation of the class.
         // Add a corresponding field to the object representation of the class.
-        context.args_type_info_stack().AddInstId(context.AddInstInNoBlock(
-            {binding_id, SemIR::StructTypeField{name_id, cast_type_id}}));
+        context.args_type_info_stack().AddInstId(
+            context.AddInstInNoBlock<SemIR::StructTypeField>(
+                binding_id,
+                {.name_id = name_id, .field_type_id = cast_type_id}));
         context.node_stack().Push(node_id, field_id);
         context.node_stack().Push(node_id, field_id);
         break;
         break;
       }
       }
@@ -120,8 +129,8 @@ auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
             CheckReturnedVar(context, context.node_stack().PeekNodeId(),
             CheckReturnedVar(context, context.node_stack().PeekNodeId(),
                              name_node, name_id, type_node, cast_type_id);
                              name_node, name_id, type_node, cast_type_id);
       } else {
       } else {
-        value_id = context.AddInst(
-            {name_node, SemIR::VarStorage{cast_type_id, name_id}});
+        value_id = context.AddInst<SemIR::VarStorage>(
+            name_node, {.type_id = cast_type_id, .name_id = name_id});
       }
       }
       auto bind_id = context.AddInst(make_bind_name(cast_type_id, value_id));
       auto bind_id = context.AddInst(make_bind_name(cast_type_id, value_id));
       context.node_stack().Push(node_id, bind_id);
       context.node_stack().Push(node_id, bind_id);
@@ -138,8 +147,8 @@ auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
       // in a function definition. We don't know which kind we have here.
       // in a function definition. We don't know which kind we have here.
       // TODO: A tuple pattern can appear in other places than function
       // TODO: A tuple pattern can appear in other places than function
       // parameters.
       // parameters.
-      auto param_id =
-          context.AddInst({name_node, SemIR::Param{cast_type_id, name_id}});
+      auto param_id = context.AddInst<SemIR::Param>(
+          name_node, {.type_id = cast_type_id, .name_id = name_id});
       auto bind_id = context.AddInst(make_bind_name(cast_type_id, param_id));
       auto bind_id = context.AddInst(make_bind_name(cast_type_id, param_id));
       // TODO: Bindings should come into scope immediately in other contexts
       // TODO: Bindings should come into scope immediately in other contexts
       // too.
       // too.
@@ -192,8 +201,8 @@ auto HandleAddr(Context& context, Parse::AddrId node_id) -> bool {
           SemIR::NameId::SelfValue) {
           SemIR::NameId::SelfValue) {
     // TODO: The type of an `addr_pattern` should probably be the non-pointer
     // TODO: The type of an `addr_pattern` should probably be the non-pointer
     // type, because that's the type that the pattern matches.
     // type, because that's the type that the pattern matches.
-    context.AddInstAndPush(
-        {node_id, SemIR::AddrPattern{self_param->type_id, self_param_id}});
+    context.AddInstAndPush<SemIR::AddrPattern>(
+        node_id, {.type_id = self_param->type_id, .inner_id = self_param_id});
   } else {
   } else {
     CARBON_DIAGNOSTIC(AddrOnNonSelfParam, Error,
     CARBON_DIAGNOSTIC(AddrOnNonSelfParam, Error,
                       "`addr` can only be applied to a `self` parameter.");
                       "`addr` can only be applied to a `self` parameter.");

+ 18 - 14
toolchain/check/handle_class.cpp

@@ -11,6 +11,7 @@
 #include "toolchain/check/modifiers.h"
 #include "toolchain/check/modifiers.h"
 #include "toolchain/check/name_component.h"
 #include "toolchain/check/name_component.h"
 #include "toolchain/sem_ir/ids.h"
 #include "toolchain/sem_ir/ids.h"
+#include "toolchain/sem_ir/inst.h"
 #include "toolchain/sem_ir/typed_insts.h"
 #include "toolchain/sem_ir/typed_insts.h"
 
 
 namespace Carbon::Check {
 namespace Carbon::Check {
@@ -211,9 +212,11 @@ static auto BuildClassDecl(Context& context, Parse::AnyClassDeclId node_id,
   auto decl_block_id = context.inst_block_stack().Pop();
   auto decl_block_id = context.inst_block_stack().Pop();
 
 
   // Add the class declaration.
   // Add the class declaration.
-  auto class_decl = SemIR::ClassDecl{SemIR::TypeId::TypeType,
-                                     SemIR::ClassId::Invalid, decl_block_id};
-  auto class_decl_id = context.AddPlaceholderInst({node_id, class_decl});
+  auto class_decl = SemIR::ClassDecl{.type_id = SemIR::TypeId::TypeType,
+                                     .class_id = SemIR::ClassId::Invalid,
+                                     .decl_block_id = decl_block_id};
+  auto class_decl_id =
+      context.AddPlaceholderInst(SemIR::LocIdAndInst(node_id, class_decl));
 
 
   // TODO: Store state regarding is_extern.
   // TODO: Store state regarding is_extern.
   SemIR::Class class_info = {
   SemIR::Class class_info = {
@@ -388,8 +391,8 @@ auto HandleAdaptDecl(Context& context, Parse::AdaptDeclId node_id) -> bool {
   });
   });
 
 
   // Build a SemIR representation for the declaration.
   // Build a SemIR representation for the declaration.
-  class_info.adapt_id =
-      context.AddInst({node_id, SemIR::AdaptDecl{adapted_type_id}});
+  class_info.adapt_id = context.AddInst<SemIR::AdaptDecl>(
+      node_id, {.adapted_type_id = adapted_type_id});
 
 
   // Extend the class scope with the adapted type's scope if requested.
   // Extend the class scope with the adapted type's scope if requested.
   if (modifiers.HasAnyOf(KeywordModifierSet::Extend)) {
   if (modifiers.HasAnyOf(KeywordModifierSet::Extend)) {
@@ -520,19 +523,20 @@ auto HandleBaseDecl(Context& context, Parse::BaseDeclId node_id) -> bool {
   // binding will be performed when it's found by name lookup into an instance.
   // binding will be performed when it's found by name lookup into an instance.
   auto field_type_id =
   auto field_type_id =
       context.GetUnboundElementType(class_info.self_type_id, base_info.type_id);
       context.GetUnboundElementType(class_info.self_type_id, base_info.type_id);
-  class_info.base_id = context.AddInst(
-      {node_id,
-       SemIR::BaseDecl{field_type_id, base_info.type_id,
-                       SemIR::ElementIndex(context.args_type_info_stack()
-                                               .PeekCurrentBlockContents()
-                                               .size())}});
+  class_info.base_id = context.AddInst<SemIR::BaseDecl>(
+      node_id,
+      {.type_id = field_type_id,
+       .base_type_id = base_info.type_id,
+       .index = SemIR::ElementIndex(
+           context.args_type_info_stack().PeekCurrentBlockContents().size())});
 
 
   // Add a corresponding field to the object representation of the class.
   // Add a corresponding field to the object representation of the class.
   // TODO: Consider whether we want to use `partial T` here.
   // TODO: Consider whether we want to use `partial T` here.
   // TODO: Should we diagnose if there are already any fields?
   // TODO: Should we diagnose if there are already any fields?
-  context.args_type_info_stack().AddInstId(context.AddInstInNoBlock(
-      {node_id,
-       SemIR::StructTypeField{SemIR::NameId::Base, base_info.type_id}}));
+  context.args_type_info_stack().AddInstId(
+      context.AddInstInNoBlock<SemIR::StructTypeField>(
+          node_id, {.name_id = SemIR::NameId::Base,
+                    .field_type_id = base_info.type_id}));
 
 
   // Bind the name `base` in the class to the base field.
   // Bind the name `base` in the class to the base field.
   context.decl_name_stack().AddNameOrDiagnoseDuplicate(
   context.decl_name_stack().AddNameOrDiagnoseDuplicate(

+ 4 - 4
toolchain/check/handle_export.cpp

@@ -67,10 +67,10 @@ auto HandleExportDecl(Context& context, Parse::ExportDeclId node_id) -> bool {
     return true;
     return true;
   }
   }
 
 
-  auto export_id = context.AddInst(
-      {node_id, SemIR::ExportDecl{.type_id = import_ref->type_id,
-                                  .bind_name_id = import_ref->bind_name_id,
-                                  .value_id = inst_id}});
+  auto export_id = context.AddInst<SemIR::ExportDecl>(
+      node_id, {.type_id = import_ref->type_id,
+                .bind_name_id = import_ref->bind_name_id,
+                .value_id = inst_id});
   context.AddExport(export_id);
   context.AddExport(export_id);
 
 
   // Replace the ImportRef in name lookup, both for the above duplicate
   // Replace the ImportRef in name lookup, both for the above duplicate

+ 5 - 4
toolchain/check/handle_function.cpp

@@ -39,8 +39,8 @@ auto HandleReturnType(Context& context, Parse::ReturnTypeId node_id) -> bool {
   auto [type_node_id, type_inst_id] = context.node_stack().PopExprWithNodeId();
   auto [type_node_id, type_inst_id] = context.node_stack().PopExprWithNodeId();
   auto type_id = ExprAsType(context, type_node_id, type_inst_id);
   auto type_id = ExprAsType(context, type_node_id, type_inst_id);
   // TODO: Use a dedicated instruction rather than VarStorage here.
   // TODO: Use a dedicated instruction rather than VarStorage here.
-  context.AddInstAndPush(
-      {node_id, SemIR::VarStorage{type_id, SemIR::NameId::ReturnSlot}});
+  context.AddInstAndPush<SemIR::VarStorage>(
+      node_id, {.type_id = type_id, .name_id = SemIR::NameId::ReturnSlot});
   return true;
   return true;
 }
 }
 
 
@@ -254,7 +254,8 @@ static auto BuildFunctionDecl(Context& context,
   auto function_info = SemIR::Function{
   auto function_info = SemIR::Function{
       .name_id = name_context.name_id_for_new_inst(),
       .name_id = name_context.name_id_for_new_inst(),
       .enclosing_scope_id = name_context.enclosing_scope_id_for_new_inst(),
       .enclosing_scope_id = name_context.enclosing_scope_id_for_new_inst(),
-      .decl_id = context.AddPlaceholderInst({node_id, function_decl}),
+      .decl_id = context.AddPlaceholderInst(
+          SemIR::LocIdAndInst(node_id, function_decl)),
       .implicit_param_refs_id = name.implicit_params_id,
       .implicit_param_refs_id = name.implicit_params_id,
       .param_refs_id = name.params_id,
       .param_refs_id = name.params_id,
       .return_type_id = return_type_id,
       .return_type_id = return_type_id,
@@ -406,7 +407,7 @@ auto HandleFunctionDefinition(Context& context,
           "Missing `return` at end of function with declared return type.");
           "Missing `return` at end of function with declared return type.");
       context.emitter().Emit(TokenOnly(node_id), MissingReturnStatement);
       context.emitter().Emit(TokenOnly(node_id), MissingReturnStatement);
     } else {
     } else {
-      context.AddInst({node_id, SemIR::Return{}});
+      context.AddInst<SemIR::Return>(node_id, {});
     }
     }
   }
   }
 
 

+ 1 - 1
toolchain/check/handle_if_statement.cpp

@@ -52,7 +52,7 @@ auto HandleIfStatement(Context& context, Parse::IfStatementId node_id) -> bool {
       // block.
       // block.
       auto else_block_id =
       auto else_block_id =
           context.node_stack().Pop<Parse::NodeKind::IfCondition>();
           context.node_stack().Pop<Parse::NodeKind::IfCondition>();
-      context.AddInst({node_id, SemIR::Branch{else_block_id}});
+      context.AddInst<SemIR::Branch>(node_id, {.target_id = else_block_id});
       context.inst_block_stack().Pop();
       context.inst_block_stack().Pop();
       context.inst_block_stack().Push(else_block_id);
       context.inst_block_stack().Push(else_block_id);
       break;
       break;

+ 3 - 2
toolchain/check/handle_impl.cpp

@@ -208,8 +208,9 @@ static auto BuildImplDecl(Context& context, Parse::AnyImplDeclId node_id)
   // TODO: Does lookup in an impl file need to look for a prior impl declaration
   // TODO: Does lookup in an impl file need to look for a prior impl declaration
   // in the api file?
   // in the api file?
   auto impl_id = context.impls().LookupOrAdd(self_type_id, constraint_type_id);
   auto impl_id = context.impls().LookupOrAdd(self_type_id, constraint_type_id);
-  auto impl_decl = SemIR::ImplDecl{impl_id, decl_block_id};
-  auto impl_decl_id = context.AddInst({node_id, impl_decl});
+  SemIR::ImplDecl impl_decl = {.impl_id = impl_id,
+                               .decl_block_id = decl_block_id};
+  auto impl_decl_id = context.AddInst(node_id, impl_decl);
 
 
   // For an `extend impl` declaration, mark the impl as extending this `impl`.
   // For an `extend impl` declaration, mark the impl as extending this `impl`.
   if (context.decl_state_stack().innermost().modifier_set.HasAnyOf(
   if (context.decl_state_stack().innermost().modifier_set.HasAnyOf(

+ 10 - 8
toolchain/check/handle_index.cpp

@@ -52,14 +52,15 @@ auto HandleIndexExpr(Context& context, Parse::IndexExprId node_id) -> bool {
       if (array_cat == SemIR::ExprCategory::Value) {
       if (array_cat == SemIR::ExprCategory::Value) {
         // If the operand is an array value, convert it to an ephemeral
         // If the operand is an array value, convert it to an ephemeral
         // reference to an array so we can perform a primitive indexing into it.
         // reference to an array so we can perform a primitive indexing into it.
-        operand_inst_id = context.AddInst(
-            {node_id, SemIR::ValueAsRef{operand_type_id, operand_inst_id}});
+        operand_inst_id = context.AddInst<SemIR::ValueAsRef>(
+            node_id, {.type_id = operand_type_id, .value_id = operand_inst_id});
       }
       }
       // Constant evaluation will perform a bounds check on this array indexing
       // Constant evaluation will perform a bounds check on this array indexing
       // if the index is constant.
       // if the index is constant.
-      auto elem_id = context.AddInst(
-          {node_id, SemIR::ArrayIndex{array_type.element_type_id,
-                                      operand_inst_id, cast_index_id}});
+      auto elem_id = context.AddInst<SemIR::ArrayIndex>(
+          node_id, {.type_id = array_type.element_type_id,
+                    .array_id = operand_inst_id,
+                    .index_id = cast_index_id});
       if (array_cat != SemIR::ExprCategory::DurableRef) {
       if (array_cat != SemIR::ExprCategory::DurableRef) {
         // Indexing a durable reference gives a durable reference expression.
         // Indexing a durable reference gives a durable reference expression.
         // Indexing anything else gives a value expression.
         // Indexing anything else gives a value expression.
@@ -97,9 +98,10 @@ auto HandleIndexExpr(Context& context, Parse::IndexExprId node_id) -> bool {
           index_inst_id = SemIR::InstId::BuiltinError;
           index_inst_id = SemIR::InstId::BuiltinError;
         }
         }
       }
       }
-      context.AddInstAndPush(
-          {node_id,
-           SemIR::TupleIndex{element_type_id, operand_inst_id, index_inst_id}});
+      context.AddInstAndPush<SemIR::TupleIndex>(node_id,
+                                                {.type_id = element_type_id,
+                                                 .tuple_id = operand_inst_id,
+                                                 .index_id = index_inst_id});
       return true;
       return true;
     }
     }
     default: {
     default: {

+ 1 - 1
toolchain/check/handle_interface.cpp

@@ -57,7 +57,7 @@ static auto BuildInterfaceDecl(Context& context,
   auto interface_decl = SemIR::InterfaceDecl{
   auto interface_decl = SemIR::InterfaceDecl{
       SemIR::TypeId::TypeType, SemIR::InterfaceId::Invalid, decl_block_id};
       SemIR::TypeId::TypeType, SemIR::InterfaceId::Invalid, decl_block_id};
   auto interface_decl_id =
   auto interface_decl_id =
-      context.AddPlaceholderInst({node_id, interface_decl});
+      context.AddPlaceholderInst(SemIR::LocIdAndInst(node_id, interface_decl));
 
 
   // Check whether this is a redeclaration.
   // Check whether this is a redeclaration.
   auto existing_id = context.decl_name_stack().LookupOrAddName(
   auto existing_id = context.decl_name_stack().LookupOrAddName(

+ 18 - 21
toolchain/check/handle_literal.cpp

@@ -10,19 +10,17 @@ namespace Carbon::Check {
 
 
 auto HandleBoolLiteralFalse(Context& context, Parse::BoolLiteralFalseId node_id)
 auto HandleBoolLiteralFalse(Context& context, Parse::BoolLiteralFalseId node_id)
     -> bool {
     -> bool {
-  context.AddInstAndPush(
-      {node_id,
-       SemIR::BoolLiteral{context.GetBuiltinType(SemIR::BuiltinKind::BoolType),
-                          SemIR::BoolValue::False}});
+  context.AddInstAndPush<SemIR::BoolLiteral>(
+      node_id, {.type_id = context.GetBuiltinType(SemIR::BuiltinKind::BoolType),
+                .value = SemIR::BoolValue::False});
   return true;
   return true;
 }
 }
 
 
 auto HandleBoolLiteralTrue(Context& context, Parse::BoolLiteralTrueId node_id)
 auto HandleBoolLiteralTrue(Context& context, Parse::BoolLiteralTrueId node_id)
     -> bool {
     -> bool {
-  context.AddInstAndPush(
-      {node_id,
-       SemIR::BoolLiteral{context.GetBuiltinType(SemIR::BuiltinKind::BoolType),
-                          SemIR::BoolValue::True}});
+  context.AddInstAndPush<SemIR::BoolLiteral>(
+      node_id, {.type_id = context.GetBuiltinType(SemIR::BuiltinKind::BoolType),
+                .value = SemIR::BoolValue::True});
   return true;
   return true;
 }
 }
 
 
@@ -41,10 +39,9 @@ static auto MakeI32Literal(Context& context, Parse::NodeId node_id,
   }
   }
   // Literals are always represented as unsigned, so zero-extend if needed.
   // Literals are always represented as unsigned, so zero-extend if needed.
   auto i32_val = val.zextOrTrunc(32);
   auto i32_val = val.zextOrTrunc(32);
-  return context.AddInst(
-      {node_id,
-       SemIR::IntLiteral{context.GetBuiltinType(SemIR::BuiltinKind::IntType),
-                         context.ints().Add(i32_val)}});
+  return context.AddInst<SemIR::IntLiteral>(
+      node_id, {.type_id = context.GetBuiltinType(SemIR::BuiltinKind::IntType),
+                .int_id = context.ints().Add(i32_val)});
 }
 }
 
 
 auto HandleIntLiteral(Context& context, Parse::IntLiteralId node_id) -> bool {
 auto HandleIntLiteral(Context& context, Parse::IntLiteralId node_id) -> bool {
@@ -94,20 +91,20 @@ auto HandleRealLiteral(Context& context, Parse::RealLiteralId node_id) -> bool {
                                real_value.exponent.getSExtValue());
                                real_value.exponent.getSExtValue());
 
 
   auto float_id = context.sem_ir().floats().Add(llvm::APFloat(double_val));
   auto float_id = context.sem_ir().floats().Add(llvm::APFloat(double_val));
-  context.AddInstAndPush(
-      {node_id,
-       SemIR::FloatLiteral{
-           context.GetBuiltinType(SemIR::BuiltinKind::FloatType), float_id}});
+  context.AddInstAndPush<SemIR::FloatLiteral>(
+      node_id,
+      {.type_id = context.GetBuiltinType(SemIR::BuiltinKind::FloatType),
+       .float_id = float_id});
   return true;
   return true;
 }
 }
 
 
 auto HandleStringLiteral(Context& context, Parse::StringLiteralId node_id)
 auto HandleStringLiteral(Context& context, Parse::StringLiteralId node_id)
     -> bool {
     -> bool {
-  context.AddInstAndPush(
-      {node_id, SemIR::StringLiteral{
-                    context.GetBuiltinType(SemIR::BuiltinKind::StringType),
-                    context.tokens().GetStringLiteralValue(
-                        context.parse_tree().node_token(node_id))}});
+  context.AddInstAndPush<SemIR::StringLiteral>(
+      node_id,
+      {.type_id = context.GetBuiltinType(SemIR::BuiltinKind::StringType),
+       .string_literal_id = context.tokens().GetStringLiteralValue(
+           context.parse_tree().node_token(node_id))});
   return true;
   return true;
 }
 }
 
 

+ 5 - 3
toolchain/check/handle_loop_statement.cpp

@@ -58,7 +58,7 @@ auto HandleWhileStatement(Context& context, Parse::WhileStatementId node_id)
   context.break_continue_stack().pop_back();
   context.break_continue_stack().pop_back();
 
 
   // Add the loop backedge.
   // Add the loop backedge.
-  context.AddInst({node_id, SemIR::Branch{loop_header_id}});
+  context.AddInst<SemIR::Branch>(node_id, {.target_id = loop_header_id});
   context.inst_block_stack().Pop();
   context.inst_block_stack().Pop();
 
 
   // Start emitting the loop exit block.
   // Start emitting the loop exit block.
@@ -100,7 +100,8 @@ auto HandleBreakStatementStart(Context& context,
                       "`break` can only be used in a loop.");
                       "`break` can only be used in a loop.");
     context.emitter().Emit(node_id, BreakOutsideLoop);
     context.emitter().Emit(node_id, BreakOutsideLoop);
   } else {
   } else {
-    context.AddInst({node_id, SemIR::Branch{stack.back().break_target}});
+    context.AddInst<SemIR::Branch>(node_id,
+                                   {.target_id = stack.back().break_target});
   }
   }
 
 
   context.inst_block_stack().Pop();
   context.inst_block_stack().Pop();
@@ -125,7 +126,8 @@ auto HandleContinueStatementStart(Context& context,
                       "`continue` can only be used in a loop.");
                       "`continue` can only be used in a loop.");
     context.emitter().Emit(node_id, ContinueOutsideLoop);
     context.emitter().Emit(node_id, ContinueOutsideLoop);
   } else {
   } else {
-    context.AddInst({node_id, SemIR::Branch{stack.back().continue_target}});
+    context.AddInst<SemIR::Branch>(node_id,
+                                   {.target_id = stack.back().continue_target});
   }
   }
 
 
   context.inst_block_stack().Pop();
   context.inst_block_stack().Pop();

+ 8 - 7
toolchain/check/handle_name.cpp

@@ -80,8 +80,9 @@ static auto HandleNameAsExpr(Context& context, Parse::NodeId node_id,
                              SemIR::NameId name_id) -> bool {
                              SemIR::NameId name_id) -> bool {
   auto value_id = context.LookupUnqualifiedName(node_id, name_id);
   auto value_id = context.LookupUnqualifiedName(node_id, name_id);
   auto value = context.insts().Get(value_id);
   auto value = context.insts().Get(value_id);
-  context.AddInstAndPush(
-      {node_id, SemIR::NameRef{value.type_id(), name_id, value_id}});
+  context.AddInstAndPush<SemIR::NameRef>(
+      node_id,
+      {.type_id = value.type_id(), .name_id = name_id, .value_id = value_id});
   return true;
   return true;
 }
 }
 
 
@@ -133,11 +134,11 @@ auto HandleNameQualifier(Context& context, Parse::NameQualifierId /*node_id*/)
 }
 }
 
 
 auto HandlePackageExpr(Context& context, Parse::PackageExprId node_id) -> bool {
 auto HandlePackageExpr(Context& context, Parse::PackageExprId node_id) -> bool {
-  context.AddInstAndPush(
-      {node_id,
-       SemIR::NameRef{context.GetBuiltinType(SemIR::BuiltinKind::NamespaceType),
-                      SemIR::NameId::PackageNamespace,
-                      SemIR::InstId::PackageNamespace}});
+  context.AddInstAndPush<SemIR::NameRef>(
+      node_id,
+      {.type_id = context.GetBuiltinType(SemIR::BuiltinKind::NamespaceType),
+       .name_id = SemIR::NameId::PackageNamespace,
+       .value_id = SemIR::InstId::PackageNamespace});
   return true;
   return true;
 }
 }
 
 

+ 3 - 1
toolchain/check/handle_namespace.cpp

@@ -7,6 +7,7 @@
 #include "toolchain/check/modifiers.h"
 #include "toolchain/check/modifiers.h"
 #include "toolchain/check/name_component.h"
 #include "toolchain/check/name_component.h"
 #include "toolchain/sem_ir/ids.h"
 #include "toolchain/sem_ir/ids.h"
+#include "toolchain/sem_ir/inst.h"
 
 
 namespace Carbon::Check {
 namespace Carbon::Check {
 
 
@@ -26,7 +27,8 @@ auto HandleNamespace(Context& context, Parse::NamespaceId node_id) -> bool {
   auto namespace_inst = SemIR::Namespace{
   auto namespace_inst = SemIR::Namespace{
       context.GetBuiltinType(SemIR::BuiltinKind::NamespaceType),
       context.GetBuiltinType(SemIR::BuiltinKind::NamespaceType),
       SemIR::NameScopeId::Invalid, SemIR::InstId::Invalid};
       SemIR::NameScopeId::Invalid, SemIR::InstId::Invalid};
-  auto namespace_id = context.AddPlaceholderInst({node_id, namespace_inst});
+  auto namespace_id =
+      context.AddPlaceholderInst(SemIR::LocIdAndInst(node_id, namespace_inst));
   namespace_inst.name_scope_id = context.name_scopes().Add(
   namespace_inst.name_scope_id = context.name_scopes().Add(
       namespace_id, name_context.name_id_for_new_inst(),
       namespace_id, name_context.name_id_for_new_inst(),
       name_context.enclosing_scope_id_for_new_inst());
       name_context.enclosing_scope_id_for_new_inst());

+ 24 - 20
toolchain/check/handle_operator.cpp

@@ -84,7 +84,7 @@ auto HandleInfixOperatorEqual(Context& context,
   // TODO: Destroy the old value before reinitializing. This will require
   // TODO: Destroy the old value before reinitializing. This will require
   // building the destruction code before we build the RHS subexpression.
   // building the destruction code before we build the RHS subexpression.
   rhs_id = Initialize(context, node_id, lhs_id, rhs_id);
   rhs_id = Initialize(context, node_id, lhs_id, rhs_id);
-  context.AddInst({node_id, SemIR::Assign{lhs_id, rhs_id}});
+  context.AddInst<SemIR::Assign>(node_id, {.lhs_id = lhs_id, .rhs_id = rhs_id});
   // We model assignment as an expression, so we need to push a value for
   // We model assignment as an expression, so we need to push a value for
   // it, even though it doesn't produce a value.
   // it, even though it doesn't produce a value.
   // TODO: Consider changing our parse tree to model assignment as a
   // TODO: Consider changing our parse tree to model assignment as a
@@ -226,8 +226,9 @@ auto HandlePostfixOperatorStar(Context& context,
                                Parse::PostfixOperatorStarId node_id) -> bool {
                                Parse::PostfixOperatorStarId node_id) -> bool {
   auto value_id = context.node_stack().PopExpr();
   auto value_id = context.node_stack().PopExpr();
   auto inner_type_id = ExprAsType(context, node_id, value_id);
   auto inner_type_id = ExprAsType(context, node_id, value_id);
-  context.AddInstAndPush(
-      {node_id, SemIR::PointerType{SemIR::TypeId::TypeType, inner_type_id}});
+  context.AddInstAndPush<SemIR::PointerType>(
+      node_id,
+      {.type_id = SemIR::TypeId::TypeType, .pointee_id = inner_type_id});
   return true;
   return true;
 }
 }
 
 
@@ -253,8 +254,9 @@ auto HandlePrefixOperatorAmp(Context& context,
       value_id = SemIR::InstId::BuiltinError;
       value_id = SemIR::InstId::BuiltinError;
       break;
       break;
   }
   }
-  context.AddInstAndPush(
-      {node_id, SemIR::AddrOf{context.GetPointerType(type_id), value_id}});
+  context.AddInstAndPush<SemIR::AddrOf>(
+      node_id, SemIR::AddrOf{.type_id = context.GetPointerType(type_id),
+                             .lvalue_id = value_id});
   return true;
   return true;
 }
 }
 
 
@@ -277,8 +279,8 @@ auto HandlePrefixOperatorConst(Context& context,
     context.emitter().Emit(node_id, RepeatedConst);
     context.emitter().Emit(node_id, RepeatedConst);
   }
   }
   auto inner_type_id = ExprAsType(context, node_id, value_id);
   auto inner_type_id = ExprAsType(context, node_id, value_id);
-  context.AddInstAndPush(
-      {node_id, SemIR::ConstType{SemIR::TypeId::TypeType, inner_type_id}});
+  context.AddInstAndPush<SemIR::ConstType>(
+      node_id, {.type_id = SemIR::TypeId::TypeType, .inner_id = inner_type_id});
   return true;
   return true;
 }
 }
 
 
@@ -297,9 +299,9 @@ auto HandlePrefixOperatorNot(Context& context,
                              Parse::PrefixOperatorNotId node_id) -> bool {
                              Parse::PrefixOperatorNotId node_id) -> bool {
   auto value_id = context.node_stack().PopExpr();
   auto value_id = context.node_stack().PopExpr();
   value_id = ConvertToBoolValue(context, node_id, value_id);
   value_id = ConvertToBoolValue(context, node_id, value_id);
-  context.AddInstAndPush(
-      {node_id, SemIR::UnaryOperatorNot{context.insts().Get(value_id).type_id(),
-                                        value_id}});
+  context.AddInstAndPush<SemIR::UnaryOperatorNot>(
+      node_id, {.type_id = context.insts().Get(value_id).type_id(),
+                .operand_id = value_id});
   return true;
   return true;
 }
 }
 
 
@@ -349,12 +351,13 @@ static auto HandleShortCircuitOperand(Context& context, Parse::NodeId node_id,
 
 
   // Compute the branch value: the condition for `and`, inverted for `or`.
   // Compute the branch value: the condition for `and`, inverted for `or`.
   SemIR::InstId branch_value_id =
   SemIR::InstId branch_value_id =
-      is_or ? context.AddInst({node_id, SemIR::UnaryOperatorNot{bool_type_id,
-                                                                cond_value_id}})
-            : cond_value_id;
-  auto short_circuit_result_id = context.AddInst(
-      {node_id,
-       SemIR::BoolLiteral{bool_type_id, SemIR::BoolValue::From(is_or)}});
+      is_or
+          ? context.AddInst<SemIR::UnaryOperatorNot>(
+                node_id, {.type_id = bool_type_id, .operand_id = cond_value_id})
+          : cond_value_id;
+  auto short_circuit_result_id = context.AddInst<SemIR::BoolLiteral>(
+      node_id,
+      {.type_id = bool_type_id, .value = SemIR::BoolValue::From(is_or)});
 
 
   // Create a block for the right-hand side and for the continuation.
   // Create a block for the right-hand side and for the continuation.
   auto rhs_block_id =
   auto rhs_block_id =
@@ -407,14 +410,15 @@ static auto HandleShortCircuitOperator(Context& context, Parse::NodeId node_id)
   // When the second operand is evaluated, the result of `and` and `or` is
   // When the second operand is evaluated, the result of `and` and `or` is
   // its value.
   // its value.
   auto resume_block_id = context.inst_block_stack().PeekOrAdd(/*depth=*/1);
   auto resume_block_id = context.inst_block_stack().PeekOrAdd(/*depth=*/1);
-  context.AddInst({node_id, SemIR::BranchWithArg{resume_block_id, rhs_id}});
+  context.AddInst<SemIR::BranchWithArg>(
+      node_id, {.target_id = resume_block_id, .arg_id = rhs_id});
   context.inst_block_stack().Pop();
   context.inst_block_stack().Pop();
   context.AddCurrentCodeBlockToFunction(node_id);
   context.AddCurrentCodeBlockToFunction(node_id);
 
 
   // Collect the result from either the first or second operand.
   // Collect the result from either the first or second operand.
-  auto result_id = context.AddInst(
-      {node_id, SemIR::BlockArg{context.insts().Get(rhs_id).type_id(),
-                                resume_block_id}});
+  auto result_id = context.AddInst<SemIR::BlockArg>(
+      node_id, {.type_id = context.insts().Get(rhs_id).type_id(),
+                .block_id = resume_block_id});
   context.SetBlockArgResultBeforeConstantUse(result_id, branch_value_id, rhs_id,
   context.SetBlockArgResultBeforeConstantUse(result_id, branch_value_id, rhs_id,
                                              short_circuit_result_id);
                                              short_circuit_result_id);
   context.node_stack().Push(node_id, result_id);
   context.node_stack().Push(node_id, result_id);

+ 11 - 9
toolchain/check/handle_struct.cpp

@@ -44,9 +44,11 @@ auto HandleStructField(Context& context, Parse::StructFieldId node_id) -> bool {
   auto [name_node, name_id] = context.node_stack().PopNameWithNodeId();
   auto [name_node, name_id] = context.node_stack().PopNameWithNodeId();
 
 
   // Store the name for the type.
   // Store the name for the type.
-  context.args_type_info_stack().AddInstId(context.AddInstInNoBlock(
-      {name_node, SemIR::StructTypeField{
-                      name_id, context.insts().Get(value_inst_id).type_id()}}));
+  context.args_type_info_stack().AddInstId(
+      context.AddInstInNoBlock<SemIR::StructTypeField>(
+          name_node,
+          {.name_id = name_id,
+           .field_type_id = context.insts().Get(value_inst_id).type_id()}));
 
 
   // Push the value back on the stack as an argument.
   // Push the value back on the stack as an argument.
   context.node_stack().Push(node_id, value_inst_id);
   context.node_stack().Push(node_id, value_inst_id);
@@ -60,8 +62,8 @@ auto HandleStructTypeField(Context& context, Parse::StructTypeFieldId node_id)
 
 
   auto [name_node, name_id] = context.node_stack().PopNameWithNodeId();
   auto [name_node, name_id] = context.node_stack().PopNameWithNodeId();
 
 
-  auto inst_id = context.AddInst(
-      {name_node, SemIR::StructTypeField{name_id, cast_type_id}});
+  auto inst_id = context.AddInst<SemIR::StructTypeField>(
+      name_node, {.name_id = name_id, .field_type_id = cast_type_id});
   context.node_stack().Push(node_id, inst_id);
   context.node_stack().Push(node_id, inst_id);
   return true;
   return true;
 }
 }
@@ -109,8 +111,8 @@ auto HandleStructLiteral(Context& context, Parse::StructLiteralId node_id)
 
 
   auto type_id = context.GetStructType(type_block_id);
   auto type_id = context.GetStructType(type_block_id);
 
 
-  auto value_id =
-      context.AddInst({node_id, SemIR::StructLiteral{type_id, refs_id}});
+  auto value_id = context.AddInst<SemIR::StructLiteral>(
+      node_id, {.type_id = type_id, .elements_id = refs_id});
   context.node_stack().Push(node_id, value_id);
   context.node_stack().Push(node_id, value_id);
   return true;
   return true;
 }
 }
@@ -131,8 +133,8 @@ auto HandleStructTypeLiteral(Context& context,
     context.node_stack().Push(node_id, SemIR::InstId::BuiltinError);
     context.node_stack().Push(node_id, SemIR::InstId::BuiltinError);
     return true;
     return true;
   }
   }
-  context.AddInstAndPush(
-      {node_id, SemIR::StructType{SemIR::TypeId::TypeType, refs_id}});
+  context.AddInstAndPush<SemIR::StructType>(
+      node_id, {.type_id = SemIR::TypeId::TypeType, .fields_id = refs_id});
   return true;
   return true;
 }
 }
 
 

+ 2 - 2
toolchain/check/handle_tuple_literal.cpp

@@ -34,8 +34,8 @@ auto HandleTupleLiteral(Context& context, Parse::TupleLiteralId node_id)
   }
   }
   auto type_id = context.GetTupleType(type_ids);
   auto type_id = context.GetTupleType(type_ids);
 
 
-  auto value_id =
-      context.AddInst({node_id, SemIR::TupleLiteral{type_id, refs_id}});
+  auto value_id = context.AddInst<SemIR::TupleLiteral>(
+      node_id, {.type_id = type_id, .elements_id = refs_id});
   context.node_stack().Push(node_id, value_id);
   context.node_stack().Push(node_id, value_id);
   return true;
   return true;
 }
 }

+ 2 - 1
toolchain/check/handle_variable.cpp

@@ -84,7 +84,8 @@ auto HandleVariableDecl(Context& context, Parse::VariableDeclId node_id)
       init_id = Initialize(context, node_id, value_id, *init_id);
       init_id = Initialize(context, node_id, value_id, *init_id);
       // TODO: Consider using different instruction kinds for assignment versus
       // TODO: Consider using different instruction kinds for assignment versus
       // initialization.
       // initialization.
-      context.AddInst({node_id, SemIR::Assign{value_id, *init_id}});
+      context.AddInst<SemIR::Assign>(node_id,
+                                     {.lhs_id = value_id, .rhs_id = *init_id});
     }
     }
     if (context.scope_stack().PeekIndex() == ScopeIndex::Package) {
     if (context.scope_stack().PeekIndex() == ScopeIndex::Package) {
       context.inst_block_stack().PopGlobalInit();
       context.inst_block_stack().PopGlobalInit();

+ 3 - 2
toolchain/check/impl.cpp

@@ -138,8 +138,9 @@ static auto BuildInterfaceWitness(
   }
   }
 
 
   auto table_id = context.inst_blocks().Add(table);
   auto table_id = context.inst_blocks().Add(table);
-  return context.AddInst(SemIR::LocIdAndInst::NoLoc(SemIR::InterfaceWitness{
-      context.GetBuiltinType(SemIR::BuiltinKind::WitnessType), table_id}));
+  return context.AddInst(SemIR::LocIdAndInst::NoLoc<SemIR::InterfaceWitness>(
+      {.type_id = context.GetBuiltinType(SemIR::BuiltinKind::WitnessType),
+       .elements_id = table_id}));
 }
 }
 
 
 auto BuildImplWitness(Context& context, SemIR::ImplId impl_id)
 auto BuildImplWitness(Context& context, SemIR::ImplId impl_id)

+ 6 - 6
toolchain/check/import.cpp

@@ -103,7 +103,8 @@ static auto AddNamespace(
   auto namespace_inst = SemIR::Namespace{
   auto namespace_inst = SemIR::Namespace{
       namespace_type_id, SemIR::NameScopeId::Invalid, import_id};
       namespace_type_id, SemIR::NameScopeId::Invalid, import_id};
   // Use the invalid node because there's no node to associate with.
   // Use the invalid node because there's no node to associate with.
-  auto namespace_id = context.AddPlaceholderInst({node_id, namespace_inst});
+  auto namespace_id =
+      context.AddPlaceholderInst(SemIR::LocIdAndInst(node_id, namespace_inst));
   namespace_inst.name_scope_id =
   namespace_inst.name_scope_id =
       context.name_scopes().Add(namespace_id, name_id, enclosing_scope_id);
       context.name_scopes().Add(namespace_id, name_id, enclosing_scope_id);
   context.ReplaceInstBeforeConstantUse(namespace_id, namespace_inst);
   context.ReplaceInstBeforeConstantUse(namespace_id, namespace_inst);
@@ -147,11 +148,10 @@ static auto CopySingleNameScopeFromImportIR(
          .bind_index = SemIR::CompileTimeBindIndex::Invalid});
          .bind_index = SemIR::CompileTimeBindIndex::Invalid});
     auto import_ir_inst_id = context.import_ir_insts().Add(
     auto import_ir_inst_id = context.import_ir_insts().Add(
         {.ir_id = ir_id, .inst_id = import_inst_id});
         {.ir_id = ir_id, .inst_id = import_inst_id});
-    return context.AddInst(
-        {import_ir_inst_id,
-         SemIR::ImportRefLoaded{.type_id = namespace_type_id,
-                                .import_ir_inst_id = import_ir_inst_id,
-                                .bind_name_id = bind_name_id}});
+    return context.AddInst<SemIR::ImportRefLoaded>(
+        import_ir_inst_id, {.type_id = namespace_type_id,
+                            .import_ir_inst_id = import_ir_inst_id,
+                            .bind_name_id = bind_name_id});
   };
   };
   auto [namespace_scope_id, namespace_const_id, _] =
   auto [namespace_scope_id, namespace_const_id, _] =
       AddNamespace(context, namespace_type_id, Parse::NodeId::Invalid, name_id,
       AddNamespace(context, namespace_type_id, Parse::NodeId::Invalid, name_id,

+ 44 - 40
toolchain/check/import_ref.cpp

@@ -56,10 +56,10 @@ auto AddImportIR(Context& context, SemIR::ImportIR import_ir)
 auto AddImportRef(Context& context, SemIR::ImportIRInst import_ir_inst,
 auto AddImportRef(Context& context, SemIR::ImportIRInst import_ir_inst,
                   SemIR::BindNameId bind_name_id) -> SemIR::InstId {
                   SemIR::BindNameId bind_name_id) -> SemIR::InstId {
   auto import_ir_inst_id = context.import_ir_insts().Add(import_ir_inst);
   auto import_ir_inst_id = context.import_ir_insts().Add(import_ir_inst);
+  SemIR::ImportRefUnloaded inst = {.import_ir_inst_id = import_ir_inst_id,
+                                   .bind_name_id = bind_name_id};
   auto import_ref_id = context.AddPlaceholderInstInNoBlock(
   auto import_ref_id = context.AddPlaceholderInstInNoBlock(
-      {import_ir_inst_id,
-       SemIR::ImportRefUnloaded{.import_ir_inst_id = import_ir_inst_id,
-                                .bind_name_id = bind_name_id}});
+      SemIR::LocIdAndInst(import_ir_inst_id, inst));
 
 
   // We can't insert this instruction into whatever block we happen to be in,
   // We can't insert this instruction into whatever block we happen to be in,
   // because this function is typically called by name lookup in the middle of
   // because this function is typically called by name lookup in the middle of
@@ -438,8 +438,8 @@ class ImportRefResolver {
       auto name_id = GetLocalNameId(param_inst.name_id);
       auto name_id = GetLocalNameId(param_inst.name_id);
       auto type_id = context_.GetTypeIdForTypeConstant(const_id);
       auto type_id = context_.GetTypeIdForTypeConstant(const_id);
 
 
-      auto new_param_id = context_.AddInstInNoBlock(
-          {AddImportIRInst(param_id), SemIR::Param{type_id, name_id}});
+      auto new_param_id = context_.AddInstInNoBlock<SemIR::Param>(
+          AddImportIRInst(param_id), {.type_id = type_id, .name_id = name_id});
       if (bind_inst) {
       if (bind_inst) {
         switch (bind_inst->kind) {
         switch (bind_inst->kind) {
           case SemIR::BindName::Kind: {
           case SemIR::BindName::Kind: {
@@ -447,9 +447,10 @@ class ImportRefResolver {
                 {.name_id = name_id,
                 {.name_id = name_id,
                  .enclosing_scope_id = SemIR::NameScopeId::Invalid,
                  .enclosing_scope_id = SemIR::NameScopeId::Invalid,
                  .bind_index = SemIR::CompileTimeBindIndex::Invalid});
                  .bind_index = SemIR::CompileTimeBindIndex::Invalid});
-            new_param_id = context_.AddInstInNoBlock(
-                {AddImportIRInst(bind_id),
-                 SemIR::BindName{type_id, bind_name_id, new_param_id}});
+            new_param_id = context_.AddInstInNoBlock<SemIR::BindName>(
+                AddImportIRInst(bind_id), {.type_id = type_id,
+                                           .bind_name_id = bind_name_id,
+                                           .value_id = new_param_id});
             break;
             break;
           }
           }
           case SemIR::BindSymbolicName::Kind: {
           case SemIR::BindSymbolicName::Kind: {
@@ -473,9 +474,9 @@ class ImportRefResolver {
         }
         }
       }
       }
       if (addr_inst) {
       if (addr_inst) {
-        new_param_id = context_.AddInstInNoBlock(
-            {AddImportIRInst(ref_id),
-             SemIR::AddrPattern{type_id, new_param_id}});
+        new_param_id = context_.AddInstInNoBlock<SemIR::AddrPattern>(
+            AddImportIRInst(ref_id),
+            {.type_id = type_id, .inner_id = new_param_id});
       }
       }
       new_param_refs.push_back(new_param_id);
       new_param_refs.push_back(new_param_id);
     }
     }
@@ -669,11 +670,11 @@ class ImportRefResolver {
         context_, {.ir_id = import_ir_id_, .inst_id = inst.decl_id},
         context_, {.ir_id = import_ir_id_, .inst_id = inst.decl_id},
         SemIR::BindNameId::Invalid);
         SemIR::BindNameId::Invalid);
 
 
-    auto inst_id = context_.AddInstInNoBlock(
-        {AddImportIRInst(inst.decl_id),
-         SemIR::AssociatedEntity{
-             context_.GetTypeIdForTypeConstant(type_const_id), inst.index,
-             decl_id}});
+    auto inst_id = context_.AddInstInNoBlock<SemIR::AssociatedEntity>(
+        AddImportIRInst(inst.decl_id),
+        {.type_id = context_.GetTypeIdForTypeConstant(type_const_id),
+         .index = inst.index,
+         .decl_id = decl_id});
     return {context_.constant_values().Get(inst_id)};
     return {context_.constant_values().Get(inst_id)};
   }
   }
 
 
@@ -709,11 +710,11 @@ class ImportRefResolver {
     }
     }
 
 
     // Import the instruction in order to update contained base_type_id.
     // Import the instruction in order to update contained base_type_id.
-    auto inst_id = context_.AddInstInNoBlock(
-        {AddImportIRInst(import_inst_id),
-         SemIR::BaseDecl{context_.GetTypeIdForTypeConstant(type_const_id),
-                         context_.GetTypeIdForTypeConstant(base_type_const_id),
-                         inst.index}});
+    auto inst_id = context_.AddInstInNoBlock<SemIR::BaseDecl>(
+        AddImportIRInst(import_inst_id),
+        {.type_id = context_.GetTypeIdForTypeConstant(type_const_id),
+         .base_type_id = context_.GetTypeIdForTypeConstant(base_type_const_id),
+         .index = inst.index});
     return {context_.constant_values().Get(inst_id)};
     return {context_.constant_values().Get(inst_id)};
   }
   }
 
 
@@ -741,10 +742,11 @@ class ImportRefResolver {
         {.name_id = name_id,
         {.name_id = name_id,
          .enclosing_scope_id = SemIR::NameScopeId::Invalid,
          .enclosing_scope_id = SemIR::NameScopeId::Invalid,
          .bind_index = import_bind_info.bind_index});
          .bind_index = import_bind_info.bind_index});
-    auto new_bind_id = context_.AddInstInNoBlock(
-        {AddImportIRInst(import_inst_id),
-         SemIR::BindSymbolicName{context_.GetTypeIdForTypeConstant(type_id),
-                                 bind_name_id, SemIR::InstId::Invalid}});
+    auto new_bind_id = context_.AddInstInNoBlock<SemIR::BindSymbolicName>(
+        AddImportIRInst(import_inst_id),
+        {.type_id = context_.GetTypeIdForTypeConstant(type_id),
+         .bind_name_id = bind_name_id,
+         .value_id = SemIR::InstId::Invalid});
     return {context_.constant_values().Get(new_bind_id)};
     return {context_.constant_values().Get(new_bind_id)};
   }
   }
 
 
@@ -757,7 +759,7 @@ class ImportRefResolver {
         SemIR::ClassDecl{SemIR::TypeId::TypeType, SemIR::ClassId::Invalid,
         SemIR::ClassDecl{SemIR::TypeId::TypeType, SemIR::ClassId::Invalid,
                          SemIR::InstBlockId::Empty};
                          SemIR::InstBlockId::Empty};
     auto class_decl_id = context_.AddPlaceholderInstInNoBlock(
     auto class_decl_id = context_.AddPlaceholderInstInNoBlock(
-        {AddImportIRInst(import_class.decl_id), class_decl});
+        SemIR::LocIdAndInst(AddImportIRInst(import_class.decl_id), class_decl));
     // Regardless of whether ClassDecl is a complete type, we first need an
     // Regardless of whether ClassDecl is a complete type, we first need an
     // incomplete type so that any references have something to point at.
     // incomplete type so that any references have something to point at.
     class_decl.class_id = context_.classes().Add({
     class_decl.class_id = context_.classes().Add({
@@ -929,10 +931,11 @@ class ImportRefResolver {
     if (HasNewWork(initial_work)) {
     if (HasNewWork(initial_work)) {
       return ResolveResult::Retry();
       return ResolveResult::Retry();
     }
     }
-    auto inst_id = context_.AddInstInNoBlock(
-        {AddImportIRInst(import_inst_id),
-         SemIR::FieldDecl{context_.GetTypeIdForTypeConstant(const_id),
-                          GetLocalNameId(inst.name_id), inst.index}});
+    auto inst_id = context_.AddInstInNoBlock<SemIR::FieldDecl>(
+        AddImportIRInst(import_inst_id),
+        {.type_id = context_.GetTypeIdForTypeConstant(const_id),
+         .name_id = GetLocalNameId(inst.name_id),
+         .index = inst.index});
     return {context_.constant_values().Get(inst_id)};
     return {context_.constant_values().Get(inst_id)};
   }
   }
 
 
@@ -963,7 +966,7 @@ class ImportRefResolver {
                                                  ? function.definition_id
                                                  ? function.definition_id
                                                  : function.decl_id);
                                                  : function.decl_id);
     auto function_decl_id = context_.AddPlaceholderInstInNoBlock(
     auto function_decl_id = context_.AddPlaceholderInstInNoBlock(
-        {import_ir_inst_id, function_decl});
+        SemIR::LocIdAndInst(import_ir_inst_id, function_decl));
 
 
     auto new_return_type_id =
     auto new_return_type_id =
         return_type_const_id.is_valid()
         return_type_const_id.is_valid()
@@ -974,9 +977,10 @@ class ImportRefResolver {
       // Recreate the return slot from scratch.
       // Recreate the return slot from scratch.
       // TODO: Once we import function definitions, we'll need to make sure we
       // TODO: Once we import function definitions, we'll need to make sure we
       // use the same return storage variable in the declaration and definition.
       // use the same return storage variable in the declaration and definition.
-      new_return_storage = context_.AddInstInNoBlock(
-          {AddImportIRInst(function.return_storage_id),
-           SemIR::VarStorage{new_return_type_id, SemIR::NameId::ReturnSlot}});
+      new_return_storage = context_.AddInstInNoBlock<SemIR::VarStorage>(
+          AddImportIRInst(function.return_storage_id),
+          {.type_id = new_return_type_id,
+           .name_id = SemIR::NameId::ReturnSlot});
     }
     }
     function_decl.function_id = context_.functions().Add(
     function_decl.function_id = context_.functions().Add(
         {.name_id = GetLocalNameId(function.name_id),
         {.name_id = GetLocalNameId(function.name_id),
@@ -1056,8 +1060,9 @@ class ImportRefResolver {
     auto interface_decl = SemIR::InterfaceDecl{SemIR::TypeId::TypeType,
     auto interface_decl = SemIR::InterfaceDecl{SemIR::TypeId::TypeType,
                                                SemIR::InterfaceId::Invalid,
                                                SemIR::InterfaceId::Invalid,
                                                SemIR::InstBlockId::Empty};
                                                SemIR::InstBlockId::Empty};
-    auto interface_decl_id = context_.AddPlaceholderInstInNoBlock(
-        {AddImportIRInst(import_interface.decl_id), interface_decl});
+    auto interface_decl_id =
+        context_.AddPlaceholderInstInNoBlock(SemIR::LocIdAndInst(
+            AddImportIRInst(import_interface.decl_id), interface_decl));
 
 
     // Start with an incomplete interface.
     // Start with an incomplete interface.
     SemIR::Interface new_interface = {
     SemIR::Interface new_interface = {
@@ -1207,10 +1212,9 @@ class ImportRefResolver {
       auto field = import_ir_.insts().GetAs<SemIR::StructTypeField>(field_id);
       auto field = import_ir_.insts().GetAs<SemIR::StructTypeField>(field_id);
       auto name_id = GetLocalNameId(field.name_id);
       auto name_id = GetLocalNameId(field.name_id);
       auto field_type_id = context_.GetTypeIdForTypeConstant(field_const_id);
       auto field_type_id = context_.GetTypeIdForTypeConstant(field_const_id);
-      fields.push_back(context_.AddInstInNoBlock(
-          {AddImportIRInst(import_inst_id),
-           SemIR::StructTypeField{.name_id = name_id,
-                                  .field_type_id = field_type_id}}));
+      fields.push_back(context_.AddInstInNoBlock<SemIR::StructTypeField>(
+          AddImportIRInst(import_inst_id),
+          {.name_id = name_id, .field_type_id = field_type_id}));
     }
     }
 
 
     return {context_.types().GetConstantId(
     return {context_.types().GetConstantId(

+ 3 - 2
toolchain/check/interface.cpp

@@ -30,8 +30,9 @@ auto BuildAssociatedEntity(Context& context, SemIR::InterfaceId interface_id,
   // not the declaration itself.
   // not the declaration itself.
   auto type_id = context.GetAssociatedEntityType(
   auto type_id = context.GetAssociatedEntityType(
       interface_id, context.insts().Get(decl_id).type_id());
       interface_id, context.insts().Get(decl_id).type_id());
-  return context.AddInst({context.insts().GetLocId(decl_id),
-                          SemIR::AssociatedEntity{type_id, index, decl_id}});
+  return context.AddInst<SemIR::AssociatedEntity>(
+      context.insts().GetLocId(decl_id),
+      {.type_id = type_id, .index = index, .decl_id = decl_id});
 }
 }
 
 
 }  // namespace Carbon::Check
 }  // namespace Carbon::Check

+ 20 - 15
toolchain/check/member_access.cpp

@@ -197,8 +197,10 @@ static auto PerformImplLookup(Context& context, Parse::NodeId node_id,
       SubstType(context, assoc_type.entity_type_id, substitutions);
       SubstType(context, assoc_type.entity_type_id, substitutions);
 
 
   return context.AddInst(
   return context.AddInst(
-      SemIR::LocIdAndInst::NoLoc(SemIR::InterfaceWitnessAccess{
-          subst_type_id, witness_id, assoc_entity->index}));
+      SemIR::LocIdAndInst::NoLoc<SemIR::InterfaceWitnessAccess>(
+          {.type_id = subst_type_id,
+           .witness_id = witness_id,
+           .index = assoc_entity->index}));
 }
 }
 
 
 // Performs a member name lookup into the specified scope, including performing
 // Performs a member name lookup into the specified scope, including performing
@@ -216,8 +218,9 @@ static auto LookupMemberNameInScope(Context& context, Parse::NodeId node_id,
   auto inst = context.insts().Get(inst_id);
   auto inst = context.insts().Get(inst_id);
   // TODO: Use a different kind of instruction that also references the
   // TODO: Use a different kind of instruction that also references the
   // `base_id` so that `SemIR` consumers can find it.
   // `base_id` so that `SemIR` consumers can find it.
-  auto member_id = context.AddInst(
-      {node_id, SemIR::NameRef{inst.type_id(), name_id, inst_id}});
+  auto member_id = context.AddInst<SemIR::NameRef>(
+      node_id,
+      {.type_id = inst.type_id(), .name_id = name_id, .value_id = inst_id});
 
 
   // If member name lookup finds an associated entity name, and the scope is not
   // If member name lookup finds an associated entity name, and the scope is not
   // a facet type, perform impl lookup.
   // a facet type, perform impl lookup.
@@ -256,9 +259,10 @@ static auto PerformInstanceBinding(Context& context, Parse::NodeId node_id,
           << "Non-constant value " << context.insts().Get(member_id)
           << "Non-constant value " << context.insts().Get(member_id)
           << " of unbound element type";
           << " of unbound element type";
       auto index = GetClassElementIndex(context, element_id.inst_id());
       auto index = GetClassElementIndex(context, element_id.inst_id());
-      auto access_id = context.AddInst(
-          {node_id, SemIR::ClassElementAccess{
-                        unbound_element_type.element_type_id, base_id, index}});
+      auto access_id = context.AddInst<SemIR::ClassElementAccess>(
+          node_id, {.type_id = unbound_element_type.element_type_id,
+                    .base_id = base_id,
+                    .index = index});
       if (SemIR::GetExprCategory(context.sem_ir(), base_id) ==
       if (SemIR::GetExprCategory(context.sem_ir(), base_id) ==
               SemIR::ExprCategory::Value &&
               SemIR::ExprCategory::Value &&
           SemIR::GetExprCategory(context.sem_ir(), access_id) !=
           SemIR::GetExprCategory(context.sem_ir(), access_id) !=
@@ -274,11 +278,11 @@ static auto PerformInstanceBinding(Context& context, Parse::NodeId node_id,
     }
     }
     case CARBON_KIND(SemIR::FunctionType fn_type): {
     case CARBON_KIND(SemIR::FunctionType fn_type): {
       if (IsInstanceMethod(context.sem_ir(), fn_type.function_id)) {
       if (IsInstanceMethod(context.sem_ir(), fn_type.function_id)) {
-        return context.AddInst(
-            {node_id,
-             SemIR::BoundMethod{
-                 context.GetBuiltinType(SemIR::BuiltinKind::BoundMethodType),
-                 base_id, member_id}});
+        return context.AddInst<SemIR::BoundMethod>(
+            node_id, {.type_id = context.GetBuiltinType(
+                          SemIR::BuiltinKind::BoundMethodType),
+                      .object_id = base_id,
+                      .function_id = member_id});
       }
       }
       [[fallthrough]];
       [[fallthrough]];
     }
     }
@@ -331,9 +335,10 @@ auto PerformMemberAccess(Context& context, Parse::NodeId node_id,
         if (name_id == field.name_id) {
         if (name_id == field.name_id) {
           // TODO: Model this as producing a lookup result, and do instance
           // TODO: Model this as producing a lookup result, and do instance
           // binding separately. Perhaps a struct type should be a name scope.
           // binding separately. Perhaps a struct type should be a name scope.
-          return context.AddInst(
-              {node_id, SemIR::StructAccess{field.field_type_id, base_id,
-                                            SemIR::ElementIndex(i)}});
+          return context.AddInst<SemIR::StructAccess>(
+              node_id, {.type_id = field.field_type_id,
+                        .struct_id = base_id,
+                        .index = SemIR::ElementIndex(i)});
         }
         }
       }
       }
       CARBON_DIAGNOSTIC(QualifiedExprNameNotFound, Error,
       CARBON_DIAGNOSTIC(QualifiedExprNameNotFound, Error,

+ 3 - 2
toolchain/check/pending_block.h

@@ -39,8 +39,9 @@ class PendingBlock {
     size_t size_;
     size_t size_;
   };
   };
 
 
-  auto AddInst(SemIR::LocIdAndInst loc_id_and_inst) -> SemIR::InstId {
-    auto inst_id = context_.AddInstInNoBlock(loc_id_and_inst);
+  template <typename InstT>
+  auto AddInst(SemIR::LocId loc_id, InstT inst) -> SemIR::InstId {
+    auto inst_id = context_.AddInstInNoBlock(loc_id, inst);
     insts_.push_back(inst_id);
     insts_.push_back(inst_id);
     return inst_id;
     return inst_id;
   }
   }

+ 2 - 1
toolchain/check/pointer_dereference.cpp

@@ -30,7 +30,8 @@ auto PerformPointerDereference(
   } else if (type_id != SemIR::TypeId::Error) {
   } else if (type_id != SemIR::TypeId::Error) {
     diagnose_not_pointer(type_id);
     diagnose_not_pointer(type_id);
   }
   }
-  return context.AddInst({node_id, SemIR::Deref{result_type_id, base_id}});
+  return context.AddInst<SemIR::Deref>(
+      node_id, {.type_id = result_type_id, .pointer_id = base_id});
 }
 }
 
 
 }  // namespace Carbon::Check
 }  // namespace Carbon::Check

+ 7 - 5
toolchain/check/return.cpp

@@ -84,7 +84,8 @@ auto CheckReturnedVar(Context& context, Parse::NodeId returned_node,
   if (function.has_return_slot()) {
   if (function.has_return_slot()) {
     return function.return_storage_id;
     return function.return_storage_id;
   }
   }
-  return context.AddInst({name_node, SemIR::VarStorage{type_id, name_id}});
+  return context.AddInst<SemIR::VarStorage>(
+      name_node, {.type_id = type_id, .name_id = name_id});
 }
 }
 
 
 auto RegisterReturnedVar(Context& context, SemIR::InstId bind_id) -> void {
 auto RegisterReturnedVar(Context& context, SemIR::InstId bind_id) -> void {
@@ -111,7 +112,7 @@ auto BuildReturnWithNoExpr(Context& context, Parse::ReturnStatementId node_id)
     diag.Emit();
     diag.Emit();
   }
   }
 
 
-  context.AddInst({node_id, SemIR::Return{}});
+  context.AddInst<SemIR::Return>(node_id, {});
 }
 }
 
 
 auto BuildReturnWithExpr(Context& context, Parse::ReturnStatementId node_id,
 auto BuildReturnWithExpr(Context& context, Parse::ReturnStatementId node_id,
@@ -147,7 +148,8 @@ auto BuildReturnWithExpr(Context& context, Parse::ReturnStatementId node_id,
                                    function.return_type_id);
                                    function.return_type_id);
   }
   }
 
 
-  context.AddInst({node_id, SemIR::ReturnExpr{expr_id, return_slot_id}});
+  context.AddInst<SemIR::ReturnExpr>(
+      node_id, {.expr_id = expr_id, .dest_id = return_slot_id});
 }
 }
 
 
 auto BuildReturnVar(Context& context, Parse::ReturnStatementId node_id)
 auto BuildReturnVar(Context& context, Parse::ReturnStatementId node_id)
@@ -170,8 +172,8 @@ auto BuildReturnVar(Context& context, Parse::ReturnStatementId node_id)
     return_slot_id = SemIR::InstId::Invalid;
     return_slot_id = SemIR::InstId::Invalid;
   }
   }
 
 
-  context.AddInst(
-      {node_id, SemIR::ReturnExpr{returned_var_id, return_slot_id}});
+  context.AddInst<SemIR::ReturnExpr>(
+      node_id, {.expr_id = returned_var_id, .dest_id = return_slot_id});
 }
 }
 
 
 }  // namespace Carbon::Check
 }  // namespace Carbon::Check

+ 2 - 5
toolchain/sem_ir/inst.h

@@ -301,15 +301,12 @@ inline auto operator<<(llvm::raw_ostream& out, TypedInst inst)
 // Associates a LocId and Inst in order to provide type-checking that the
 // Associates a LocId and Inst in order to provide type-checking that the
 // TypedNodeId corresponds to the InstT.
 // TypedNodeId corresponds to the InstT.
 struct LocIdAndInst {
 struct LocIdAndInst {
-  // For cases with no location.
   template <typename InstT>
   template <typename InstT>
-    requires(!Internal::HasNodeId<InstT>)
   static auto NoLoc(InstT inst) -> LocIdAndInst {
   static auto NoLoc(InstT inst) -> LocIdAndInst {
     return LocIdAndInst(LocId::Invalid, inst, /*is_untyped=*/true);
     return LocIdAndInst(LocId::Invalid, inst, /*is_untyped=*/true);
   }
   }
 
 
-  // For the common case, support construction as:
-  //   context.AddInst({node_id, SemIR::MyInst{...}});
+  // Construction for the common case with a typed node.
   template <typename InstT>
   template <typename InstT>
     requires(Internal::HasNodeId<InstT>)
     requires(Internal::HasNodeId<InstT>)
   LocIdAndInst(decltype(InstT::Kind)::TypedNodeId node_id, InstT inst)
   LocIdAndInst(decltype(InstT::Kind)::TypedNodeId node_id, InstT inst)
@@ -327,7 +324,6 @@ struct LocIdAndInst {
   template <typename InstT>
   template <typename InstT>
   LocIdAndInst(ImportIRInstId import_ir_inst_id, InstT inst)
   LocIdAndInst(ImportIRInstId import_ir_inst_id, InstT inst)
       : loc_id(import_ir_inst_id), inst(inst) {}
       : loc_id(import_ir_inst_id), inst(inst) {}
-
   LocId loc_id;
   LocId loc_id;
   Inst inst;
   Inst inst;
 
 
@@ -335,6 +331,7 @@ struct LocIdAndInst {
   // Expose the internal constructor for GetWithLocId.
   // Expose the internal constructor for GetWithLocId.
   friend class InstStore;
   friend class InstStore;
 
 
+  // Note `is_untyped` serves to disambiguate from public constructors.
   explicit LocIdAndInst(LocId loc_id, Inst inst, bool /*is_untyped*/)
   explicit LocIdAndInst(LocId loc_id, Inst inst, bool /*is_untyped*/)
       : loc_id(loc_id), inst(inst) {}
       : loc_id(loc_id), inst(inst) {}
 };
 };