Procházet zdrojové kódy

Ensure we evaluate instructions created in uncommon ways. (#3598)

Instructions created by splices during conversion are now evaluated, as
are instructions created in cases where we first create a placeholder
instruction and later replace it by a different instruction.

This also removes the ability to set a parse node and instruction
independently after creating an `InstId`, which could lead to them
accidentally not matching.
Richard Smith před 2 roky
rodič
revize
906346cf35
42 změnil soubory, kde provedl 366 přidání a 240 odebrání
  1. 54 8
      toolchain/check/context.cpp
  2. 29 1
      toolchain/check/context.h
  3. 5 6
      toolchain/check/handle_binding_pattern.cpp
  4. 4 4
      toolchain/check/handle_class.cpp
  5. 4 2
      toolchain/check/handle_function.cpp
  6. 4 2
      toolchain/check/handle_interface.cpp
  7. 6 5
      toolchain/check/handle_let.cpp
  8. 3 3
      toolchain/check/handle_namespace.cpp
  9. 2 2
      toolchain/check/handle_struct.cpp
  10. 3 3
      toolchain/check/import.cpp
  11. 0 1
      toolchain/check/inst_block_stack.cpp
  12. 0 10
      toolchain/check/inst_block_stack.h
  13. 11 11
      toolchain/check/pending_block.h
  14. 4 2
      toolchain/check/testdata/array/array_in_place.carbon
  15. 2 1
      toolchain/check/testdata/array/assign_return_value.carbon
  16. 6 3
      toolchain/check/testdata/array/assign_var.carbon
  17. 34 26
      toolchain/check/testdata/array/base.carbon
  18. 27 23
      toolchain/check/testdata/array/fail_type_mismatch.carbon
  19. 6 3
      toolchain/check/testdata/array/function_param.carbon
  20. 18 9
      toolchain/check/testdata/array/nine_elements.carbon
  21. 1 1
      toolchain/check/testdata/as/as_type.carbon
  22. 1 1
      toolchain/check/testdata/basics/builtin_types.carbon
  23. 44 32
      toolchain/check/testdata/basics/numeric_literals.carbon
  24. 2 1
      toolchain/check/testdata/basics/verbose.carbon
  25. 2 2
      toolchain/check/testdata/class/fail_field_modifiers.carbon
  26. 1 1
      toolchain/check/testdata/class/fail_member_of_let.carbon
  27. 2 1
      toolchain/check/testdata/if_expr/basic.carbon
  28. 8 6
      toolchain/check/testdata/index/array_element_access.carbon
  29. 35 29
      toolchain/check/testdata/index/expr_category.carbon
  30. 4 3
      toolchain/check/testdata/index/fail_array_large_index.carbon
  31. 4 3
      toolchain/check/testdata/index/fail_array_non_int_indexing.carbon
  32. 4 3
      toolchain/check/testdata/index/fail_array_out_of_bound_access.carbon
  33. 1 1
      toolchain/check/testdata/let/fail_duplicate_decl.carbon
  34. 2 2
      toolchain/check/testdata/let/fail_generic.carbon
  35. 8 8
      toolchain/check/testdata/let/fail_modifiers.carbon
  36. 1 1
      toolchain/check/testdata/let/fail_todo_modifiers.carbon
  37. 3 3
      toolchain/check/testdata/let/generic.carbon
  38. 2 2
      toolchain/check/testdata/let/global.carbon
  39. 2 2
      toolchain/check/testdata/operators/unary_op.carbon
  40. 3 3
      toolchain/check/testdata/struct/two_entries.carbon
  41. 3 3
      toolchain/check/testdata/tuples/two_elements.carbon
  42. 11 7
      toolchain/sem_ir/value_stores.h

+ 54 - 8
toolchain/check/context.cpp

@@ -62,9 +62,9 @@ auto Context::VerifyOnFinish() -> void {
   CARBON_CHECK(params_or_args_stack_.empty()) << params_or_args_stack_.size();
 }
 
-auto Context::AddInst(SemIR::ParseNodeAndInst parse_node_and_inst)
+auto Context::AddInstInNoBlock(SemIR::ParseNodeAndInst parse_node_and_inst)
     -> SemIR::InstId {
-  auto inst_id = inst_block_stack_.AddInst(parse_node_and_inst);
+  auto inst_id = sem_ir().insts().AddInNoBlock(parse_node_and_inst);
   CARBON_VLOG() << "AddInst: " << parse_node_and_inst.inst << "\n";
 
   auto const_id = TryEvalInst(*this, inst_id, parse_node_and_inst.inst);
@@ -77,10 +77,31 @@ auto Context::AddInst(SemIR::ParseNodeAndInst parse_node_and_inst)
   return inst_id;
 }
 
+auto Context::AddInst(SemIR::ParseNodeAndInst parse_node_and_inst)
+    -> SemIR::InstId {
+  auto inst_id = AddInstInNoBlock(parse_node_and_inst);
+  inst_block_stack_.AddInstId(inst_id);
+  return inst_id;
+}
+
+auto Context::AddPlaceholderInstInNoBlock(
+    SemIR::ParseNodeAndInst parse_node_and_inst) -> SemIR::InstId {
+  auto inst_id = sem_ir().insts().AddInNoBlock(parse_node_and_inst);
+  CARBON_VLOG() << "AddPlaceholderInst: " << parse_node_and_inst.inst << "\n";
+  return inst_id;
+}
+
+auto Context::AddPlaceholderInst(SemIR::ParseNodeAndInst parse_node_and_inst)
+    -> SemIR::InstId {
+  auto inst_id = AddPlaceholderInstInNoBlock(parse_node_and_inst);
+  inst_block_stack_.AddInstId(inst_id);
+  return inst_id;
+}
+
 auto Context::AddConstant(SemIR::Inst inst, bool is_symbolic)
     -> SemIR::ConstantId {
   // TODO: Deduplicate constants.
-  auto inst_id = insts().AddInNoBlock(
+  auto inst_id = sem_ir().insts().AddInNoBlock(
       SemIR::ParseNodeAndInst::Untyped(Parse::NodeId::Invalid, inst));
   constants().Add(inst_id);
 
@@ -98,6 +119,25 @@ auto Context::AddInstAndPush(SemIR::ParseNodeAndInst parse_node_and_inst)
   node_stack_.Push(parse_node_and_inst.parse_node, inst_id);
 }
 
+auto Context::ReplaceInstBeforeConstantUse(
+    SemIR::InstId inst_id, SemIR::ParseNodeAndInst parse_node_and_inst)
+    -> void {
+  sem_ir().insts().Set(inst_id, parse_node_and_inst);
+
+  CARBON_VLOG() << "ReplaceInst: " << inst_id << " -> "
+                << parse_node_and_inst.inst << "\n";
+
+  // Redo evaluation. This is only safe to do if this instruction has not
+  // already been used as a constant, which is the caller's responsibility to
+  // ensure.
+  auto const_id = TryEvalInst(*this, inst_id, parse_node_and_inst.inst);
+  if (const_id.is_constant()) {
+    CARBON_VLOG() << "Constant: " << parse_node_and_inst.inst << " -> "
+                  << const_id.inst_id() << "\n";
+  }
+  constant_values().Set(inst_id, const_id);
+}
+
 auto Context::DiagnoseDuplicateName(Parse::NodeId parse_node,
                                     SemIR::InstId prev_def_id) -> void {
   CARBON_DIAGNOSTIC(NameDeclDuplicate, Error,
@@ -203,9 +243,13 @@ auto Context::ResolveIfLazyImportRef(SemIR::InstId inst_id) -> void {
                            .param_refs_id = SemIR::InstBlockId::Empty,
                            .return_type_id = SemIR::TypeId::Invalid,
                            .return_slot_id = SemIR::InstId::Invalid});
-      insts().Set(inst_id, SemIR::FunctionDecl{
-                               GetBuiltinType(SemIR::BuiltinKind::FunctionType),
-                               function_id});
+      ReplaceInstBeforeConstantUse(
+          inst_id,
+          // TODO: For diagnostic purposes, we should provide some form of
+          // location for the function.
+          {Parse::NodeId::Invalid,
+           SemIR::FunctionDecl{GetBuiltinType(SemIR::BuiltinKind::FunctionType),
+                               function_id}});
       constant_values().Set(inst_id,
                             SemIR::ConstantId::ForTemplateConstant(inst_id));
       break;
@@ -218,8 +262,10 @@ auto Context::ResolveIfLazyImportRef(SemIR::InstId inst_id) -> void {
       // error.
       TODO(Parse::NodeId::Invalid,
            (llvm::Twine("TODO: support ") + import_inst.kind().name()).str());
-      insts().Set(inst_id, SemIR::VarStorage{SemIR::TypeId::Error,
-                                             SemIR::NameId::PackageNamespace});
+      ReplaceInstBeforeConstantUse(
+          inst_id, {Parse::NodeId::Invalid,
+                    SemIR::VarStorage{SemIR::TypeId::Error,
+                                      SemIR::NameId::PackageNamespace}});
       break;
   }
 }

+ 29 - 1
toolchain/check/context.h

@@ -81,6 +81,23 @@ class Context {
   // Adds an instruction to the current block, returning the produced ID.
   auto AddInst(SemIR::ParseNodeAndInst parse_node_and_inst) -> SemIR::InstId;
 
+  // Adds an instruction in no block, returning the produced ID. Should be used
+  // rarely.
+  auto AddInstInNoBlock(SemIR::ParseNodeAndInst parse_node_and_inst)
+      -> SemIR::InstId;
+
+  // Adds an instruction to the current block, returning the produced ID. The
+  // instruction is a placeholder that is expected to be replaced by
+  // `ReplaceInstBeforeConstantUse`.
+  auto AddPlaceholderInst(SemIR::ParseNodeAndInst parse_node_and_inst)
+      -> SemIR::InstId;
+
+  // Adds an instruction in no block, returning the produced ID. Should be used
+  // rarely. The instruction is a placeholder that is expected to be replaced by
+  // `ReplaceInstBeforeConstantUse`.
+  auto AddPlaceholderInstInNoBlock(SemIR::ParseNodeAndInst parse_node_and_inst)
+      -> SemIR::InstId;
+
   // Adds an instruction to the constants block, returning the produced ID.
   auto AddConstant(SemIR::Inst inst, bool is_symbolic) -> SemIR::ConstantId;
 
@@ -88,6 +105,15 @@ class Context {
   // result.
   auto AddInstAndPush(SemIR::ParseNodeAndInst parse_node_and_inst) -> void;
 
+  // Replaces the value of the instruction `inst_id` with `parse_node_and_inst`.
+  // The instruction is required to not have been used in any constant
+  // evaluation, either because it's newly created and entirely unused, or
+  // because it's only used in a position that constant evaluation ignores, such
+  // as a return slot.
+  auto ReplaceInstBeforeConstantUse(SemIR::InstId inst_id,
+                                    SemIR::ParseNodeAndInst parse_node_and_inst)
+      -> void;
+
   // Adds a package's imports to name lookup, with all libraries together.
   // sem_irs will all be non-null; has_load_error must be used for any errors.
   auto AddPackageImports(Parse::NodeId import_node, IdentifierId package_id,
@@ -404,7 +430,9 @@ class Context {
   auto type_blocks() -> SemIR::BlockValueStore<SemIR::TypeBlockId>& {
     return sem_ir().type_blocks();
   }
-  auto insts() -> SemIR::InstStore& { return sem_ir().insts(); }
+  // Instructions should be added with `AddInst` or `AddInstInNoBlock`. This is
+  // `const` to prevent accidental misuse.
+  auto insts() -> const SemIR::InstStore& { return sem_ir().insts(); }
   auto constant_values() -> SemIR::ConstantValueStore& {
     return sem_ir().constant_values();
   }

+ 5 - 6
toolchain/check/handle_binding_pattern.cpp

@@ -5,7 +5,6 @@
 #include "toolchain/check/context.h"
 #include "toolchain/check/convert.h"
 #include "toolchain/check/return.h"
-#include "toolchain/sem_ir/inst.h"
 #include "toolchain/sem_ir/value_stores.h"
 
 namespace Carbon::Check {
@@ -105,8 +104,8 @@ auto HandleAnyBindingPattern(Context& context, Parse::NodeId parse_node,
                                                      .size())}});
 
         // Add a corresponding field to the object representation of the class.
-        context.args_type_info_stack().AddInst(
-            {binding_id, SemIR::StructTypeField{name_id, cast_type_id}});
+        context.args_type_info_stack().AddInstId(context.AddInstInNoBlock(
+            {binding_id, SemIR::StructTypeField{name_id, cast_type_id}}));
       } else {
         value_id = context.AddInst(
             {name_node, SemIR::VarStorage{value_type_id, name_id}});
@@ -146,9 +145,9 @@ auto HandleAnyBindingPattern(Context& context, Parse::NodeId parse_node,
       // formed its initializer.
       // TODO: For general pattern parsing, we'll need to create a block to hold
       // the `let` pattern before we see the initializer.
-      context.node_stack().Push(parse_node,
-                                context.insts().AddInNoBlock(make_bind_name(
-                                    cast_type_id, SemIR::InstId::Invalid)));
+      context.node_stack().Push(
+          parse_node, context.AddPlaceholderInstInNoBlock(make_bind_name(
+                          cast_type_id, SemIR::InstId::Invalid)));
       break;
 
     default:

+ 4 - 4
toolchain/check/handle_class.cpp

@@ -55,7 +55,7 @@ static auto BuildClassDecl(Context& context, Parse::AnyClassDeclId parse_node)
 
   // Add the class declaration.
   auto class_decl = SemIR::ClassDecl{SemIR::ClassId::Invalid, decl_block_id};
-  auto class_decl_id = context.AddInst({parse_node, class_decl});
+  auto class_decl_id = context.AddPlaceholderInst({parse_node, class_decl});
 
   // Check whether this is a redeclaration.
   auto existing_id =
@@ -110,7 +110,7 @@ static auto BuildClassDecl(Context& context, Parse::AnyClassDeclId parse_node)
   }
 
   // Write the class ID into the ClassDecl.
-  context.insts().Set(class_decl_id, class_decl);
+  context.ReplaceInstBeforeConstantUse(class_decl_id, {parse_node, class_decl});
 
   return {class_decl.class_id, class_decl_id};
 }
@@ -304,9 +304,9 @@ auto HandleBaseDecl(Context& context, Parse::BaseDeclId parse_node) -> bool {
 
   // Add a corresponding field to the object representation of the class.
   // TODO: Consider whether we want to use `partial T` here.
-  context.args_type_info_stack().AddInst(
+  context.args_type_info_stack().AddInstId(context.AddInstInNoBlock(
       {parse_node,
-       SemIR::StructTypeField{SemIR::NameId::Base, base_info.type_id}});
+       SemIR::StructTypeField{SemIR::NameId::Base, base_info.type_id}}));
 
   // Bind the name `base` in the class to the base field.
   context.decl_name_stack().AddNameToLookup(

+ 4 - 2
toolchain/check/handle_function.cpp

@@ -138,7 +138,8 @@ static auto BuildFunctionDecl(Context& context,
   auto function_decl = SemIR::FunctionDecl{
       context.GetBuiltinType(SemIR::BuiltinKind::FunctionType),
       SemIR::FunctionId::Invalid};
-  auto function_decl_id = context.AddInst({parse_node, function_decl});
+  auto function_decl_id =
+      context.AddPlaceholderInst({parse_node, function_decl});
 
   // Check whether this is a redeclaration.
   auto existing_id =
@@ -180,7 +181,8 @@ static auto BuildFunctionDecl(Context& context,
   }
 
   // Write the function ID into the FunctionDecl.
-  context.insts().Set(function_decl_id, function_decl);
+  context.ReplaceInstBeforeConstantUse(function_decl_id,
+                                       {parse_node, function_decl});
 
   if (SemIR::IsEntryPoint(context.sem_ir(), function_decl.function_id)) {
     // TODO: Update this once valid signatures for the entry point are decided.

+ 4 - 2
toolchain/check/handle_interface.cpp

@@ -52,7 +52,8 @@ static auto BuildInterfaceDecl(Context& context,
   // Add the interface declaration.
   auto interface_decl =
       SemIR::InterfaceDecl{SemIR::InterfaceId::Invalid, decl_block_id};
-  auto interface_decl_id = context.AddInst({parse_node, interface_decl});
+  auto interface_decl_id =
+      context.AddPlaceholderInst({parse_node, interface_decl});
 
   // Check whether this is a redeclaration.
   auto existing_id = context.decl_name_stack().LookupOrAddName(
@@ -85,7 +86,8 @@ static auto BuildInterfaceDecl(Context& context,
   }
 
   // Write the interface ID into the InterfaceDecl.
-  context.insts().Set(interface_decl_id, interface_decl);
+  context.ReplaceInstBeforeConstantUse(interface_decl_id,
+                                       {parse_node, interface_decl});
 
   return {interface_decl.interface_id, interface_decl_id};
 }

+ 6 - 5
toolchain/check/handle_let.cpp

@@ -49,18 +49,19 @@ auto HandleLetDecl(Context& context, Parse::LetDeclId parse_node) -> bool {
   context.decl_state_stack().Pop(DeclState::Let);
 
   // Convert the value to match the type of the pattern.
-  auto pattern = context.insts().Get(pattern_id);
-  value_id =
-      ConvertToValueOfType(context, parse_node, value_id, pattern.type_id());
+  auto pattern = context.insts().GetWithParseNode(pattern_id);
+  value_id = ConvertToValueOfType(context, parse_node, value_id,
+                                  pattern.inst.type_id());
 
   // Update the binding with its value and add it to the current block, after
   // the computation of the value.
   // TODO: Support other kinds of pattern here.
-  auto bind_name = pattern.As<SemIR::AnyBindName>();
+  auto bind_name = pattern.inst.As<SemIR::AnyBindName>();
   CARBON_CHECK(!bind_name.value_id.is_valid())
       << "Binding should not already have a value!";
   bind_name.value_id = value_id;
-  context.insts().Set(pattern_id, bind_name);
+  pattern.inst = bind_name;
+  context.ReplaceInstBeforeConstantUse(pattern_id, pattern);
   context.inst_block_stack().AddInstId(pattern_id);
 
   // Add the name of the binding to the current scope.

+ 3 - 3
toolchain/check/handle_namespace.cpp

@@ -6,7 +6,6 @@
 #include "toolchain/check/decl_state.h"
 #include "toolchain/check/modifiers.h"
 #include "toolchain/sem_ir/ids.h"
-#include "toolchain/sem_ir/inst.h"
 
 namespace Carbon::Check {
 
@@ -25,10 +24,11 @@ auto HandleNamespace(Context& context, Parse::NamespaceId parse_node) -> bool {
   auto namespace_inst = SemIR::Namespace{
       context.GetBuiltinType(SemIR::BuiltinKind::NamespaceType),
       name_context.name_id_for_new_inst(), SemIR::NameScopeId::Invalid};
-  auto namespace_id = context.AddInst({parse_node, namespace_inst});
+  auto namespace_id = context.AddPlaceholderInst({parse_node, namespace_inst});
   namespace_inst.name_scope_id = context.name_scopes().Add(
       namespace_id, name_context.enclosing_scope_id_for_new_inst());
-  context.insts().Set(namespace_id, namespace_inst);
+  context.ReplaceInstBeforeConstantUse(namespace_id,
+                                       {parse_node, namespace_inst});
   context.decl_name_stack().AddNameToLookup(name_context, namespace_id);
 
   context.decl_name_stack().PopScope();

+ 2 - 2
toolchain/check/handle_struct.cpp

@@ -40,9 +40,9 @@ auto HandleStructFieldValue(Context& context,
   auto [name_node, name_id] = context.node_stack().PopNameWithParseNode();
 
   // Store the name for the type.
-  context.args_type_info_stack().AddInst(
+  context.args_type_info_stack().AddInstId(context.AddInstInNoBlock(
       {name_node, SemIR::StructTypeField{
-                      name_id, context.insts().Get(value_inst_id).type_id()}});
+                      name_id, context.insts().Get(value_inst_id).type_id()}}));
 
   // Push the value back on the stack as an argument.
   context.node_stack().Push(parse_node, value_inst_id);

+ 3 - 3
toolchain/check/import.cpp

@@ -71,9 +71,9 @@ static auto AddNamespace(Context& context,
   // Use the invalid node because there's no node to associate with.
   auto inst =
       SemIR::Namespace{namespace_type_id, name_id, SemIR::NameScopeId::Invalid};
-  auto id = context.AddInst({Parse::NodeId::Invalid, inst});
+  auto id = context.AddPlaceholderInst({Parse::NodeId::Invalid, inst});
   inst.name_scope_id = context.name_scopes().Add(id, enclosing_scope_id);
-  context.insts().Set(id, inst);
+  context.ReplaceInstBeforeConstantUse(id, {Parse::NodeId::Invalid, inst});
   return {id, inst.name_scope_id};
 }
 
@@ -194,7 +194,7 @@ auto Import(Context& context, SemIR::TypeId namespace_type_id,
                            import_namespace_inst->name_scope_id, name_scope_id);
     } else {
       // Leave a placeholder that the inst comes from the other IR.
-      auto target_id = context.AddInst(
+      auto target_id = context.AddPlaceholderInst(
           {Parse::NodeId::Invalid,
            SemIR::LazyImportRef{.ir_id = ir_id, .inst_id = import_inst_id}});
       // TODO: When importing from other packages, the scope's names should

+ 0 - 1
toolchain/check/inst_block_stack.cpp

@@ -7,7 +7,6 @@
 #include "common/vlog.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/StringExtras.h"
-#include "toolchain/sem_ir/inst.h"
 
 namespace Carbon::Check {
 

+ 0 - 10
toolchain/check/inst_block_stack.h

@@ -7,8 +7,6 @@
 
 #include "llvm/ADT/SmallVector.h"
 #include "toolchain/sem_ir/file.h"
-#include "toolchain/sem_ir/inst.h"
-#include "toolchain/sem_ir/value_stores.h"
 
 namespace Carbon::Check {
 
@@ -48,14 +46,6 @@ class InstBlockStack {
   // allocated.
   auto PopAndDiscard() -> void;
 
-  // Adds the given instruction to the block at the top of the stack and returns
-  // its ID.
-  auto AddInst(SemIR::ParseNodeAndInst parse_node_and_inst) -> SemIR::InstId {
-    auto inst_id = sem_ir_->insts().AddInNoBlock(parse_node_and_inst);
-    AddInstId(inst_id);
-    return inst_id;
-  }
-
   // Adds the given instruction ID to the block at the top of the stack.
   auto AddInstId(SemIR::InstId inst_id) -> void {
     CARBON_CHECK(!empty()) << "no current block";

+ 11 - 11
toolchain/check/pending_block.h

@@ -41,7 +41,7 @@ class PendingBlock {
   };
 
   auto AddInst(SemIR::ParseNodeAndInst parse_node_and_inst) -> SemIR::InstId {
-    auto inst_id = context_.insts().AddInNoBlock(parse_node_and_inst);
+    auto inst_id = context_.AddInstInNoBlock(parse_node_and_inst);
     insts_.push_back(inst_id);
     return inst_id;
   }
@@ -57,29 +57,29 @@ class PendingBlock {
   // Replace the instruction at target_id with the instructions in this block.
   // The new value for target_id should be value_id.
   auto MergeReplacing(SemIR::InstId target_id, SemIR::InstId value_id) -> void {
-    auto value = context_.insts().Get(value_id);
+    auto value = context_.insts().GetWithParseNode(value_id);
 
     // There are three cases here:
 
     if (insts_.empty()) {
       // 1) The block is empty. Replace `target_id` with an empty splice
       // pointing at `value_id`.
-      context_.insts().Set(
-          target_id, SemIR::SpliceBlock{value.type_id(),
-                                        SemIR::InstBlockId::Empty, value_id});
+      context_.ReplaceInstBeforeConstantUse(
+          target_id, {value.parse_node,
+                      SemIR::SpliceBlock{value.inst.type_id(),
+                                         SemIR::InstBlockId::Empty, value_id}});
     } else if (insts_.size() == 1 && insts_[0] == value_id) {
       // 2) The block is {value_id}. Replace `target_id` with the instruction
       // referred to by `value_id`. This is intended to be the common case.
-      context_.insts().Set(target_id, value);
+      context_.ReplaceInstBeforeConstantUse(target_id, value);
     } else {
       // 3) Anything else: splice it into the IR, replacing `target_id`.
-      context_.insts().Set(
+      context_.ReplaceInstBeforeConstantUse(
           target_id,
-          SemIR::SpliceBlock{value.type_id(),
-                             context_.inst_blocks().Add(insts_), value_id});
+          {value.parse_node,
+           SemIR::SpliceBlock{value.inst.type_id(),
+                              context_.inst_blocks().Add(insts_), value_id}});
     }
-    context_.insts().SetParseNode(target_id,
-                                  context_.insts().GetParseNode(value_id));
 
     // Prepare to stash more pending instructions.
     insts_.clear();

+ 4 - 2
toolchain/check/testdata/array/array_in_place.carbon

@@ -20,6 +20,8 @@ fn G() {
 // CHECK:STDOUT:   %.5: type = array_type %.4, (i32, i32, i32) [template]
 // CHECK:STDOUT:   %.6: type = ptr_type [(i32, i32, i32); 2] [template]
 // CHECK:STDOUT:   %.7: type = tuple_type ((i32, i32, i32), (i32, i32, i32)) [template]
+// CHECK:STDOUT:   %.8: i32 = int_literal 0 [template]
+// CHECK:STDOUT:   %.9: i32 = int_literal 1 [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -40,13 +42,13 @@ fn G() {
 // CHECK:STDOUT:   %v: ref [(i32, i32, i32); 2] = bind_name v, %v.var
 // CHECK:STDOUT:   %F.ref.loc10_34: <function> = name_ref F, file.%F [template = file.%F]
 // CHECK:STDOUT:   %.loc10_42.3: ref (i32, i32, i32) = splice_block %.loc10_42.2 {
-// CHECK:STDOUT:     %.loc10_42.1: i32 = int_literal 0
+// CHECK:STDOUT:     %.loc10_42.1: i32 = int_literal 0 [template = constants.%.8]
 // CHECK:STDOUT:     %.loc10_42.2: ref (i32, i32, i32) = array_index %v.var, %.loc10_42.1
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.loc10_35: init (i32, i32, i32) = call %F.ref.loc10_34() to %.loc10_42.3
 // CHECK:STDOUT:   %F.ref.loc10_39: <function> = name_ref F, file.%F [template = file.%F]
 // CHECK:STDOUT:   %.loc10_42.6: ref (i32, i32, i32) = splice_block %.loc10_42.5 {
-// CHECK:STDOUT:     %.loc10_42.4: i32 = int_literal 1
+// CHECK:STDOUT:     %.loc10_42.4: i32 = int_literal 1 [template = constants.%.9]
 // CHECK:STDOUT:     %.loc10_42.5: ref (i32, i32, i32) = array_index %v.var, %.loc10_42.4
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.loc10_40: init (i32, i32, i32) = call %F.ref.loc10_39() to %.loc10_42.6

+ 2 - 1
toolchain/check/testdata/array/assign_return_value.carbon

@@ -20,6 +20,7 @@ fn Run() {
 // CHECK:STDOUT:   %.5: i32 = int_literal 1 [template]
 // CHECK:STDOUT:   %.6: type = array_type %.5, i32 [template]
 // CHECK:STDOUT:   %.7: type = ptr_type [i32; 1] [template]
+// CHECK:STDOUT:   %.8: i32 = int_literal 0 [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -49,7 +50,7 @@ fn Run() {
 // CHECK:STDOUT:   %.loc10_22.3: ref (i32,) = temporary %.loc10_22.2, %.loc10_22.1
 // CHECK:STDOUT:   %.loc10_22.4: ref i32 = tuple_access %.loc10_22.3, element0
 // CHECK:STDOUT:   %.loc10_22.5: i32 = bind_value %.loc10_22.4
-// CHECK:STDOUT:   %.loc10_22.6: i32 = int_literal 0
+// CHECK:STDOUT:   %.loc10_22.6: i32 = int_literal 0 [template = constants.%.8]
 // CHECK:STDOUT:   %.loc10_22.7: ref i32 = array_index %t.var, %.loc10_22.6
 // CHECK:STDOUT:   %.loc10_22.8: init i32 = initialize_from %.loc10_22.5 to %.loc10_22.7
 // CHECK:STDOUT:   %.loc10_22.9: init [i32; 1] = array_init (%.loc10_22.8) to %t.var

+ 6 - 3
toolchain/check/testdata/array/assign_var.carbon

@@ -19,6 +19,9 @@ var b: [i32; 3] = a;
 // CHECK:STDOUT:   %.7: i32 = int_literal 3 [template]
 // CHECK:STDOUT:   %.8: type = array_type %.7, i32 [template]
 // CHECK:STDOUT:   %.9: type = ptr_type [i32; 3] [template]
+// CHECK:STDOUT:   %.10: i32 = int_literal 0 [template]
+// CHECK:STDOUT:   %.11: i32 = int_literal 1 [template]
+// CHECK:STDOUT:   %.12: i32 = int_literal 2 [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -47,17 +50,17 @@ var b: [i32; 3] = a;
 // CHECK:STDOUT:   %a.ref: ref (i32, i32, i32) = name_ref a, %a
 // CHECK:STDOUT:   %.loc8_19.1: ref i32 = tuple_access %a.ref, element0
 // CHECK:STDOUT:   %.loc8_19.2: i32 = bind_value %.loc8_19.1
-// CHECK:STDOUT:   %.loc8_19.3: i32 = int_literal 0
+// CHECK:STDOUT:   %.loc8_19.3: i32 = int_literal 0 [template = constants.%.10]
 // CHECK:STDOUT:   %.loc8_19.4: ref i32 = array_index %b.var, %.loc8_19.3
 // CHECK:STDOUT:   %.loc8_19.5: init i32 = initialize_from %.loc8_19.2 to %.loc8_19.4
 // CHECK:STDOUT:   %.loc8_19.6: ref i32 = tuple_access %a.ref, element1
 // CHECK:STDOUT:   %.loc8_19.7: i32 = bind_value %.loc8_19.6
-// CHECK:STDOUT:   %.loc8_19.8: i32 = int_literal 1
+// CHECK:STDOUT:   %.loc8_19.8: i32 = int_literal 1 [template = constants.%.11]
 // CHECK:STDOUT:   %.loc8_19.9: ref i32 = array_index %b.var, %.loc8_19.8
 // CHECK:STDOUT:   %.loc8_19.10: init i32 = initialize_from %.loc8_19.7 to %.loc8_19.9
 // CHECK:STDOUT:   %.loc8_19.11: ref i32 = tuple_access %a.ref, element2
 // CHECK:STDOUT:   %.loc8_19.12: i32 = bind_value %.loc8_19.11
-// CHECK:STDOUT:   %.loc8_19.13: i32 = int_literal 2
+// CHECK:STDOUT:   %.loc8_19.13: i32 = int_literal 2 [template = constants.%.12]
 // CHECK:STDOUT:   %.loc8_19.14: ref i32 = array_index %b.var, %.loc8_19.13
 // CHECK:STDOUT:   %.loc8_19.15: init i32 = initialize_from %.loc8_19.12 to %.loc8_19.14
 // CHECK:STDOUT:   %.loc8_19.16: init [i32; 3] = array_init (%.loc8_19.5, %.loc8_19.10, %.loc8_19.15) to %b.var

+ 34 - 26
toolchain/check/testdata/array/base.carbon

@@ -16,17 +16,25 @@ var c: [(); 5] = ((), (), (), (), (),);
 // CHECK:STDOUT:   %.3: type = ptr_type [i32; 1] [template]
 // CHECK:STDOUT:   %.4: i32 = int_literal 1 [template]
 // CHECK:STDOUT:   %.5: type = tuple_type (i32) [template]
-// CHECK:STDOUT:   %.6: i32 = int_literal 2 [template]
-// CHECK:STDOUT:   %.7: type = array_type %.6, f64 [template]
-// CHECK:STDOUT:   %.8: type = ptr_type [f64; 2] [template]
-// CHECK:STDOUT:   %.9: f64 = real_literal 111e-1 [template]
-// CHECK:STDOUT:   %.10: f64 = real_literal 22e-1 [template]
-// CHECK:STDOUT:   %.11: type = tuple_type (f64, f64) [template]
-// CHECK:STDOUT:   %.12: type = tuple_type () [template]
-// CHECK:STDOUT:   %.13: i32 = int_literal 5 [template]
-// CHECK:STDOUT:   %.14: type = array_type %.13, () [template]
-// CHECK:STDOUT:   %.15: type = ptr_type [(); 5] [template]
-// CHECK:STDOUT:   %.16: type = tuple_type ((), (), (), (), ()) [template]
+// CHECK:STDOUT:   %.6: i32 = int_literal 0 [template]
+// CHECK:STDOUT:   %.7: i32 = int_literal 2 [template]
+// CHECK:STDOUT:   %.8: type = array_type %.7, f64 [template]
+// CHECK:STDOUT:   %.9: type = ptr_type [f64; 2] [template]
+// CHECK:STDOUT:   %.10: f64 = real_literal 111e-1 [template]
+// CHECK:STDOUT:   %.11: f64 = real_literal 22e-1 [template]
+// CHECK:STDOUT:   %.12: type = tuple_type (f64, f64) [template]
+// CHECK:STDOUT:   %.13: i32 = int_literal 0 [template]
+// CHECK:STDOUT:   %.14: i32 = int_literal 1 [template]
+// CHECK:STDOUT:   %.15: type = tuple_type () [template]
+// CHECK:STDOUT:   %.16: i32 = int_literal 5 [template]
+// CHECK:STDOUT:   %.17: type = array_type %.16, () [template]
+// CHECK:STDOUT:   %.18: type = ptr_type [(); 5] [template]
+// CHECK:STDOUT:   %.19: type = tuple_type ((), (), (), (), ()) [template]
+// CHECK:STDOUT:   %.20: i32 = int_literal 0 [template]
+// CHECK:STDOUT:   %.21: i32 = int_literal 1 [template]
+// CHECK:STDOUT:   %.22: i32 = int_literal 2 [template]
+// CHECK:STDOUT:   %.23: i32 = int_literal 3 [template]
+// CHECK:STDOUT:   %.24: i32 = int_literal 4 [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -37,32 +45,32 @@ var c: [(); 5] = ((), (), (), (), (),);
 // CHECK:STDOUT:   %a: ref [i32; 1] = bind_name a, %a.var
 // CHECK:STDOUT:   %.loc7_20: i32 = int_literal 1 [template = constants.%.4]
 // CHECK:STDOUT:   %.loc7_22.1: (i32,) = tuple_literal (%.loc7_20)
-// CHECK:STDOUT:   %.loc7_22.2: i32 = int_literal 0
+// CHECK:STDOUT:   %.loc7_22.2: i32 = int_literal 0 [template = constants.%.6]
 // CHECK:STDOUT:   %.loc7_22.3: ref i32 = array_index %a.var, %.loc7_22.2
 // CHECK:STDOUT:   %.loc7_22.4: init i32 = initialize_from %.loc7_20 to %.loc7_22.3
 // CHECK:STDOUT:   %.loc7_22.5: init [i32; 1] = array_init (%.loc7_22.4) to %a.var
 // CHECK:STDOUT:   %.loc7_22.6: init [i32; 1] = converted %.loc7_22.1, %.loc7_22.5
 // CHECK:STDOUT:   assign %a.var, %.loc7_22.6
-// CHECK:STDOUT:   %.loc8_14: i32 = int_literal 2 [template = constants.%.6]
-// CHECK:STDOUT:   %.loc8_15: type = array_type %.loc8_14, f64 [template = constants.%.7]
+// CHECK:STDOUT:   %.loc8_14: i32 = int_literal 2 [template = constants.%.7]
+// CHECK:STDOUT:   %.loc8_15: type = array_type %.loc8_14, f64 [template = constants.%.8]
 // CHECK:STDOUT:   %b.var: ref [f64; 2] = var b
 // CHECK:STDOUT:   %b: ref [f64; 2] = bind_name b, %b.var
-// CHECK:STDOUT:   %.loc8_20: f64 = real_literal 111e-1 [template = constants.%.9]
-// CHECK:STDOUT:   %.loc8_26: f64 = real_literal 22e-1 [template = constants.%.10]
+// CHECK:STDOUT:   %.loc8_20: f64 = real_literal 111e-1 [template = constants.%.10]
+// CHECK:STDOUT:   %.loc8_26: f64 = real_literal 22e-1 [template = constants.%.11]
 // CHECK:STDOUT:   %.loc8_30.1: (f64, f64) = tuple_literal (%.loc8_20, %.loc8_26)
-// CHECK:STDOUT:   %.loc8_30.2: i32 = int_literal 0
+// CHECK:STDOUT:   %.loc8_30.2: i32 = int_literal 0 [template = constants.%.13]
 // CHECK:STDOUT:   %.loc8_30.3: ref f64 = array_index %b.var, %.loc8_30.2
 // CHECK:STDOUT:   %.loc8_30.4: init f64 = initialize_from %.loc8_20 to %.loc8_30.3
-// CHECK:STDOUT:   %.loc8_30.5: i32 = int_literal 1
+// CHECK:STDOUT:   %.loc8_30.5: i32 = int_literal 1 [template = constants.%.14]
 // CHECK:STDOUT:   %.loc8_30.6: ref f64 = array_index %b.var, %.loc8_30.5
 // CHECK:STDOUT:   %.loc8_30.7: init f64 = initialize_from %.loc8_26 to %.loc8_30.6
 // CHECK:STDOUT:   %.loc8_30.8: init [f64; 2] = array_init (%.loc8_30.4, %.loc8_30.7) to %b.var
 // CHECK:STDOUT:   %.loc8_30.9: init [f64; 2] = converted %.loc8_30.1, %.loc8_30.8
 // CHECK:STDOUT:   assign %b.var, %.loc8_30.9
 // CHECK:STDOUT:   %.loc9_10.1: () = tuple_literal ()
-// CHECK:STDOUT:   %.loc9_13: i32 = int_literal 5 [template = constants.%.13]
-// CHECK:STDOUT:   %.loc9_10.2: type = converted %.loc9_10.1, constants.%.12 [template = constants.%.12]
-// CHECK:STDOUT:   %.loc9_14: type = array_type %.loc9_13, () [template = constants.%.14]
+// CHECK:STDOUT:   %.loc9_13: i32 = int_literal 5 [template = constants.%.16]
+// CHECK:STDOUT:   %.loc9_10.2: type = converted %.loc9_10.1, constants.%.15 [template = constants.%.15]
+// CHECK:STDOUT:   %.loc9_14: type = array_type %.loc9_13, () [template = constants.%.17]
 // CHECK:STDOUT:   %c.var: ref [(); 5] = var c
 // CHECK:STDOUT:   %c: ref [(); 5] = bind_name c, %c.var
 // CHECK:STDOUT:   %.loc9_20.1: () = tuple_literal ()
@@ -71,23 +79,23 @@ var c: [(); 5] = ((), (), (), (), (),);
 // CHECK:STDOUT:   %.loc9_32.1: () = tuple_literal ()
 // CHECK:STDOUT:   %.loc9_36.1: () = tuple_literal ()
 // CHECK:STDOUT:   %.loc9_38.1: ((), (), (), (), ()) = tuple_literal (%.loc9_20.1, %.loc9_24.1, %.loc9_28.1, %.loc9_32.1, %.loc9_36.1)
-// CHECK:STDOUT:   %.loc9_38.2: i32 = int_literal 0
+// CHECK:STDOUT:   %.loc9_38.2: i32 = int_literal 0 [template = constants.%.20]
 // CHECK:STDOUT:   %.loc9_38.3: ref () = array_index %c.var, %.loc9_38.2
 // CHECK:STDOUT:   %.loc9_20.2: init () = tuple_init () to %.loc9_38.3
 // CHECK:STDOUT:   %.loc9_20.3: init () = converted %.loc9_20.1, %.loc9_20.2
-// CHECK:STDOUT:   %.loc9_38.4: i32 = int_literal 1
+// CHECK:STDOUT:   %.loc9_38.4: i32 = int_literal 1 [template = constants.%.21]
 // CHECK:STDOUT:   %.loc9_38.5: ref () = array_index %c.var, %.loc9_38.4
 // CHECK:STDOUT:   %.loc9_24.2: init () = tuple_init () to %.loc9_38.5
 // CHECK:STDOUT:   %.loc9_24.3: init () = converted %.loc9_24.1, %.loc9_24.2
-// CHECK:STDOUT:   %.loc9_38.6: i32 = int_literal 2
+// CHECK:STDOUT:   %.loc9_38.6: i32 = int_literal 2 [template = constants.%.22]
 // CHECK:STDOUT:   %.loc9_38.7: ref () = array_index %c.var, %.loc9_38.6
 // CHECK:STDOUT:   %.loc9_28.2: init () = tuple_init () to %.loc9_38.7
 // CHECK:STDOUT:   %.loc9_28.3: init () = converted %.loc9_28.1, %.loc9_28.2
-// CHECK:STDOUT:   %.loc9_38.8: i32 = int_literal 3
+// CHECK:STDOUT:   %.loc9_38.8: i32 = int_literal 3 [template = constants.%.23]
 // CHECK:STDOUT:   %.loc9_38.9: ref () = array_index %c.var, %.loc9_38.8
 // CHECK:STDOUT:   %.loc9_32.2: init () = tuple_init () to %.loc9_38.9
 // CHECK:STDOUT:   %.loc9_32.3: init () = converted %.loc9_32.1, %.loc9_32.2
-// CHECK:STDOUT:   %.loc9_38.10: i32 = int_literal 4
+// CHECK:STDOUT:   %.loc9_38.10: i32 = int_literal 4 [template = constants.%.24]
 // CHECK:STDOUT:   %.loc9_38.11: ref () = array_index %c.var, %.loc9_38.10
 // CHECK:STDOUT:   %.loc9_36.2: init () = tuple_init () to %.loc9_38.11
 // CHECK:STDOUT:   %.loc9_36.3: init () = converted %.loc9_36.1, %.loc9_36.2

+ 27 - 23
toolchain/check/testdata/array/fail_type_mismatch.carbon

@@ -37,20 +37,24 @@ var d: [i32; 3] = t2;
 // CHECK:STDOUT:   %.6: String = string_literal "Hello" [template]
 // CHECK:STDOUT:   %.7: String = string_literal "World" [template]
 // CHECK:STDOUT:   %.8: type = tuple_type (i32, String, String) [template]
-// CHECK:STDOUT:   %.9: type = tuple_type (type, type, type) [template]
-// CHECK:STDOUT:   %.10: type = tuple_type (i32, String*, String*) [template]
-// CHECK:STDOUT:   %.11: type = ptr_type (i32, String*, String*) [template]
-// CHECK:STDOUT:   %.12: i32 = int_literal 3 [template]
-// CHECK:STDOUT:   %.13: type = array_type %.12, i32 [template]
+// CHECK:STDOUT:   %.9: i32 = int_literal 0 [template]
+// CHECK:STDOUT:   %.10: i32 = int_literal 1 [template]
+// CHECK:STDOUT:   %.11: type = tuple_type (type, type, type) [template]
+// CHECK:STDOUT:   %.12: type = tuple_type (i32, String*, String*) [template]
+// CHECK:STDOUT:   %.13: type = ptr_type (i32, String*, String*) [template]
 // CHECK:STDOUT:   %.14: i32 = int_literal 3 [template]
 // CHECK:STDOUT:   %.15: type = array_type %.14, i32 [template]
-// CHECK:STDOUT:   %.16: i32 = int_literal 1 [template]
-// CHECK:STDOUT:   %.17: i32 = int_literal 2 [template]
-// CHECK:STDOUT:   %.18: type = tuple_type (i32, i32) [template]
-// CHECK:STDOUT:   %.19: type = tuple_type (type, type) [template]
-// CHECK:STDOUT:   %.20: type = ptr_type (i32, i32) [template]
-// CHECK:STDOUT:   %.21: i32 = int_literal 3 [template]
-// CHECK:STDOUT:   %.22: type = array_type %.21, i32 [template]
+// CHECK:STDOUT:   %.16: i32 = int_literal 0 [template]
+// CHECK:STDOUT:   %.17: i32 = int_literal 1 [template]
+// CHECK:STDOUT:   %.18: i32 = int_literal 3 [template]
+// CHECK:STDOUT:   %.19: type = array_type %.18, i32 [template]
+// CHECK:STDOUT:   %.20: i32 = int_literal 1 [template]
+// CHECK:STDOUT:   %.21: i32 = int_literal 2 [template]
+// CHECK:STDOUT:   %.22: type = tuple_type (i32, i32) [template]
+// CHECK:STDOUT:   %.23: type = tuple_type (type, type) [template]
+// CHECK:STDOUT:   %.24: type = ptr_type (i32, i32) [template]
+// CHECK:STDOUT:   %.25: i32 = int_literal 3 [template]
+// CHECK:STDOUT:   %.26: type = array_type %.25, i32 [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -63,7 +67,7 @@ var d: [i32; 3] = t2;
 // CHECK:STDOUT:   %.loc10_23: String = string_literal "Hello" [template = constants.%.6]
 // CHECK:STDOUT:   %.loc10_32: String = string_literal "World" [template = constants.%.7]
 // CHECK:STDOUT:   %.loc10_39.1: (i32, String, String) = tuple_literal (%.loc10_20, %.loc10_23, %.loc10_32)
-// CHECK:STDOUT:   %.loc10_39.2: i32 = int_literal 0
+// CHECK:STDOUT:   %.loc10_39.2: i32 = int_literal 0 [template = constants.%.9]
 // CHECK:STDOUT:   %.loc10_39.3: ref i32 = array_index %a.var, %.loc10_39.2
 // CHECK:STDOUT:   %.loc10_39.4: init i32 = initialize_from %.loc10_20 to %.loc10_39.3
 // CHECK:STDOUT:   assign %a.var, <error>
@@ -71,32 +75,32 @@ var d: [i32; 3] = t2;
 // CHECK:STDOUT:   %.loc12_29.2: type = converted %.loc12_29.1, constants.%.8 [template = constants.%.8]
 // CHECK:STDOUT:   %t1.var: ref (i32, String, String) = var t1
 // CHECK:STDOUT:   %t1: ref (i32, String, String) = bind_name t1, %t1.var
-// CHECK:STDOUT:   %.loc16_14: i32 = int_literal 3 [template = constants.%.12]
-// CHECK:STDOUT:   %.loc16_15: type = array_type %.loc16_14, i32 [template = constants.%.13]
+// CHECK:STDOUT:   %.loc16_14: i32 = int_literal 3 [template = constants.%.14]
+// CHECK:STDOUT:   %.loc16_15: type = array_type %.loc16_14, i32 [template = constants.%.15]
 // CHECK:STDOUT:   %b.var: ref [i32; 3] = var b
 // CHECK:STDOUT:   %b: ref [i32; 3] = bind_name b, %b.var
 // CHECK:STDOUT:   %t1.ref: ref (i32, String, String) = name_ref t1, %t1
 // CHECK:STDOUT:   %.loc16_19.1: ref i32 = tuple_access %t1.ref, element0
 // CHECK:STDOUT:   %.loc16_19.2: i32 = bind_value %.loc16_19.1
-// CHECK:STDOUT:   %.loc16_19.3: i32 = int_literal 0
+// CHECK:STDOUT:   %.loc16_19.3: i32 = int_literal 0 [template = constants.%.16]
 // CHECK:STDOUT:   %.loc16_19.4: ref i32 = array_index %b.var, %.loc16_19.3
 // CHECK:STDOUT:   %.loc16_19.5: init i32 = initialize_from %.loc16_19.2 to %.loc16_19.4
 // CHECK:STDOUT:   %.loc16_19.6: ref String = tuple_access %t1.ref, element1
 // CHECK:STDOUT:   assign %b.var, <error>
-// CHECK:STDOUT:   %.loc21_14: i32 = int_literal 3 [template = constants.%.14]
-// CHECK:STDOUT:   %.loc21_15: type = array_type %.loc21_14, i32 [template = constants.%.15]
+// CHECK:STDOUT:   %.loc21_14: i32 = int_literal 3 [template = constants.%.18]
+// CHECK:STDOUT:   %.loc21_15: type = array_type %.loc21_14, i32 [template = constants.%.19]
 // CHECK:STDOUT:   %c.var: ref [i32; 3] = var c
 // CHECK:STDOUT:   %c: ref [i32; 3] = bind_name c, %c.var
-// CHECK:STDOUT:   %.loc21_20: i32 = int_literal 1 [template = constants.%.16]
-// CHECK:STDOUT:   %.loc21_23: i32 = int_literal 2 [template = constants.%.17]
+// CHECK:STDOUT:   %.loc21_20: i32 = int_literal 1 [template = constants.%.20]
+// CHECK:STDOUT:   %.loc21_23: i32 = int_literal 2 [template = constants.%.21]
 // CHECK:STDOUT:   %.loc21_24: (i32, i32) = tuple_literal (%.loc21_20, %.loc21_23)
 // CHECK:STDOUT:   assign %c.var, <error>
 // CHECK:STDOUT:   %.loc23_18.1: (type, type) = tuple_literal (i32, i32)
-// CHECK:STDOUT:   %.loc23_18.2: type = converted %.loc23_18.1, constants.%.18 [template = constants.%.18]
+// CHECK:STDOUT:   %.loc23_18.2: type = converted %.loc23_18.1, constants.%.22 [template = constants.%.22]
 // CHECK:STDOUT:   %t2.var: ref (i32, i32) = var t2
 // CHECK:STDOUT:   %t2: ref (i32, i32) = bind_name t2, %t2.var
-// CHECK:STDOUT:   %.loc27_14: i32 = int_literal 3 [template = constants.%.21]
-// CHECK:STDOUT:   %.loc27_15: type = array_type %.loc27_14, i32 [template = constants.%.22]
+// CHECK:STDOUT:   %.loc27_14: i32 = int_literal 3 [template = constants.%.25]
+// CHECK:STDOUT:   %.loc27_15: type = array_type %.loc27_14, i32 [template = constants.%.26]
 // CHECK:STDOUT:   %d.var: ref [i32; 3] = var d
 // CHECK:STDOUT:   %d: ref [i32; 3] = bind_name d, %d.var
 // CHECK:STDOUT:   %t2.ref: ref (i32, i32) = name_ref t2, %t2

+ 6 - 3
toolchain/check/testdata/array/function_param.carbon

@@ -23,6 +23,9 @@ fn G() -> i32 {
 // CHECK:STDOUT:   %.6: i32 = int_literal 3 [template]
 // CHECK:STDOUT:   %.7: type = tuple_type (i32, i32, i32) [template]
 // CHECK:STDOUT:   %.8: i32 = int_literal 1 [template]
+// CHECK:STDOUT:   %.9: i32 = int_literal 0 [template]
+// CHECK:STDOUT:   %.10: i32 = int_literal 1 [template]
+// CHECK:STDOUT:   %.11: i32 = int_literal 2 [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -50,13 +53,13 @@ fn G() -> i32 {
 // CHECK:STDOUT:   %.loc12_20.1: (i32, i32, i32) = tuple_literal (%.loc12_13, %.loc12_16, %.loc12_19)
 // CHECK:STDOUT:   %.loc12_23: i32 = int_literal 1 [template = constants.%.8]
 // CHECK:STDOUT:   %.loc12_20.2: ref [i32; 3] = temporary_storage
-// CHECK:STDOUT:   %.loc12_20.3: i32 = int_literal 0
+// CHECK:STDOUT:   %.loc12_20.3: i32 = int_literal 0 [template = constants.%.9]
 // CHECK:STDOUT:   %.loc12_20.4: ref i32 = array_index %.loc12_20.2, %.loc12_20.3
 // CHECK:STDOUT:   %.loc12_20.5: init i32 = initialize_from %.loc12_13 to %.loc12_20.4
-// CHECK:STDOUT:   %.loc12_20.6: i32 = int_literal 1
+// CHECK:STDOUT:   %.loc12_20.6: i32 = int_literal 1 [template = constants.%.10]
 // CHECK:STDOUT:   %.loc12_20.7: ref i32 = array_index %.loc12_20.2, %.loc12_20.6
 // CHECK:STDOUT:   %.loc12_20.8: init i32 = initialize_from %.loc12_16 to %.loc12_20.7
-// CHECK:STDOUT:   %.loc12_20.9: i32 = int_literal 2
+// CHECK:STDOUT:   %.loc12_20.9: i32 = int_literal 2 [template = constants.%.11]
 // CHECK:STDOUT:   %.loc12_20.10: ref i32 = array_index %.loc12_20.2, %.loc12_20.9
 // CHECK:STDOUT:   %.loc12_20.11: init i32 = initialize_from %.loc12_19 to %.loc12_20.10
 // CHECK:STDOUT:   %.loc12_20.12: init [i32; 3] = array_init (%.loc12_20.5, %.loc12_20.8, %.loc12_20.11) to %.loc12_20.2

+ 18 - 9
toolchain/check/testdata/array/nine_elements.carbon

@@ -22,6 +22,15 @@ var a: [i32; 9] = (1, 2, 3, 4, 5, 6, 7, 8, 9);
 // CHECK:STDOUT:   %.11: i32 = int_literal 8 [template]
 // CHECK:STDOUT:   %.12: i32 = int_literal 9 [template]
 // CHECK:STDOUT:   %.13: type = tuple_type (i32, i32, i32, i32, i32, i32, i32, i32, i32) [template]
+// CHECK:STDOUT:   %.14: i32 = int_literal 0 [template]
+// CHECK:STDOUT:   %.15: i32 = int_literal 1 [template]
+// CHECK:STDOUT:   %.16: i32 = int_literal 2 [template]
+// CHECK:STDOUT:   %.17: i32 = int_literal 3 [template]
+// CHECK:STDOUT:   %.18: i32 = int_literal 4 [template]
+// CHECK:STDOUT:   %.19: i32 = int_literal 5 [template]
+// CHECK:STDOUT:   %.20: i32 = int_literal 6 [template]
+// CHECK:STDOUT:   %.21: i32 = int_literal 7 [template]
+// CHECK:STDOUT:   %.22: i32 = int_literal 8 [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -40,31 +49,31 @@ var a: [i32; 9] = (1, 2, 3, 4, 5, 6, 7, 8, 9);
 // CHECK:STDOUT:   %.loc7_41: i32 = int_literal 8 [template = constants.%.11]
 // CHECK:STDOUT:   %.loc7_44: i32 = int_literal 9 [template = constants.%.12]
 // CHECK:STDOUT:   %.loc7_45.1: (i32, i32, i32, i32, i32, i32, i32, i32, i32) = tuple_literal (%.loc7_20, %.loc7_23, %.loc7_26, %.loc7_29, %.loc7_32, %.loc7_35, %.loc7_38, %.loc7_41, %.loc7_44)
-// CHECK:STDOUT:   %.loc7_45.2: i32 = int_literal 0
+// CHECK:STDOUT:   %.loc7_45.2: i32 = int_literal 0 [template = constants.%.14]
 // CHECK:STDOUT:   %.loc7_45.3: ref i32 = array_index %a.var, %.loc7_45.2
 // CHECK:STDOUT:   %.loc7_45.4: init i32 = initialize_from %.loc7_20 to %.loc7_45.3
-// CHECK:STDOUT:   %.loc7_45.5: i32 = int_literal 1
+// CHECK:STDOUT:   %.loc7_45.5: i32 = int_literal 1 [template = constants.%.15]
 // CHECK:STDOUT:   %.loc7_45.6: ref i32 = array_index %a.var, %.loc7_45.5
 // CHECK:STDOUT:   %.loc7_45.7: init i32 = initialize_from %.loc7_23 to %.loc7_45.6
-// CHECK:STDOUT:   %.loc7_45.8: i32 = int_literal 2
+// CHECK:STDOUT:   %.loc7_45.8: i32 = int_literal 2 [template = constants.%.16]
 // CHECK:STDOUT:   %.loc7_45.9: ref i32 = array_index %a.var, %.loc7_45.8
 // CHECK:STDOUT:   %.loc7_45.10: init i32 = initialize_from %.loc7_26 to %.loc7_45.9
-// CHECK:STDOUT:   %.loc7_45.11: i32 = int_literal 3
+// CHECK:STDOUT:   %.loc7_45.11: i32 = int_literal 3 [template = constants.%.17]
 // CHECK:STDOUT:   %.loc7_45.12: ref i32 = array_index %a.var, %.loc7_45.11
 // CHECK:STDOUT:   %.loc7_45.13: init i32 = initialize_from %.loc7_29 to %.loc7_45.12
-// CHECK:STDOUT:   %.loc7_45.14: i32 = int_literal 4
+// CHECK:STDOUT:   %.loc7_45.14: i32 = int_literal 4 [template = constants.%.18]
 // CHECK:STDOUT:   %.loc7_45.15: ref i32 = array_index %a.var, %.loc7_45.14
 // CHECK:STDOUT:   %.loc7_45.16: init i32 = initialize_from %.loc7_32 to %.loc7_45.15
-// CHECK:STDOUT:   %.loc7_45.17: i32 = int_literal 5
+// CHECK:STDOUT:   %.loc7_45.17: i32 = int_literal 5 [template = constants.%.19]
 // CHECK:STDOUT:   %.loc7_45.18: ref i32 = array_index %a.var, %.loc7_45.17
 // CHECK:STDOUT:   %.loc7_45.19: init i32 = initialize_from %.loc7_35 to %.loc7_45.18
-// CHECK:STDOUT:   %.loc7_45.20: i32 = int_literal 6
+// CHECK:STDOUT:   %.loc7_45.20: i32 = int_literal 6 [template = constants.%.20]
 // CHECK:STDOUT:   %.loc7_45.21: ref i32 = array_index %a.var, %.loc7_45.20
 // CHECK:STDOUT:   %.loc7_45.22: init i32 = initialize_from %.loc7_38 to %.loc7_45.21
-// CHECK:STDOUT:   %.loc7_45.23: i32 = int_literal 7
+// CHECK:STDOUT:   %.loc7_45.23: i32 = int_literal 7 [template = constants.%.21]
 // CHECK:STDOUT:   %.loc7_45.24: ref i32 = array_index %a.var, %.loc7_45.23
 // CHECK:STDOUT:   %.loc7_45.25: init i32 = initialize_from %.loc7_41 to %.loc7_45.24
-// CHECK:STDOUT:   %.loc7_45.26: i32 = int_literal 8
+// CHECK:STDOUT:   %.loc7_45.26: i32 = int_literal 8 [template = constants.%.22]
 // CHECK:STDOUT:   %.loc7_45.27: ref i32 = array_index %a.var, %.loc7_45.26
 // CHECK:STDOUT:   %.loc7_45.28: init i32 = initialize_from %.loc7_44 to %.loc7_45.27
 // CHECK:STDOUT:   %.loc7_45.29: init [i32; 9] = array_init (%.loc7_45.4, %.loc7_45.7, %.loc7_45.10, %.loc7_45.13, %.loc7_45.16, %.loc7_45.19, %.loc7_45.22, %.loc7_45.25, %.loc7_45.28) to %a.var

+ 1 - 1
toolchain/check/testdata/as/as_type.carbon

@@ -17,6 +17,6 @@ let t: type = (i32, i32) as type;
 // CHECK:STDOUT:   package: <namespace> = namespace package, {}
 // CHECK:STDOUT:   %.loc7_24.1: (type, type) = tuple_literal (i32, i32)
 // CHECK:STDOUT:   %.loc7_24.2: type = converted %.loc7_24.1, constants.%.2 [template = constants.%.2]
-// CHECK:STDOUT:   %t: type = bind_name t, %.loc7_24.2
+// CHECK:STDOUT:   %t: type = bind_name t, %.loc7_24.2 [template = constants.%.2]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 1 - 1
toolchain/check/testdata/basics/builtin_types.carbon

@@ -29,7 +29,7 @@ var test_type: type = i32;
 // CHECK:STDOUT:   %.loc8: f64 = real_literal 1e-1 [template = constants.%.2]
 // CHECK:STDOUT:   assign %test_f64.var, %.loc8
 // CHECK:STDOUT:   %.loc9: String = string_literal "Test" [template = constants.%.4]
-// CHECK:STDOUT:   %test_str: String = bind_name test_str, %.loc9
+// CHECK:STDOUT:   %test_str: String = bind_name test_str, %.loc9 [template = constants.%.4]
 // CHECK:STDOUT:   %test_type.var: ref type = var test_type
 // CHECK:STDOUT:   %test_type: ref type = bind_name test_type, %test_type.var
 // CHECK:STDOUT:   assign %test_type.var, i32

+ 44 - 32
toolchain/check/testdata/basics/numeric_literals.carbon

@@ -37,17 +37,29 @@ fn F() {
 // CHECK:STDOUT:   %.7: i32 = int_literal 8 [template]
 // CHECK:STDOUT:   %.8: i32 = int_literal 39999999999999999993 [template]
 // CHECK:STDOUT:   %.9: type = tuple_type (i32, i32, i32, i32, i32) [template]
-// CHECK:STDOUT:   %.10: i32 = int_literal 7 [template]
-// CHECK:STDOUT:   %.11: type = array_type %.10, f64 [template]
-// CHECK:STDOUT:   %.12: type = ptr_type [f64; 7] [template]
-// CHECK:STDOUT:   %.13: f64 = real_literal 9e-1 [template]
-// CHECK:STDOUT:   %.14: f64 = real_literal 80e-1 [template]
-// CHECK:STDOUT:   %.15: f64 = real_literal 800e-1 [template]
-// CHECK:STDOUT:   %.16: f64 = real_literal 10e6 [template]
-// CHECK:STDOUT:   %.17: f64 = real_literal 10e7 [template]
-// CHECK:STDOUT:   %.18: f64 = real_literal 10e-9 [template]
-// CHECK:STDOUT:   %.19: f64 = real_literal 399999999999999999930e39999999999999999992 [template]
-// CHECK:STDOUT:   %.20: type = tuple_type (f64, f64, f64, f64, f64, f64, f64) [template]
+// CHECK:STDOUT:   %.10: i32 = int_literal 0 [template]
+// CHECK:STDOUT:   %.11: i32 = int_literal 1 [template]
+// CHECK:STDOUT:   %.12: i32 = int_literal 2 [template]
+// CHECK:STDOUT:   %.13: i32 = int_literal 3 [template]
+// CHECK:STDOUT:   %.14: i32 = int_literal 4 [template]
+// CHECK:STDOUT:   %.15: i32 = int_literal 7 [template]
+// CHECK:STDOUT:   %.16: type = array_type %.15, f64 [template]
+// CHECK:STDOUT:   %.17: type = ptr_type [f64; 7] [template]
+// CHECK:STDOUT:   %.18: f64 = real_literal 9e-1 [template]
+// CHECK:STDOUT:   %.19: f64 = real_literal 80e-1 [template]
+// CHECK:STDOUT:   %.20: f64 = real_literal 800e-1 [template]
+// CHECK:STDOUT:   %.21: f64 = real_literal 10e6 [template]
+// CHECK:STDOUT:   %.22: f64 = real_literal 10e7 [template]
+// CHECK:STDOUT:   %.23: f64 = real_literal 10e-9 [template]
+// CHECK:STDOUT:   %.24: f64 = real_literal 399999999999999999930e39999999999999999992 [template]
+// CHECK:STDOUT:   %.25: type = tuple_type (f64, f64, f64, f64, f64, f64, f64) [template]
+// CHECK:STDOUT:   %.26: i32 = int_literal 0 [template]
+// CHECK:STDOUT:   %.27: i32 = int_literal 1 [template]
+// CHECK:STDOUT:   %.28: i32 = int_literal 2 [template]
+// CHECK:STDOUT:   %.29: i32 = int_literal 3 [template]
+// CHECK:STDOUT:   %.30: i32 = int_literal 4 [template]
+// CHECK:STDOUT:   %.31: i32 = int_literal 5 [template]
+// CHECK:STDOUT:   %.32: i32 = int_literal 6 [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -67,55 +79,55 @@ fn F() {
 // CHECK:STDOUT:   %.loc14: i32 = int_literal 8 [template = constants.%.7]
 // CHECK:STDOUT:   %.loc15: i32 = int_literal 39999999999999999993 [template = constants.%.8]
 // CHECK:STDOUT:   %.loc16_3.1: (i32, i32, i32, i32, i32) = tuple_literal (%.loc11, %.loc12, %.loc13, %.loc14, %.loc15)
-// CHECK:STDOUT:   %.loc16_3.2: i32 = int_literal 0
+// CHECK:STDOUT:   %.loc16_3.2: i32 = int_literal 0 [template = constants.%.10]
 // CHECK:STDOUT:   %.loc16_3.3: ref i32 = array_index %ints.var, %.loc16_3.2
 // CHECK:STDOUT:   %.loc16_3.4: init i32 = initialize_from %.loc11 to %.loc16_3.3
-// CHECK:STDOUT:   %.loc16_3.5: i32 = int_literal 1
+// CHECK:STDOUT:   %.loc16_3.5: i32 = int_literal 1 [template = constants.%.11]
 // CHECK:STDOUT:   %.loc16_3.6: ref i32 = array_index %ints.var, %.loc16_3.5
 // CHECK:STDOUT:   %.loc16_3.7: init i32 = initialize_from %.loc12 to %.loc16_3.6
-// CHECK:STDOUT:   %.loc16_3.8: i32 = int_literal 2
+// CHECK:STDOUT:   %.loc16_3.8: i32 = int_literal 2 [template = constants.%.12]
 // CHECK:STDOUT:   %.loc16_3.9: ref i32 = array_index %ints.var, %.loc16_3.8
 // CHECK:STDOUT:   %.loc16_3.10: init i32 = initialize_from %.loc13 to %.loc16_3.9
-// CHECK:STDOUT:   %.loc16_3.11: i32 = int_literal 3
+// CHECK:STDOUT:   %.loc16_3.11: i32 = int_literal 3 [template = constants.%.13]
 // CHECK:STDOUT:   %.loc16_3.12: ref i32 = array_index %ints.var, %.loc16_3.11
 // CHECK:STDOUT:   %.loc16_3.13: init i32 = initialize_from %.loc14 to %.loc16_3.12
-// CHECK:STDOUT:   %.loc16_3.14: i32 = int_literal 4
+// CHECK:STDOUT:   %.loc16_3.14: i32 = int_literal 4 [template = constants.%.14]
 // CHECK:STDOUT:   %.loc16_3.15: ref i32 = array_index %ints.var, %.loc16_3.14
 // CHECK:STDOUT:   %.loc16_3.16: init i32 = initialize_from %.loc15 to %.loc16_3.15
 // CHECK:STDOUT:   %.loc16_3.17: init [i32; 5] = array_init (%.loc16_3.4, %.loc16_3.7, %.loc16_3.10, %.loc16_3.13, %.loc16_3.16) to %ints.var
 // CHECK:STDOUT:   %.loc16_3.18: init [i32; 5] = converted %.loc16_3.1, %.loc16_3.17
 // CHECK:STDOUT:   assign %ints.var, %.loc16_3.18
-// CHECK:STDOUT:   %.loc17_21: i32 = int_literal 7 [template = constants.%.10]
-// CHECK:STDOUT:   %.loc17_22: type = array_type %.loc17_21, f64 [template = constants.%.11]
+// CHECK:STDOUT:   %.loc17_21: i32 = int_literal 7 [template = constants.%.15]
+// CHECK:STDOUT:   %.loc17_22: type = array_type %.loc17_21, f64 [template = constants.%.16]
 // CHECK:STDOUT:   %floats.var: ref [f64; 7] = var floats
 // CHECK:STDOUT:   %floats: ref [f64; 7] = bind_name floats, %floats.var
-// CHECK:STDOUT:   %.loc18: f64 = real_literal 9e-1 [template = constants.%.13]
-// CHECK:STDOUT:   %.loc19: f64 = real_literal 80e-1 [template = constants.%.14]
-// CHECK:STDOUT:   %.loc20: f64 = real_literal 800e-1 [template = constants.%.15]
-// CHECK:STDOUT:   %.loc21: f64 = real_literal 10e6 [template = constants.%.16]
-// CHECK:STDOUT:   %.loc22: f64 = real_literal 10e7 [template = constants.%.17]
-// CHECK:STDOUT:   %.loc23: f64 = real_literal 10e-9 [template = constants.%.18]
-// CHECK:STDOUT:   %.loc24: f64 = real_literal 399999999999999999930e39999999999999999992 [template = constants.%.19]
+// CHECK:STDOUT:   %.loc18: f64 = real_literal 9e-1 [template = constants.%.18]
+// CHECK:STDOUT:   %.loc19: f64 = real_literal 80e-1 [template = constants.%.19]
+// CHECK:STDOUT:   %.loc20: f64 = real_literal 800e-1 [template = constants.%.20]
+// CHECK:STDOUT:   %.loc21: f64 = real_literal 10e6 [template = constants.%.21]
+// CHECK:STDOUT:   %.loc22: f64 = real_literal 10e7 [template = constants.%.22]
+// CHECK:STDOUT:   %.loc23: f64 = real_literal 10e-9 [template = constants.%.23]
+// CHECK:STDOUT:   %.loc24: f64 = real_literal 399999999999999999930e39999999999999999992 [template = constants.%.24]
 // CHECK:STDOUT:   %.loc25_3.1: (f64, f64, f64, f64, f64, f64, f64) = tuple_literal (%.loc18, %.loc19, %.loc20, %.loc21, %.loc22, %.loc23, %.loc24)
-// CHECK:STDOUT:   %.loc25_3.2: i32 = int_literal 0
+// CHECK:STDOUT:   %.loc25_3.2: i32 = int_literal 0 [template = constants.%.26]
 // CHECK:STDOUT:   %.loc25_3.3: ref f64 = array_index %floats.var, %.loc25_3.2
 // CHECK:STDOUT:   %.loc25_3.4: init f64 = initialize_from %.loc18 to %.loc25_3.3
-// CHECK:STDOUT:   %.loc25_3.5: i32 = int_literal 1
+// CHECK:STDOUT:   %.loc25_3.5: i32 = int_literal 1 [template = constants.%.27]
 // CHECK:STDOUT:   %.loc25_3.6: ref f64 = array_index %floats.var, %.loc25_3.5
 // CHECK:STDOUT:   %.loc25_3.7: init f64 = initialize_from %.loc19 to %.loc25_3.6
-// CHECK:STDOUT:   %.loc25_3.8: i32 = int_literal 2
+// CHECK:STDOUT:   %.loc25_3.8: i32 = int_literal 2 [template = constants.%.28]
 // CHECK:STDOUT:   %.loc25_3.9: ref f64 = array_index %floats.var, %.loc25_3.8
 // CHECK:STDOUT:   %.loc25_3.10: init f64 = initialize_from %.loc20 to %.loc25_3.9
-// CHECK:STDOUT:   %.loc25_3.11: i32 = int_literal 3
+// CHECK:STDOUT:   %.loc25_3.11: i32 = int_literal 3 [template = constants.%.29]
 // CHECK:STDOUT:   %.loc25_3.12: ref f64 = array_index %floats.var, %.loc25_3.11
 // CHECK:STDOUT:   %.loc25_3.13: init f64 = initialize_from %.loc21 to %.loc25_3.12
-// CHECK:STDOUT:   %.loc25_3.14: i32 = int_literal 4
+// CHECK:STDOUT:   %.loc25_3.14: i32 = int_literal 4 [template = constants.%.30]
 // CHECK:STDOUT:   %.loc25_3.15: ref f64 = array_index %floats.var, %.loc25_3.14
 // CHECK:STDOUT:   %.loc25_3.16: init f64 = initialize_from %.loc22 to %.loc25_3.15
-// CHECK:STDOUT:   %.loc25_3.17: i32 = int_literal 5
+// CHECK:STDOUT:   %.loc25_3.17: i32 = int_literal 5 [template = constants.%.31]
 // CHECK:STDOUT:   %.loc25_3.18: ref f64 = array_index %floats.var, %.loc25_3.17
 // CHECK:STDOUT:   %.loc25_3.19: init f64 = initialize_from %.loc23 to %.loc25_3.18
-// CHECK:STDOUT:   %.loc25_3.20: i32 = int_literal 6
+// CHECK:STDOUT:   %.loc25_3.20: i32 = int_literal 6 [template = constants.%.32]
 // CHECK:STDOUT:   %.loc25_3.21: ref f64 = array_index %floats.var, %.loc25_3.20
 // CHECK:STDOUT:   %.loc25_3.22: init f64 = initialize_from %.loc24 to %.loc25_3.21
 // CHECK:STDOUT:   %.loc25_3.23: init [f64; 7] = array_init (%.loc25_3.4, %.loc25_3.7, %.loc25_3.10, %.loc25_3.13, %.loc25_3.16, %.loc25_3.19, %.loc25_3.22) to %floats.var

+ 2 - 1
toolchain/check/testdata/basics/verbose.carbon

@@ -8,7 +8,8 @@
 // NOAUTOUPDATE
 // SET-CHECK-SUBSET
 // CHECK:STDERR: Node Push 0: FunctionIntroducer -> <none>
-// CHECK:STDERR: AddInst: {kind: FunctionDecl, arg0: {{.*}}, type: type{{[0-9]+}}}
+// CHECK:STDERR: AddPlaceholderInst: {kind: FunctionDecl, arg0: {{.*}}, type: type{{[0-9]+}}}
+// CHECK:STDERR: ReplaceInst: inst+{{[0-9]+}} -> {kind: FunctionDecl, arg0: {{.*}}, type: type{{[0-9]+}}}
 // CHECK:STDERR: inst_block_stack_ Push 1
 // CHECK:STDERR: AddInst: {kind: Return}
 // CHECK:STDERR: inst_block_stack_ Pop 1: block{{[0-9]+}}

+ 2 - 2
toolchain/check/testdata/class/fail_field_modifiers.carbon

@@ -49,9 +49,9 @@ class Class {
 // CHECK:STDOUT:   %.loc17_14.2: <unbound element of class Class> = field_decl k, element1 [template]
 // CHECK:STDOUT:   %k: <unbound element of class Class> = bind_name k, %.loc17_14.2 [template = %.loc17_14.2]
 // CHECK:STDOUT:   %.loc22: i32 = int_literal 0 [template = constants.%.1]
-// CHECK:STDOUT:   %l: i32 = bind_name l, %.loc22
+// CHECK:STDOUT:   %l: i32 = bind_name l, %.loc22 [template = constants.%.1]
 // CHECK:STDOUT:   %.loc27: i32 = int_literal 1 [template = constants.%.2]
-// CHECK:STDOUT:   %m: i32 = bind_name m, %.loc27
+// CHECK:STDOUT:   %m: i32 = bind_name m, %.loc27 [template = constants.%.2]
 // CHECK:STDOUT:
 // CHECK:STDOUT: !members:
 // CHECK:STDOUT:   .j = %j

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

@@ -29,7 +29,7 @@ fn T.F() {}
 // CHECK:STDOUT:   %Class.decl = class_decl @Class, ()
 // CHECK:STDOUT:   %Class: type = class_type @Class [template]
 // CHECK:STDOUT:   %Class.ref: type = name_ref Class, %Class [template = %Class]
-// CHECK:STDOUT:   %T: type = bind_name T, %Class.ref
+// CHECK:STDOUT:   %T: type = bind_name T, %Class.ref [template = %Class]
 // CHECK:STDOUT:   %.loc19: <function> = fn_decl @.1 [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 2 - 1
toolchain/check/testdata/if_expr/basic.carbon

@@ -17,6 +17,7 @@ fn F(b: bool, n: i32, m: i32) -> i32 {
 // CHECK:STDOUT:   %.3: type = ptr_type [i32; 1] [template]
 // CHECK:STDOUT:   %.4: i32 = int_literal 0 [template]
 // CHECK:STDOUT:   %.5: type = tuple_type (i32) [template]
+// CHECK:STDOUT:   %.6: i32 = int_literal 0 [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -32,7 +33,7 @@ fn F(b: bool, n: i32, m: i32) -> i32 {
 // CHECK:STDOUT:   %x: ref [i32; 1] = bind_name x, %x.var
 // CHECK:STDOUT:   %.loc8_22: i32 = int_literal 0 [template = constants.%.4]
 // CHECK:STDOUT:   %.loc8_24.1: (i32,) = tuple_literal (%.loc8_22)
-// CHECK:STDOUT:   %.loc8_24.2: i32 = int_literal 0
+// CHECK:STDOUT:   %.loc8_24.2: i32 = int_literal 0 [template = constants.%.6]
 // CHECK:STDOUT:   %.loc8_24.3: ref i32 = array_index %x.var, %.loc8_24.2
 // CHECK:STDOUT:   %.loc8_24.4: init i32 = initialize_from %.loc8_22 to %.loc8_24.3
 // CHECK:STDOUT:   %.loc8_24.5: init [i32; 1] = array_init (%.loc8_24.4) to %x.var

+ 8 - 6
toolchain/check/testdata/index/array_element_access.carbon

@@ -18,8 +18,10 @@ var d: i32 = a[b];
 // CHECK:STDOUT:   %.4: i32 = int_literal 12 [template]
 // CHECK:STDOUT:   %.5: i32 = int_literal 24 [template]
 // CHECK:STDOUT:   %.6: type = tuple_type (i32, i32) [template]
-// CHECK:STDOUT:   %.7: i32 = int_literal 1 [template]
-// CHECK:STDOUT:   %.8: i32 = int_literal 0 [template]
+// CHECK:STDOUT:   %.7: i32 = int_literal 0 [template]
+// CHECK:STDOUT:   %.8: i32 = int_literal 1 [template]
+// CHECK:STDOUT:   %.9: i32 = int_literal 1 [template]
+// CHECK:STDOUT:   %.10: i32 = int_literal 0 [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -31,10 +33,10 @@ var d: i32 = a[b];
 // CHECK:STDOUT:   %.loc7_20: i32 = int_literal 12 [template = constants.%.4]
 // CHECK:STDOUT:   %.loc7_24: i32 = int_literal 24 [template = constants.%.5]
 // CHECK:STDOUT:   %.loc7_26.1: (i32, i32) = tuple_literal (%.loc7_20, %.loc7_24)
-// CHECK:STDOUT:   %.loc7_26.2: i32 = int_literal 0
+// CHECK:STDOUT:   %.loc7_26.2: i32 = int_literal 0 [template = constants.%.7]
 // CHECK:STDOUT:   %.loc7_26.3: ref i32 = array_index %a.var, %.loc7_26.2
 // CHECK:STDOUT:   %.loc7_26.4: init i32 = initialize_from %.loc7_20 to %.loc7_26.3
-// CHECK:STDOUT:   %.loc7_26.5: i32 = int_literal 1
+// CHECK:STDOUT:   %.loc7_26.5: i32 = int_literal 1 [template = constants.%.8]
 // CHECK:STDOUT:   %.loc7_26.6: ref i32 = array_index %a.var, %.loc7_26.5
 // CHECK:STDOUT:   %.loc7_26.7: init i32 = initialize_from %.loc7_24 to %.loc7_26.6
 // CHECK:STDOUT:   %.loc7_26.8: init [i32; 2] = array_init (%.loc7_26.4, %.loc7_26.7) to %a.var
@@ -42,12 +44,12 @@ var d: i32 = a[b];
 // CHECK:STDOUT:   assign %a.var, %.loc7_26.9
 // CHECK:STDOUT:   %b.var: ref i32 = var b
 // CHECK:STDOUT:   %b: ref i32 = bind_name b, %b.var
-// CHECK:STDOUT:   %.loc8: i32 = int_literal 1 [template = constants.%.7]
+// CHECK:STDOUT:   %.loc8: i32 = int_literal 1 [template = constants.%.9]
 // CHECK:STDOUT:   assign %b.var, %.loc8
 // CHECK:STDOUT:   %c.var: ref i32 = var c
 // CHECK:STDOUT:   %c: ref i32 = bind_name c, %c.var
 // CHECK:STDOUT:   %a.ref.loc9: ref [i32; 2] = name_ref a, %a
-// CHECK:STDOUT:   %.loc9_16: i32 = int_literal 0 [template = constants.%.8]
+// CHECK:STDOUT:   %.loc9_16: i32 = int_literal 0 [template = constants.%.10]
 // CHECK:STDOUT:   %.loc9_17.1: ref i32 = array_index %a.ref.loc9, %.loc9_16
 // CHECK:STDOUT:   %.loc9_17.2: i32 = bind_value %.loc9_17.1
 // CHECK:STDOUT:   assign %c.var, %.loc9_17.2

+ 35 - 29
toolchain/check/testdata/index/expr_category.carbon

@@ -39,18 +39,24 @@ fn ValueBinding(b: [i32; 3]) {
 // CHECK:STDOUT:   %.10: i32 = int_literal 3 [template]
 // CHECK:STDOUT:   %.11: type = tuple_type (i32, i32, i32) [template]
 // CHECK:STDOUT:   %.12: i32 = int_literal 0 [template]
-// CHECK:STDOUT:   %.13: i32 = int_literal 0 [template]
-// CHECK:STDOUT:   %.14: i32 = int_literal 4 [template]
-// CHECK:STDOUT:   %.15: i32 = int_literal 3 [template]
-// CHECK:STDOUT:   %.16: type = array_type %.15, i32 [template]
-// CHECK:STDOUT:   %.17: i32 = int_literal 3 [template]
-// CHECK:STDOUT:   %.18: type = array_type %.17, i32 [template]
-// CHECK:STDOUT:   %.19: i32 = int_literal 1 [template]
-// CHECK:STDOUT:   %.20: i32 = int_literal 2 [template]
-// CHECK:STDOUT:   %.21: i32 = int_literal 3 [template]
-// CHECK:STDOUT:   %.22: i32 = int_literal 0 [template]
-// CHECK:STDOUT:   %.23: i32 = int_literal 0 [template]
-// CHECK:STDOUT:   %.24: i32 = int_literal 0 [template]
+// CHECK:STDOUT:   %.13: i32 = int_literal 1 [template]
+// CHECK:STDOUT:   %.14: i32 = int_literal 2 [template]
+// CHECK:STDOUT:   %.15: i32 = int_literal 0 [template]
+// CHECK:STDOUT:   %.16: i32 = int_literal 0 [template]
+// CHECK:STDOUT:   %.17: i32 = int_literal 4 [template]
+// CHECK:STDOUT:   %.18: i32 = int_literal 3 [template]
+// CHECK:STDOUT:   %.19: type = array_type %.18, i32 [template]
+// CHECK:STDOUT:   %.20: i32 = int_literal 3 [template]
+// CHECK:STDOUT:   %.21: type = array_type %.20, i32 [template]
+// CHECK:STDOUT:   %.22: i32 = int_literal 1 [template]
+// CHECK:STDOUT:   %.23: i32 = int_literal 2 [template]
+// CHECK:STDOUT:   %.24: i32 = int_literal 3 [template]
+// CHECK:STDOUT:   %.25: i32 = int_literal 0 [template]
+// CHECK:STDOUT:   %.26: i32 = int_literal 1 [template]
+// CHECK:STDOUT:   %.27: i32 = int_literal 2 [template]
+// CHECK:STDOUT:   %.28: i32 = int_literal 0 [template]
+// CHECK:STDOUT:   %.29: i32 = int_literal 0 [template]
+// CHECK:STDOUT:   %.30: i32 = int_literal 0 [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -72,13 +78,13 @@ fn ValueBinding(b: [i32; 3]) {
 // CHECK:STDOUT:   %.loc10_25: i32 = int_literal 2 [template = constants.%.9]
 // CHECK:STDOUT:   %.loc10_28: i32 = int_literal 3 [template = constants.%.10]
 // CHECK:STDOUT:   %.loc10_29.1: (i32, i32, i32) = tuple_literal (%.loc10_22, %.loc10_25, %.loc10_28)
-// CHECK:STDOUT:   %.loc10_29.2: i32 = int_literal 0
+// CHECK:STDOUT:   %.loc10_29.2: i32 = int_literal 0 [template = constants.%.12]
 // CHECK:STDOUT:   %.loc10_29.3: ref i32 = array_index %a.var, %.loc10_29.2
 // CHECK:STDOUT:   %.loc10_29.4: init i32 = initialize_from %.loc10_22 to %.loc10_29.3
-// CHECK:STDOUT:   %.loc10_29.5: i32 = int_literal 1
+// CHECK:STDOUT:   %.loc10_29.5: i32 = int_literal 1 [template = constants.%.13]
 // CHECK:STDOUT:   %.loc10_29.6: ref i32 = array_index %a.var, %.loc10_29.5
 // CHECK:STDOUT:   %.loc10_29.7: init i32 = initialize_from %.loc10_25 to %.loc10_29.6
-// CHECK:STDOUT:   %.loc10_29.8: i32 = int_literal 2
+// CHECK:STDOUT:   %.loc10_29.8: i32 = int_literal 2 [template = constants.%.14]
 // CHECK:STDOUT:   %.loc10_29.9: ref i32 = array_index %a.var, %.loc10_29.8
 // CHECK:STDOUT:   %.loc10_29.10: init i32 = initialize_from %.loc10_28 to %.loc10_29.9
 // CHECK:STDOUT:   %.loc10_29.11: init [i32; 3] = array_init (%.loc10_29.4, %.loc10_29.7, %.loc10_29.10) to %a.var
@@ -88,52 +94,52 @@ fn ValueBinding(b: [i32; 3]) {
 // CHECK:STDOUT:   %pa.var: ref i32* = var pa
 // CHECK:STDOUT:   %pa: ref i32* = bind_name pa, %pa.var
 // CHECK:STDOUT:   %a.ref.loc13: ref [i32; 3] = name_ref a, %a
-// CHECK:STDOUT:   %.loc13_21: i32 = int_literal 0 [template = constants.%.12]
+// CHECK:STDOUT:   %.loc13_21: i32 = int_literal 0 [template = constants.%.15]
 // CHECK:STDOUT:   %.loc13_22: ref i32 = array_index %a.ref.loc13, %.loc13_21
 // CHECK:STDOUT:   %.loc13_18: i32* = addr_of %.loc13_22
 // CHECK:STDOUT:   assign %pa.var, %.loc13_18
 // CHECK:STDOUT:   %a.ref.loc14: ref [i32; 3] = name_ref a, %a
-// CHECK:STDOUT:   %.loc14_5: i32 = int_literal 0 [template = constants.%.13]
+// CHECK:STDOUT:   %.loc14_5: i32 = int_literal 0 [template = constants.%.16]
 // CHECK:STDOUT:   %.loc14_6: ref i32 = array_index %a.ref.loc14, %.loc14_5
-// CHECK:STDOUT:   %.loc14_10: i32 = int_literal 4 [template = constants.%.14]
+// CHECK:STDOUT:   %.loc14_10: i32 = int_literal 4 [template = constants.%.17]
 // CHECK:STDOUT:   assign %.loc14_6, %.loc14_10
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @ValueBinding(%b: [i32; 3]) {
 // CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %.loc18_16: i32 = int_literal 3 [template = constants.%.17]
-// CHECK:STDOUT:   %.loc18_17: type = array_type %.loc18_16, i32 [template = constants.%.18]
+// CHECK:STDOUT:   %.loc18_16: i32 = int_literal 3 [template = constants.%.20]
+// CHECK:STDOUT:   %.loc18_17: type = array_type %.loc18_16, i32 [template = constants.%.21]
 // CHECK:STDOUT:   %a.var: ref [i32; 3] = var a
 // CHECK:STDOUT:   %a: ref [i32; 3] = bind_name a, %a.var
-// CHECK:STDOUT:   %.loc18_22: i32 = int_literal 1 [template = constants.%.19]
-// CHECK:STDOUT:   %.loc18_25: i32 = int_literal 2 [template = constants.%.20]
-// CHECK:STDOUT:   %.loc18_28: i32 = int_literal 3 [template = constants.%.21]
+// CHECK:STDOUT:   %.loc18_22: i32 = int_literal 1 [template = constants.%.22]
+// CHECK:STDOUT:   %.loc18_25: i32 = int_literal 2 [template = constants.%.23]
+// CHECK:STDOUT:   %.loc18_28: i32 = int_literal 3 [template = constants.%.24]
 // CHECK:STDOUT:   %.loc18_29.1: (i32, i32, i32) = tuple_literal (%.loc18_22, %.loc18_25, %.loc18_28)
-// CHECK:STDOUT:   %.loc18_29.2: i32 = int_literal 0
+// CHECK:STDOUT:   %.loc18_29.2: i32 = int_literal 0 [template = constants.%.25]
 // CHECK:STDOUT:   %.loc18_29.3: ref i32 = array_index %a.var, %.loc18_29.2
 // CHECK:STDOUT:   %.loc18_29.4: init i32 = initialize_from %.loc18_22 to %.loc18_29.3
-// CHECK:STDOUT:   %.loc18_29.5: i32 = int_literal 1
+// CHECK:STDOUT:   %.loc18_29.5: i32 = int_literal 1 [template = constants.%.26]
 // CHECK:STDOUT:   %.loc18_29.6: ref i32 = array_index %a.var, %.loc18_29.5
 // CHECK:STDOUT:   %.loc18_29.7: init i32 = initialize_from %.loc18_25 to %.loc18_29.6
-// CHECK:STDOUT:   %.loc18_29.8: i32 = int_literal 2
+// CHECK:STDOUT:   %.loc18_29.8: i32 = int_literal 2 [template = constants.%.27]
 // CHECK:STDOUT:   %.loc18_29.9: ref i32 = array_index %a.var, %.loc18_29.8
 // CHECK:STDOUT:   %.loc18_29.10: init i32 = initialize_from %.loc18_28 to %.loc18_29.9
 // CHECK:STDOUT:   %.loc18_29.11: init [i32; 3] = array_init (%.loc18_29.4, %.loc18_29.7, %.loc18_29.10) to %a.var
 // CHECK:STDOUT:   %.loc18_29.12: init [i32; 3] = converted %.loc18_29.1, %.loc18_29.11
 // CHECK:STDOUT:   assign %a.var, %.loc18_29.12
 // CHECK:STDOUT:   %a.ref: ref [i32; 3] = name_ref a, %a
-// CHECK:STDOUT:   %.loc22_5: i32 = int_literal 0 [template = constants.%.22]
+// CHECK:STDOUT:   %.loc22_5: i32 = int_literal 0 [template = constants.%.28]
 // CHECK:STDOUT:   %.loc22_6: ref i32 = array_index %a.ref, %.loc22_5
 // CHECK:STDOUT:   %b.ref: [i32; 3] = name_ref b, %b
-// CHECK:STDOUT:   %.loc23_5: i32 = int_literal 0 [template = constants.%.23]
+// CHECK:STDOUT:   %.loc23_5: i32 = int_literal 0 [template = constants.%.29]
 // CHECK:STDOUT:   %.loc23_6.1: ref [i32; 3] = value_as_ref %b.ref
 // CHECK:STDOUT:   %.loc23_6.2: ref i32 = array_index %.loc23_6.1, %.loc23_5
 // CHECK:STDOUT:   %.loc23_6.3: i32 = bind_value %.loc23_6.2
 // CHECK:STDOUT:   %F.ref: <function> = name_ref F, file.%F [template = file.%F]
 // CHECK:STDOUT:   %.loc24_4.1: ref [i32; 3] = temporary_storage
 // CHECK:STDOUT:   %.loc24_4.2: init [i32; 3] = call %F.ref() to %.loc24_4.1
-// CHECK:STDOUT:   %.loc24_7: i32 = int_literal 0 [template = constants.%.24]
+// CHECK:STDOUT:   %.loc24_7: i32 = int_literal 0 [template = constants.%.30]
 // CHECK:STDOUT:   %.loc24_4.3: ref [i32; 3] = temporary %.loc24_4.1, %.loc24_4.2
 // CHECK:STDOUT:   %.loc24_8.1: ref i32 = array_index %.loc24_4.3, %.loc24_7
 // CHECK:STDOUT:   %.loc24_8.2: i32 = bind_value %.loc24_8.1

+ 4 - 3
toolchain/check/testdata/index/fail_array_large_index.carbon

@@ -18,7 +18,8 @@ var b: i32 = a[0xFFFFFFFFFFFFFFFFF];
 // CHECK:STDOUT:   %.3: type = ptr_type [i32; 1] [template]
 // CHECK:STDOUT:   %.4: i32 = int_literal 12 [template]
 // CHECK:STDOUT:   %.5: type = tuple_type (i32) [template]
-// CHECK:STDOUT:   %.6: i32 = int_literal 295147905179352825855 [template]
+// CHECK:STDOUT:   %.6: i32 = int_literal 0 [template]
+// CHECK:STDOUT:   %.7: i32 = int_literal 295147905179352825855 [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -29,7 +30,7 @@ var b: i32 = a[0xFFFFFFFFFFFFFFFFF];
 // CHECK:STDOUT:   %a: ref [i32; 1] = bind_name a, %a.var
 // CHECK:STDOUT:   %.loc7_20: i32 = int_literal 12 [template = constants.%.4]
 // CHECK:STDOUT:   %.loc7_23.1: (i32,) = tuple_literal (%.loc7_20)
-// CHECK:STDOUT:   %.loc7_23.2: i32 = int_literal 0
+// CHECK:STDOUT:   %.loc7_23.2: i32 = int_literal 0 [template = constants.%.6]
 // CHECK:STDOUT:   %.loc7_23.3: ref i32 = array_index %a.var, %.loc7_23.2
 // CHECK:STDOUT:   %.loc7_23.4: init i32 = initialize_from %.loc7_20 to %.loc7_23.3
 // CHECK:STDOUT:   %.loc7_23.5: init [i32; 1] = array_init (%.loc7_23.4) to %a.var
@@ -38,7 +39,7 @@ var b: i32 = a[0xFFFFFFFFFFFFFFFFF];
 // CHECK:STDOUT:   %b.var: ref i32 = var b
 // CHECK:STDOUT:   %b: ref i32 = bind_name b, %b.var
 // CHECK:STDOUT:   %a.ref: ref [i32; 1] = name_ref a, %a
-// CHECK:STDOUT:   %.loc11_16: i32 = int_literal 295147905179352825855 [template = constants.%.6]
+// CHECK:STDOUT:   %.loc11_16: i32 = int_literal 295147905179352825855 [template = constants.%.7]
 // CHECK:STDOUT:   %.loc11_35.1: ref i32 = array_index %a.ref, <error>
 // CHECK:STDOUT:   %.loc11_35.2: i32 = bind_value %.loc11_35.1
 // CHECK:STDOUT:   assign %b.var, %.loc11_35.2

+ 4 - 3
toolchain/check/testdata/index/fail_array_non_int_indexing.carbon

@@ -18,7 +18,8 @@ var b: i32 = a[2.6];
 // CHECK:STDOUT:   %.3: type = ptr_type [i32; 1] [template]
 // CHECK:STDOUT:   %.4: i32 = int_literal 12 [template]
 // CHECK:STDOUT:   %.5: type = tuple_type (i32) [template]
-// CHECK:STDOUT:   %.6: f64 = real_literal 26e-1 [template]
+// CHECK:STDOUT:   %.6: i32 = int_literal 0 [template]
+// CHECK:STDOUT:   %.7: f64 = real_literal 26e-1 [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -29,7 +30,7 @@ var b: i32 = a[2.6];
 // CHECK:STDOUT:   %a: ref [i32; 1] = bind_name a, %a.var
 // CHECK:STDOUT:   %.loc7_20: i32 = int_literal 12 [template = constants.%.4]
 // CHECK:STDOUT:   %.loc7_23.1: (i32,) = tuple_literal (%.loc7_20)
-// CHECK:STDOUT:   %.loc7_23.2: i32 = int_literal 0
+// CHECK:STDOUT:   %.loc7_23.2: i32 = int_literal 0 [template = constants.%.6]
 // CHECK:STDOUT:   %.loc7_23.3: ref i32 = array_index %a.var, %.loc7_23.2
 // CHECK:STDOUT:   %.loc7_23.4: init i32 = initialize_from %.loc7_20 to %.loc7_23.3
 // CHECK:STDOUT:   %.loc7_23.5: init [i32; 1] = array_init (%.loc7_23.4) to %a.var
@@ -38,7 +39,7 @@ var b: i32 = a[2.6];
 // CHECK:STDOUT:   %b.var: ref i32 = var b
 // CHECK:STDOUT:   %b: ref i32 = bind_name b, %b.var
 // CHECK:STDOUT:   %a.ref: ref [i32; 1] = name_ref a, %a
-// CHECK:STDOUT:   %.loc11_16: f64 = real_literal 26e-1 [template = constants.%.6]
+// CHECK:STDOUT:   %.loc11_16: f64 = real_literal 26e-1 [template = constants.%.7]
 // CHECK:STDOUT:   %.loc11_19.1: ref i32 = array_index %a.ref, <error>
 // CHECK:STDOUT:   %.loc11_19.2: i32 = bind_value %.loc11_19.1
 // CHECK:STDOUT:   assign %b.var, %.loc11_19.2

+ 4 - 3
toolchain/check/testdata/index/fail_array_out_of_bound_access.carbon

@@ -18,7 +18,8 @@ var b: i32 = a[2];
 // CHECK:STDOUT:   %.3: type = ptr_type [i32; 1] [template]
 // CHECK:STDOUT:   %.4: i32 = int_literal 12 [template]
 // CHECK:STDOUT:   %.5: type = tuple_type (i32) [template]
-// CHECK:STDOUT:   %.6: i32 = int_literal 2 [template]
+// CHECK:STDOUT:   %.6: i32 = int_literal 0 [template]
+// CHECK:STDOUT:   %.7: i32 = int_literal 2 [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -29,7 +30,7 @@ var b: i32 = a[2];
 // CHECK:STDOUT:   %a: ref [i32; 1] = bind_name a, %a.var
 // CHECK:STDOUT:   %.loc7_20: i32 = int_literal 12 [template = constants.%.4]
 // CHECK:STDOUT:   %.loc7_23.1: (i32,) = tuple_literal (%.loc7_20)
-// CHECK:STDOUT:   %.loc7_23.2: i32 = int_literal 0
+// CHECK:STDOUT:   %.loc7_23.2: i32 = int_literal 0 [template = constants.%.6]
 // CHECK:STDOUT:   %.loc7_23.3: ref i32 = array_index %a.var, %.loc7_23.2
 // CHECK:STDOUT:   %.loc7_23.4: init i32 = initialize_from %.loc7_20 to %.loc7_23.3
 // CHECK:STDOUT:   %.loc7_23.5: init [i32; 1] = array_init (%.loc7_23.4) to %a.var
@@ -38,7 +39,7 @@ var b: i32 = a[2];
 // CHECK:STDOUT:   %b.var: ref i32 = var b
 // CHECK:STDOUT:   %b: ref i32 = bind_name b, %b.var
 // CHECK:STDOUT:   %a.ref: ref [i32; 1] = name_ref a, %a
-// CHECK:STDOUT:   %.loc11_16: i32 = int_literal 2 [template = constants.%.6]
+// CHECK:STDOUT:   %.loc11_16: i32 = int_literal 2 [template = constants.%.7]
 // CHECK:STDOUT:   %.loc11_17.1: ref i32 = array_index %a.ref, <error>
 // CHECK:STDOUT:   %.loc11_17.2: i32 = bind_value %.loc11_17.1
 // CHECK:STDOUT:   assign %b.var, %.loc11_17.2

+ 1 - 1
toolchain/check/testdata/let/fail_duplicate_decl.carbon

@@ -28,7 +28,7 @@ fn F(a: i32) {
 // CHECK:STDOUT: fn @F(%a.loc7: i32) {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %.loc14: i32 = int_literal 1 [template = constants.%.1]
-// CHECK:STDOUT:   %a.loc14: i32 = bind_name a, %.loc14
+// CHECK:STDOUT:   %a.loc14: i32 = bind_name a, %.loc14 [template = constants.%.1]
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 2 - 2
toolchain/check/testdata/let/fail_generic.carbon

@@ -30,8 +30,8 @@ fn F(a: i32) -> i32 {
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F(%a: i32) -> i32 {
 // CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %T: type = bind_symbolic_name T, i32
-// CHECK:STDOUT:   %T.ref: type = name_ref T, %T
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T, i32 [symbolic]
+// CHECK:STDOUT:   %T.ref: type = name_ref T, %T [symbolic = %T]
 // CHECK:STDOUT:   %.loc13: i32 = int_literal 5 [template = constants.%.1]
 // CHECK:STDOUT:   %x: T = bind_name x, <error>
 // CHECK:STDOUT:   %x.ref: T = name_ref x, %x

+ 8 - 8
toolchain/check/testdata/let/fail_modifiers.carbon

@@ -84,20 +84,20 @@ protected protected let i: i32 = 1;
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   package: <namespace> = namespace package, {}
 // CHECK:STDOUT:   %.loc10: i32 = int_literal 1 [template = constants.%.1]
-// CHECK:STDOUT:   %b: i32 = bind_name b, %.loc10
+// CHECK:STDOUT:   %b: i32 = bind_name b, %.loc10 [template = constants.%.1]
 // CHECK:STDOUT:   %.loc15: i32 = int_literal 1 [template = constants.%.2]
-// CHECK:STDOUT:   %c: i32 = bind_name c, %.loc15
+// CHECK:STDOUT:   %c: i32 = bind_name c, %.loc15 [template = constants.%.2]
 // CHECK:STDOUT:   %.loc20: i32 = int_literal 1 [template = constants.%.3]
-// CHECK:STDOUT:   %d: i32 = bind_name d, %.loc20
+// CHECK:STDOUT:   %d: i32 = bind_name d, %.loc20 [template = constants.%.3]
 // CHECK:STDOUT:   %.loc25: i32 = int_literal 1 [template = constants.%.4]
-// CHECK:STDOUT:   %e: i32 = bind_name e, %.loc25
+// CHECK:STDOUT:   %e: i32 = bind_name e, %.loc25 [template = constants.%.4]
 // CHECK:STDOUT:   %.loc36: i32 = int_literal 1 [template = constants.%.5]
-// CHECK:STDOUT:   %f: i32 = bind_name f, %.loc36
+// CHECK:STDOUT:   %f: i32 = bind_name f, %.loc36 [template = constants.%.5]
 // CHECK:STDOUT:   %.loc47: i32 = int_literal 1 [template = constants.%.6]
-// CHECK:STDOUT:   %g: i32 = bind_name g, %.loc47
+// CHECK:STDOUT:   %g: i32 = bind_name g, %.loc47 [template = constants.%.6]
 // CHECK:STDOUT:   %.loc58: i32 = int_literal 1 [template = constants.%.7]
-// CHECK:STDOUT:   %h: i32 = bind_name h, %.loc58
+// CHECK:STDOUT:   %h: i32 = bind_name h, %.loc58 [template = constants.%.7]
 // CHECK:STDOUT:   %.loc69: i32 = int_literal 1 [template = constants.%.8]
-// CHECK:STDOUT:   %i: i32 = bind_name i, %.loc69
+// CHECK:STDOUT:   %i: i32 = bind_name i, %.loc69 [template = constants.%.8]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 1 - 1
toolchain/check/testdata/let/fail_todo_modifiers.carbon

@@ -18,6 +18,6 @@ private let a: i32 = 1;
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   package: <namespace> = namespace package, {}
 // CHECK:STDOUT:   %.loc10: i32 = int_literal 1 [template = constants.%.1]
-// CHECK:STDOUT:   %a: i32 = bind_name a, %.loc10
+// CHECK:STDOUT:   %a: i32 = bind_name a, %.loc10 [template = constants.%.1]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 3 - 3
toolchain/check/testdata/let/generic.carbon

@@ -19,12 +19,12 @@ fn F() {
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F() {
 // CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %T: type = bind_symbolic_name T, i32
-// CHECK:STDOUT:   %T.ref.loc9: type = name_ref T, %T
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T, i32 [symbolic]
+// CHECK:STDOUT:   %T.ref.loc9: type = name_ref T, %T [symbolic = %T]
 // CHECK:STDOUT:   %.loc9: type = ptr_type T [template]
 // CHECK:STDOUT:   %p.var: ref T* = var p
 // CHECK:STDOUT:   %p: ref T* = bind_name p, %p.var
-// CHECK:STDOUT:   %T.ref.loc10: type = name_ref T, %T
+// CHECK:STDOUT:   %T.ref.loc10: type = name_ref T, %T [symbolic = %T]
 // CHECK:STDOUT:   %a.var: ref T = var a
 // CHECK:STDOUT:   %a: ref T = bind_name a, %a.var
 // CHECK:STDOUT:   %p.ref: ref T* = name_ref p, %p

+ 2 - 2
toolchain/check/testdata/let/global.carbon

@@ -17,13 +17,13 @@ fn F() -> i32 { return n; }
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   package: <namespace> = namespace package, {.F = %F}
 // CHECK:STDOUT:   %.loc7: i32 = int_literal 1 [template = constants.%.1]
-// CHECK:STDOUT:   %n: i32 = bind_name n, %.loc7
+// CHECK:STDOUT:   %n: i32 = bind_name n, %.loc7 [template = constants.%.1]
 // CHECK:STDOUT:   %F: <function> = fn_decl @F [template]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F() -> i32 {
 // CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %n.ref: i32 = name_ref n, file.%n
+// CHECK:STDOUT:   %n.ref: i32 = name_ref n, file.%n [template = constants.%.1]
 // CHECK:STDOUT:   return %n.ref
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 2 - 2
toolchain/check/testdata/operators/unary_op.carbon

@@ -25,10 +25,10 @@ let not_false: bool = not false;
 // CHECK:STDOUT:   %Not: <function> = fn_decl @Not [template]
 // CHECK:STDOUT:   %.loc11_26: bool = bool_literal true [template = constants.%.1]
 // CHECK:STDOUT:   %.loc11_22: bool = not %.loc11_26 [template = constants.%.2]
-// CHECK:STDOUT:   %not_true: bool = bind_name not_true, %.loc11_22
+// CHECK:STDOUT:   %not_true: bool = bind_name not_true, %.loc11_22 [template = constants.%.2]
 // CHECK:STDOUT:   %.loc12_27: bool = bool_literal false [template = constants.%.3]
 // CHECK:STDOUT:   %.loc12_23: bool = not %.loc12_27 [template = constants.%.4]
-// CHECK:STDOUT:   %not_false: bool = bind_name not_false, %.loc12_23
+// CHECK:STDOUT:   %not_false: bool = bind_name not_false, %.loc12_23 [template = constants.%.4]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Not(%b: bool) -> bool {

+ 3 - 3
toolchain/check/testdata/struct/two_entries.carbon

@@ -29,10 +29,10 @@ var y: {.a: i32, .b: i32} = x;
 // CHECK:STDOUT:   %.loc7_44.1: {.a: i32, .b: i32} = struct_literal (%.loc7_35, %.loc7_43)
 // CHECK:STDOUT:   %.loc7_44.2: {.a: i32, .b: i32} = struct_value (%.loc7_35, %.loc7_43) [template = constants.%.4]
 // CHECK:STDOUT:   %.loc7_44.3: {.a: i32, .b: i32} = converted %.loc7_44.1, %.loc7_44.2 [template = constants.%.4]
-// CHECK:STDOUT:   %v: {.a: i32, .b: i32} = bind_name v, %.loc7_44.3
+// CHECK:STDOUT:   %v: {.a: i32, .b: i32} = bind_name v, %.loc7_44.3 [template = constants.%.4]
 // CHECK:STDOUT:   %.loc8: type = struct_type {.a: i32, .b: i32} [template]
-// CHECK:STDOUT:   %v.ref: {.a: i32, .b: i32} = name_ref v, %v
-// CHECK:STDOUT:   %w: {.a: i32, .b: i32} = bind_name w, %v.ref
+// CHECK:STDOUT:   %v.ref: {.a: i32, .b: i32} = name_ref v, %v [template = constants.%.4]
+// CHECK:STDOUT:   %w: {.a: i32, .b: i32} = bind_name w, %v.ref [template = constants.%.4]
 // CHECK:STDOUT:   %.loc10_25: type = struct_type {.a: i32, .b: i32} [template]
 // CHECK:STDOUT:   %x.var: ref {.a: i32, .b: i32} = var x
 // CHECK:STDOUT:   %x: ref {.a: i32, .b: i32} = bind_name x, %x.var

+ 3 - 3
toolchain/check/testdata/tuples/two_elements.carbon

@@ -32,11 +32,11 @@ var y: (i32, i32) = x;
 // CHECK:STDOUT:   %.loc7_28.1: (i32, i32) = tuple_literal (%.loc7_22, %.loc7_25)
 // CHECK:STDOUT:   %.loc7_28.2: (i32, i32) = tuple_value (%.loc7_22, %.loc7_25) [template = constants.%.6]
 // CHECK:STDOUT:   %.loc7_28.3: (i32, i32) = converted %.loc7_28.1, %.loc7_28.2 [template = constants.%.6]
-// CHECK:STDOUT:   %v: (i32, i32) = bind_name v, %.loc7_28.3
+// CHECK:STDOUT:   %v: (i32, i32) = bind_name v, %.loc7_28.3 [template = constants.%.6]
 // CHECK:STDOUT:   %.loc8_17.1: (type, type) = tuple_literal (i32, i32)
 // CHECK:STDOUT:   %.loc8_17.2: type = converted %.loc8_17.1, constants.%.2 [template = constants.%.2]
-// CHECK:STDOUT:   %v.ref: (i32, i32) = name_ref v, %v
-// CHECK:STDOUT:   %w: (i32, i32) = bind_name w, %v.ref
+// CHECK:STDOUT:   %v.ref: (i32, i32) = name_ref v, %v [template = constants.%.6]
+// CHECK:STDOUT:   %w: (i32, i32) = bind_name w, %v.ref [template = constants.%.6]
 // CHECK:STDOUT:   %.loc10_17.1: (type, type) = tuple_literal (i32, i32)
 // CHECK:STDOUT:   %.loc10_17.2: type = converted %.loc10_17.1, constants.%.2 [template = constants.%.2]
 // CHECK:STDOUT:   %x.var: ref (i32, i32) = var x

+ 11 - 7
toolchain/sem_ir/value_stores.h

@@ -58,6 +58,11 @@ class InstStore {
   // Returns the requested instruction.
   auto Get(InstId inst_id) const -> Inst { return values_.Get(inst_id); }
 
+  // Returns the requested instruction and its parse node.
+  auto GetWithParseNode(InstId inst_id) const -> ParseNodeAndInst {
+    return ParseNodeAndInst::Untyped(GetParseNode(inst_id), Get(inst_id));
+  }
+
   // Returns the requested instruction, which is known to have the specified
   // type.
   template <typename InstT>
@@ -72,15 +77,14 @@ class InstStore {
     return Get(inst_id).TryAs<InstT>();
   }
 
-  // Overwrites a given instruction with a new value.
-  auto Set(InstId inst_id, Inst inst) -> void { values_.Get(inst_id) = inst; }
-
   auto GetParseNode(InstId inst_id) const -> Parse::NodeId {
     return parse_nodes_[inst_id.index];
   }
 
-  auto SetParseNode(InstId inst_id, Parse::NodeId parse_node) -> void {
-    parse_nodes_[inst_id.index] = parse_node;
+  // Overwrites a given instruction and parse node with a new value.
+  auto Set(InstId inst_id, ParseNodeAndInst parse_node_and_inst) -> void {
+    values_.Get(inst_id) = parse_node_and_inst.inst;
+    parse_nodes_[inst_id.index] = parse_node_and_inst.parse_node;
   }
 
   // Reserves space.
@@ -110,10 +114,10 @@ class ConstantValueStore : public Yaml::Printable<ConstantValueStore> {
                : values_[inst_id.index];
   }
 
-  // Sets the constant value of the given instruction.
+  // Sets the constant value of the given instruction, or sets that it is known
+  // to not be a constant.
   auto Set(InstId inst_id, ConstantId const_id) -> void {
     CARBON_CHECK(inst_id.index >= 0);
-    CARBON_CHECK(const_id.is_constant());
     if (static_cast<size_t>(inst_id.index) >= values_.size()) {
       values_.resize(inst_id.index + 1, ConstantId::NotConstant);
     }