Browse Source

Support importing `var` parameters (#5400)

This restructures the import and merge logic to support parameter
patterns in a more scalable way.

---------

Co-authored-by: Richard Smith <richard@metafoo.co.uk>
Geoff Romer 11 tháng trước cách đây
mục cha
commit
fbc5994750

+ 239 - 319
toolchain/check/import_ref.cpp

@@ -401,9 +401,14 @@ class ImportContext {
 //      - Fill in any remaining information to complete the import of the
 //      - Fill in any remaining information to complete the import of the
 //        instruction. For example, when importing a class declaration, build
 //        instruction. For example, when importing a class declaration, build
 //        the class scope and information about the definition.
 //        the class scope and information about the definition.
-//      - Return ResolveAs/ResolveAsConstant to finish the resolution process.
-//        This will cause the Resolve loop to set a constant value if we didn't
-//        retry at the end of the second phase.
+//      - Return Done() to finish the resolution process. This will cause the
+//        Resolve loop to set a constant value if we didn't retry at the end of
+//        the second phase.
+//
+//    In the common case where the second phase cannot add new work (because the
+//    inst doesn't represent a declaration of an entity that can be forward
+//    declared), the second and third phases are usually expressed as a call to
+//    ResolveAsDeduplicated or ResolveAsUnique.
 //
 //
 // 3. If resolve didn't return Retry(), pop the work. Otherwise, it needs to
 // 3. If resolve didn't return Retry(), pop the work. Otherwise, it needs to
 //    remain, and may no longer be at the top of the stack; update the state of
 //    remain, and may no longer be at the top of the stack; update the state of
@@ -516,6 +521,9 @@ class ImportRefResolver : public ImportContext {
   // Returns the ConstantId for an InstId. Adds unresolved constants to
   // Returns the ConstantId for an InstId. Adds unresolved constants to
   // work_stack_.
   // work_stack_.
   auto GetLocalConstantValueOrPush(SemIR::InstId inst_id) -> SemIR::ConstantId {
   auto GetLocalConstantValueOrPush(SemIR::InstId inst_id) -> SemIR::ConstantId {
+    if (!inst_id.has_value()) {
+      return SemIR::ConstantId::None;
+    }
     auto const_id =
     auto const_id =
         local_constant_values_for_import_insts().GetAttached(inst_id);
         local_constant_values_for_import_insts().GetAttached(inst_id);
     if (!const_id.has_value()) {
     if (!const_id.has_value()) {
@@ -654,7 +662,7 @@ static auto SetConstantValue(Context& context, SemIR::InstId inst_id,
 
 
 // Adds an imported instruction without setting its constant value. The
 // Adds an imported instruction without setting its constant value. The
 // instruction should later be updated by either `SetConstantValue` or
 // instruction should later be updated by either `SetConstantValue` or
-// `ReplaceImportedInstAndSetConstantValue`.
+// `ReplacePlaceholderImportedInst`.
 template <typename InstT>
 template <typename InstT>
 static auto AddPlaceholderImportedInst(ImportContext& context,
 static auto AddPlaceholderImportedInst(ImportContext& context,
                                        SemIR::InstId import_inst_id, InstT inst)
                                        SemIR::InstId import_inst_id, InstT inst)
@@ -662,33 +670,13 @@ static auto AddPlaceholderImportedInst(ImportContext& context,
   auto inst_id = context.local_insts().AddInNoBlock(MakeImportedLocIdAndInst(
   auto inst_id = context.local_insts().AddInNoBlock(MakeImportedLocIdAndInst(
       context.local_context(), AddImportIRInst(context, import_inst_id), inst));
       context.local_context(), AddImportIRInst(context, import_inst_id), inst));
   CARBON_VLOG_TO(context.local_context().vlog_stream(),
   CARBON_VLOG_TO(context.local_context().vlog_stream(),
-                 "AddImportedInst: {0}\n", static_cast<SemIR::Inst>(inst));
+                 "AddPlaceholderImportedInst: {0}\n", inst);
   // Track the instruction in the imports block so that it's included in
   // Track the instruction in the imports block so that it's included in
   // formatted SemIR if it's referenced.
   // formatted SemIR if it's referenced.
   context.local_context().import_ref_ids().push_back(inst_id);
   context.local_context().import_ref_ids().push_back(inst_id);
   return inst_id;
   return inst_id;
 }
 }
 
 
-// Adds an imported instruction. The constant value of the instruction will be
-// computed.
-//
-// Normally `AddImportedConstant` should be used to create the result of
-// resolving a constant, for example by calling `ResolveAs*`. However, we
-// sometimes need to create a new instruction, not just obtain a constant value,
-// such as when importing a declaration or a pattern. In that case, this
-// function should be used to create it.
-//
-// Do not use `AddInst*`, as it will add symbolic constants to the eval block of
-// whatever generic the import happens within.
-template <typename InstT>
-static auto AddImportedInst(ImportContext& context,
-                            SemIR::InstId import_inst_id, InstT inst)
-    -> SemIR::InstId {
-  auto inst_id = AddPlaceholderImportedInst(context, import_inst_id, inst);
-  SetConstantValue(context.local_context(), inst_id, inst);
-  return inst_id;
-}
-
 // Replace an imported instruction that was added by
 // Replace an imported instruction that was added by
 // `AddPlaceholderImportedInst` with a new instruction. Computes, sets, and
 // `AddPlaceholderImportedInst` with a new instruction. Computes, sets, and
 // returns the new constant value.
 // returns the new constant value.
@@ -745,35 +733,6 @@ static auto GetLocalConstantId(ImportRefResolver& resolver,
                             resolver.import_types().GetConstantId(type_id));
                             resolver.import_types().GetConstantId(type_id));
 }
 }
 
 
-// Returns the ConstantId for an InstId that is required to have already been
-// imported.
-static auto GetLocalConstantIdChecked(ImportContext& context,
-                                      SemIR::InstId inst_id)
-    -> SemIR::ConstantId {
-  auto result_id =
-      context.local_constant_values_for_import_insts().GetAttached(inst_id);
-  CARBON_CHECK(result_id.has_value());
-  return result_id;
-}
-
-// Returns the ConstantId for a ConstantId that is required to have already been
-// imported.
-static auto GetLocalConstantIdChecked(ImportContext& context,
-                                      SemIR::ConstantId const_id)
-    -> SemIR::ConstantId {
-  return GetLocalConstantIdChecked(
-      context, GetInstWithConstantValue(context.import_ir(), const_id));
-}
-
-// Returns the ConstantId for a TypeId that is required to have already been
-// imported.
-static auto GetLocalConstantIdChecked(ImportContext& context,
-                                      SemIR::TypeId type_id)
-    -> SemIR::ConstantId {
-  return GetLocalConstantIdChecked(
-      context, context.import_types().GetConstantId(type_id));
-}
-
 // Translates a NameId from the import IR to a local NameId.
 // Translates a NameId from the import IR to a local NameId.
 static auto GetLocalNameId(ImportContext& context, SemIR::NameId import_name_id)
 static auto GetLocalNameId(ImportContext& context, SemIR::NameId import_name_id)
     -> SemIR::NameId {
     -> SemIR::NameId {
@@ -1039,164 +998,6 @@ static auto GetLocalSpecificInterface(
   }
   }
 }
 }
 
 
-// Adds unresolved constants for each parameter's type to the resolver's work
-// stack.
-static auto LoadLocalPatternConstantIds(ImportRefResolver& resolver,
-                                        SemIR::InstBlockId param_patterns_id)
-    -> void {
-  if (!param_patterns_id.has_value() ||
-      param_patterns_id == SemIR::InstBlockId::Empty) {
-    return;
-  }
-
-  const auto& param_patterns =
-      resolver.import_inst_blocks().Get(param_patterns_id);
-  for (auto pattern_id : param_patterns) {
-    auto pattern_inst = resolver.import_insts().GetWithAttachedType(pattern_id);
-    GetLocalConstantId(resolver, pattern_inst.type_id());
-    if (auto addr = pattern_inst.TryAs<SemIR::AddrPattern>()) {
-      pattern_id = addr->inner_id;
-      pattern_inst = resolver.import_insts().GetWithAttachedType(pattern_id);
-      GetLocalConstantId(resolver, pattern_inst.type_id());
-    }
-    // If the parameter is a symbolic binding, build the
-    // SymbolicBindingPattern constant.
-    if (pattern_inst.Is<SemIR::SymbolicBindingPattern>()) {
-      GetLocalConstantId(resolver, pattern_id);
-    }
-  }
-}
-
-// Returns a version of param_patterns_id localized to the current IR.
-//
-// Must only be called after a call to
-// LoadLocalPatternConstantIds(param_patterns_id) has completed without adding
-// any new work to work_stack_.
-//
-// TODO: This is inconsistent with the rest of this class, which expects
-// the relevant constants to be explicitly passed in. That makes it
-// easier to statically detect when an input isn't loaded, but makes it
-// harder to support importing more complex inst structures. We should
-// take a holistic look at how to balance those concerns. For example,
-// could the same function be used to load the constants and use them, with
-// a parameter to select between the two?
-//
-// `self_param_id` is an optional out parameter, populated with the InstId in
-// the resulting parameter patterns that represents the Self parameter.
-static auto GetLocalParamPatternsId(ImportContext& context,
-                                    SemIR::InstBlockId param_patterns_id,
-                                    SemIR::InstId* self_param_id = nullptr)
-    -> SemIR::InstBlockId {
-  CARBON_CHECK(!self_param_id || !self_param_id->has_value());
-  if (!param_patterns_id.has_value() ||
-      param_patterns_id == SemIR::InstBlockId::Empty) {
-    return param_patterns_id;
-  }
-  const auto& param_patterns =
-      context.import_inst_blocks().Get(param_patterns_id);
-  llvm::SmallVector<SemIR::InstId> new_patterns;
-  for (auto inst_id : param_patterns) {
-    // Figure out the pattern structure. This echoes
-    // Function::GetParamPatternInfoFromPatternId.
-    auto inst = context.import_insts().GetWithAttachedType(inst_id);
-
-    auto addr_pattern_id = inst_id;
-    auto addr_inst = inst.TryAs<SemIR::AddrPattern>();
-    if (addr_inst) {
-      inst_id = addr_inst->inner_id;
-      inst = context.import_insts().GetWithAttachedType(inst_id);
-    }
-
-    auto param_pattern_id = inst_id;
-    auto param_pattern = inst.TryAs<SemIR::ValueParamPattern>();
-    if (param_pattern) {
-      inst_id = param_pattern->subpattern_id;
-      inst = context.import_insts().GetWithAttachedType(inst_id);
-    }
-
-    auto binding_id = inst_id;
-    auto binding = inst.As<SemIR::AnyBindingPattern>();
-
-    // Rebuild the pattern.
-    auto entity_name =
-        context.import_entity_names().Get(binding.entity_name_id);
-    auto name_id = GetLocalNameId(context, entity_name.name_id);
-    auto type_id = context.local_context().types().GetTypeIdForTypeConstantId(
-        GetLocalConstantIdChecked(context, binding.type_id));
-
-    auto new_param_id = SemIR::InstId::None;
-    switch (binding.kind) {
-      case SemIR::BindingPattern::Kind: {
-        auto entity_name_id = context.local_entity_names().Add(
-            {.name_id = name_id, .parent_scope_id = SemIR::NameScopeId::None});
-        new_param_id = AddImportedInst<SemIR::BindingPattern>(
-            context, binding_id,
-            {.type_id = type_id, .entity_name_id = entity_name_id});
-        break;
-      }
-      case SemIR::SymbolicBindingPattern::Kind: {
-        // We already imported a constant value for this symbolic binding.
-        // We can reuse most of it, but update the value to point to our
-        // specific parameter, and preserve the constant value.
-        auto bind_const_id = GetLocalConstantIdChecked(context, binding_id);
-        auto new_binding_inst =
-            context.local_insts().GetAs<SemIR::SymbolicBindingPattern>(
-                context.local_constant_values().GetInstId(bind_const_id));
-        new_param_id = AddImportedInst(context, binding_id, new_binding_inst);
-        break;
-      }
-      default: {
-        CARBON_FATAL("Unexpected kind: ", binding.kind);
-      }
-    }
-    if (param_pattern) {
-      new_param_id = AddImportedInst<SemIR::ValueParamPattern>(
-          context, param_pattern_id,
-          {.type_id = type_id,
-           .subpattern_id = new_param_id,
-           .index = param_pattern->index});
-    }
-    if (addr_inst) {
-      type_id = context.local_context().types().GetTypeIdForTypeConstantId(
-          GetLocalConstantIdChecked(context, addr_inst->type_id));
-      new_param_id = AddImportedInst<SemIR::AddrPattern>(
-          context, addr_pattern_id,
-          {.type_id = type_id, .inner_id = new_param_id});
-    }
-    if (self_param_id &&
-        context.import_entity_names().Get(binding.entity_name_id).name_id ==
-            SemIR::NameId::SelfValue) {
-      *self_param_id = new_param_id;
-    }
-    new_patterns.push_back(new_param_id);
-  }
-  return context.local_inst_blocks().Add(new_patterns);
-}
-
-// Returns a version of import_return_slot_pattern_id localized to the current
-// IR.
-static auto GetLocalReturnSlotPatternId(
-    ImportContext& context, SemIR::InstId import_return_slot_pattern_id,
-    SemIR::ConstantId return_type_const_id) -> SemIR::InstId {
-  if (!import_return_slot_pattern_id.has_value()) {
-    return SemIR::InstId::None;
-  }
-
-  auto param_pattern = context.import_insts().GetAs<SemIR::OutParamPattern>(
-      import_return_slot_pattern_id);
-  auto type_id = context.local_context().types().GetTypeIdForTypeConstantId(
-      return_type_const_id);
-
-  auto new_return_slot_pattern_id = AddImportedInst<SemIR::ReturnSlotPattern>(
-      context, param_pattern.subpattern_id,
-      {.type_id = type_id, .type_inst_id = SemIR::TypeInstId::None});
-  return AddImportedInst<SemIR::OutParamPattern>(
-      context, import_return_slot_pattern_id,
-      {.type_id = type_id,
-       .subpattern_id = new_return_slot_pattern_id,
-       .index = param_pattern.index});
-}
-
 // Translates a NameScopeId from the import IR to a local NameScopeId. Adds
 // Translates a NameScopeId from the import IR to a local NameScopeId. Adds
 // unresolved constants to the resolver's work stack.
 // unresolved constants to the resolver's work stack.
 static auto GetLocalNameScopeId(ImportRefResolver& resolver,
 static auto GetLocalNameScopeId(ImportRefResolver& resolver,
@@ -1395,26 +1196,55 @@ static auto RetryOrDone(ImportRefResolver& resolver, SemIR::ConstantId const_id)
   return ResolveResult::Done(const_id);
   return ResolveResult::Done(const_id);
 }
 }
 
 
-// Produces a resolve result for the given instruction that describes a constant
-// value. This should only be used for instructions that describe constants, and
-// not for instructions that represent declarations. For a declaration, we need
-// an associated location, so AddImportedInst should be used instead. Requires
-// that there is no new work.
-static auto ResolveAsUntyped(ImportContext& context, SemIR::Inst inst)
+// Adds `inst` to the local context as a deduplicated constant and returns a
+// successful `ResolveResult`. Requires that there is no new work.
+//
+// This implements phases 2 and 3 of resolving the inst (as described above) for
+// the common case where those phases are combined. Cases where that isn't
+// applicable should instead use `AddPlaceholderImportedInst` and
+// `ReplacePlaceholderImportedInst`.
+//
+// This should not be used for instructions that represent declarations, or
+// other instructions with `constant_kind == InstConstantKind::Unique`, because
+// they should not be deduplicated.
+template <typename InstT>
+static auto ResolveAsDeduplicated(ImportRefResolver& resolver, InstT inst)
     -> ResolveResult {
     -> ResolveResult {
+  static_assert(InstT::Kind.constant_kind() != SemIR::InstConstantKind::Unique,
+                "Use ResolveAsUnique");
+  CARBON_CHECK(!resolver.HasNewWork());
   // AddImportedConstant produces an unattached constant, so its type must
   // AddImportedConstant produces an unattached constant, so its type must
   // be unattached as well.
   // be unattached as well.
-  inst.SetType(
-      context.local_context().types().GetUnattachedType(inst.type_id()));
-  auto result = AddImportedConstant(context.local_context(), inst);
-  CARBON_CHECK(result.is_constant(), "{0} is not constant", inst);
-  return ResolveResult::Done(result);
+  inst.type_id =
+      resolver.local_context().types().GetUnattachedType(inst.type_id);
+  auto const_id = AddImportedConstant(resolver.local_context(), inst);
+  CARBON_CHECK(const_id.is_constant(), "{0} is not constant", inst);
+  return ResolveResult::Done(const_id);
 }
 }
 
 
-// Same as ResolveAsUntyped, but with an explicit type for convenience.
+// Adds `inst` to the local context as a unique constant and returns a
+// successful `ResolveResult`. `import_inst_id` is the corresponding inst ID in
+// the local context. Requires that there is no new work.
+//
+// This implements phases 2 and 3 of resolving the inst (as described above) for
+// the common case where those phases are combined. Cases where that isn't
+// applicable should instead use `AddPlaceholderImportedInst` and
+// `ReplacePlaceholderImportedInst`.
+//
+// This should only be used for instructions that represent declarations, or
+// other instructions with `constant_kind == InstConstantKind::Unique`, because
+// it does not perform deduplication.
 template <typename InstT>
 template <typename InstT>
-static auto ResolveAs(ImportContext& context, InstT inst) -> ResolveResult {
-  return ResolveAsUntyped(context, inst);
+static auto ResolveAsUnique(ImportRefResolver& resolver,
+                            SemIR::InstId import_inst_id, InstT inst)
+    -> ResolveResult {
+  static_assert(InstT::Kind.constant_kind() == SemIR::InstConstantKind::Unique,
+                "Use ResolveAsDeduplicated");
+  CARBON_CHECK(!resolver.HasNewWork());
+  auto inst_id = AddPlaceholderImportedInst(resolver, import_inst_id, inst);
+  auto const_id = SetConstantValue(resolver.local_context(), inst_id, inst);
+  CARBON_CHECK(const_id.is_constant(), "{0} is not constant", inst);
+  return ResolveResult::Done(const_id, inst_id);
 }
 }
 
 
 static auto TryResolveTypedInst(ImportRefResolver& resolver,
 static auto TryResolveTypedInst(ImportRefResolver& resolver,
@@ -1432,10 +1262,43 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
                          inst.adapted_type_inst_id, adapted_type_const_id));
                          inst.adapted_type_inst_id, adapted_type_const_id));
 
 
   // Create a corresponding instruction to represent the declaration.
   // Create a corresponding instruction to represent the declaration.
-  auto inst_id = AddImportedInst<SemIR::AdaptDecl>(
+  return ResolveAsUnique<SemIR::AdaptDecl>(
       resolver, import_inst_id, {.adapted_type_inst_id = adapted_type_inst_id});
       resolver, import_inst_id, {.adapted_type_inst_id = adapted_type_inst_id});
-  return ResolveResult::Done(resolver.local_constant_values().Get(inst_id),
-                             inst_id);
+}
+
+static auto TryResolveTypedInst(ImportRefResolver& resolver,
+                                SemIR::AddrPattern inst,
+                                SemIR::InstId import_inst_id) -> ResolveResult {
+  auto type_const_id = GetLocalConstantId(resolver, inst.type_id);
+  auto inner_id = GetLocalConstantInstId(resolver, inst.inner_id);
+  if (resolver.HasNewWork()) {
+    return ResolveResult::Retry();
+  }
+
+  return ResolveAsUnique<SemIR::AddrPattern>(
+      resolver, import_inst_id,
+      {.type_id = resolver.local_context().types().GetTypeIdForTypeConstantId(
+           type_const_id),
+       .inner_id = inner_id});
+}
+
+template <typename ParamPatternT>
+  requires SemIR::Internal::HasInstCategory<SemIR::AnyParamPattern,
+                                            ParamPatternT>
+static auto TryResolveTypedInst(ImportRefResolver& resolver, ParamPatternT inst,
+                                SemIR::InstId import_inst_id) -> ResolveResult {
+  auto type_const_id = GetLocalConstantId(resolver, inst.type_id);
+  auto subpattern_id = GetLocalConstantInstId(resolver, inst.subpattern_id);
+  if (resolver.HasNewWork()) {
+    return ResolveResult::Retry();
+  }
+
+  return ResolveAsUnique<ParamPatternT>(
+      resolver, import_inst_id,
+      {.type_id = resolver.local_context().types().GetTypeIdForTypeConstantId(
+           type_const_id),
+       .subpattern_id = subpattern_id,
+       .index = inst.index});
 }
 }
 
 
 static auto TryResolveTypedInst(ImportRefResolver& resolver,
 static auto TryResolveTypedInst(ImportRefResolver& resolver,
@@ -1448,7 +1311,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
     return ResolveResult::Retry();
     return ResolveResult::Retry();
   }
   }
 
 
-  return ResolveAs<SemIR::ArrayType>(
+  return ResolveAsDeduplicated<SemIR::ArrayType>(
       resolver, {.type_id = SemIR::TypeType::TypeId,
       resolver, {.type_id = SemIR::TypeType::TypeId,
                  .bound_id = bound_id,
                  .bound_id = bound_id,
                  .element_type_inst_id = element_type_inst_id});
                  .element_type_inst_id = element_type_inst_id});
@@ -1541,7 +1404,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
   // Add a lazy reference to the target declaration.
   // Add a lazy reference to the target declaration.
   auto decl_id = AddImportRef(resolver, inst.decl_id);
   auto decl_id = AddImportRef(resolver, inst.decl_id);
 
 
-  return ResolveAs<SemIR::AssociatedEntity>(
+  return ResolveAsDeduplicated<SemIR::AssociatedEntity>(
       resolver,
       resolver,
       {.type_id = resolver.local_context().types().GetTypeIdForTypeConstantId(
       {.type_id = resolver.local_context().types().GetTypeIdForTypeConstantId(
            type_const_id),
            type_const_id),
@@ -1562,7 +1425,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
 
 
   auto specific_interface =
   auto specific_interface =
       GetLocalSpecificInterface(resolver, inst.GetSpecificInterface(), data);
       GetLocalSpecificInterface(resolver, inst.GetSpecificInterface(), data);
-  return ResolveAs<SemIR::AssociatedEntityType>(
+  return ResolveAsDeduplicated<SemIR::AssociatedEntityType>(
       resolver, {.type_id = SemIR::TypeType::TypeId,
       resolver, {.type_id = SemIR::TypeType::TypeId,
                  .interface_id = specific_interface.interface_id,
                  .interface_id = specific_interface.interface_id,
                  .interface_specific_id = specific_interface.specific_id});
                  .interface_specific_id = specific_interface.specific_id});
@@ -1584,14 +1447,12 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
                          inst.base_type_inst_id, base_type_const_id));
                          inst.base_type_inst_id, base_type_const_id));
 
 
   // Create a corresponding instruction to represent the declaration.
   // Create a corresponding instruction to represent the declaration.
-  auto inst_id = AddImportedInst<SemIR::BaseDecl>(
+  return ResolveAsUnique<SemIR::BaseDecl>(
       resolver, import_inst_id,
       resolver, import_inst_id,
       {.type_id = resolver.local_context().types().GetTypeIdForTypeConstantId(
       {.type_id = resolver.local_context().types().GetTypeIdForTypeConstantId(
            type_const_id),
            type_const_id),
        .base_type_inst_id = base_type_inst_id,
        .base_type_inst_id = base_type_inst_id,
        .index = inst.index});
        .index = inst.index});
-  return ResolveResult::Done(resolver.local_constant_values().Get(inst_id),
-                             inst_id);
 }
 }
 
 
 static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::Vtable inst)
 static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::Vtable inst)
@@ -1605,7 +1466,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::Vtable inst)
 
 
   auto virtual_functions_id = GetLocalCanonicalInstBlockId(
   auto virtual_functions_id = GetLocalCanonicalInstBlockId(
       resolver, inst.virtual_functions_id, virtual_functions);
       resolver, inst.virtual_functions_id, virtual_functions);
-  return ResolveAs<SemIR::Vtable>(
+  return ResolveAsDeduplicated<SemIR::Vtable>(
       resolver,
       resolver,
       {.type_id = resolver.local_context().types().GetTypeIdForTypeConstantId(
       {.type_id = resolver.local_context().types().GetTypeIdForTypeConstantId(
            type_const_id),
            type_const_id),
@@ -1628,12 +1489,10 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
   const auto& import_entity_name =
   const auto& import_entity_name =
       resolver.import_entity_names().Get(inst.entity_name_id);
       resolver.import_entity_names().Get(inst.entity_name_id);
   auto name_id = GetLocalNameId(resolver, import_entity_name.name_id);
   auto name_id = GetLocalNameId(resolver, import_entity_name.name_id);
-  // TODO: Use the same `EntityName` for the `SymbolicBindingPattern` and the
-  // `BindSymbolicName`.
   auto entity_name_id = resolver.local_entity_names().AddSymbolicBindingName(
   auto entity_name_id = resolver.local_entity_names().AddSymbolicBindingName(
       name_id, SemIR::NameScopeId::None, import_entity_name.bind_index(),
       name_id, SemIR::NameScopeId::None, import_entity_name.bind_index(),
       import_entity_name.is_template);
       import_entity_name.is_template);
-  return ResolveAs<SemIR::BindSymbolicName>(
+  return ResolveAsDeduplicated<SemIR::BindSymbolicName>(
       resolver,
       resolver,
       {.type_id =
       {.type_id =
            resolver.local_context().types().GetTypeIdForTypeConstantId(type_id),
            resolver.local_context().types().GetTypeIdForTypeConstantId(type_id),
@@ -1641,10 +1500,13 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
        .value_id = SemIR::InstId::None});
        .value_id = SemIR::InstId::None});
 }
 }
 
 
+template <typename BindingPatternT>
+  requires SemIR::Internal::HasInstCategory<SemIR::AnyBindingPattern,
+                                            BindingPatternT>
 static auto TryResolveTypedInst(ImportRefResolver& resolver,
 static auto TryResolveTypedInst(ImportRefResolver& resolver,
-                                SemIR::SymbolicBindingPattern inst)
-    -> ResolveResult {
-  auto type_id = GetLocalConstantId(resolver, inst.type_id);
+                                BindingPatternT inst,
+                                SemIR::InstId import_inst_id) -> ResolveResult {
+  auto type_const_id = GetLocalConstantId(resolver, inst.type_id);
   if (resolver.HasNewWork()) {
   if (resolver.HasNewWork()) {
     return ResolveResult::Retry();
     return ResolveResult::Retry();
   }
   }
@@ -1652,15 +1514,15 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
   const auto& import_entity_name =
   const auto& import_entity_name =
       resolver.import_entity_names().Get(inst.entity_name_id);
       resolver.import_entity_names().Get(inst.entity_name_id);
   auto name_id = GetLocalNameId(resolver, import_entity_name.name_id);
   auto name_id = GetLocalNameId(resolver, import_entity_name.name_id);
-  // TODO: Use the same `EntityName` for the `SymbolicBindingPattern` and the
-  // `BindSymbolicName`.
-  auto entity_name_id = resolver.local_entity_names().AddSymbolicBindingName(
-      name_id, SemIR::NameScopeId::None, import_entity_name.bind_index(),
-      import_entity_name.is_template);
-  return ResolveAs<SemIR::SymbolicBindingPattern>(
-      resolver,
-      {.type_id =
-           resolver.local_context().types().GetTypeIdForTypeConstantId(type_id),
+  auto entity_name_id = resolver.local_entity_names().Add(
+      {.name_id = name_id,
+       .parent_scope_id = SemIR::NameScopeId::None,
+       .bind_index_value = import_entity_name.bind_index().index,
+       .is_template = import_entity_name.is_template});
+  return ResolveAsUnique<BindingPatternT>(
+      resolver, import_inst_id,
+      {.type_id = resolver.local_context().types().GetTypeIdForTypeConstantId(
+           type_const_id),
        .entity_name_id = entity_name_id});
        .entity_name_id = entity_name_id});
 }
 }
 
 
@@ -1676,7 +1538,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
     return ResolveResult::Retry();
     return ResolveResult::Retry();
   }
   }
 
 
-  return ResolveAs<SemIR::BoundMethod>(
+  return ResolveAsDeduplicated<SemIR::BoundMethod>(
       resolver,
       resolver,
       {.type_id = GetSingletonType(resolver.local_context(),
       {.type_id = GetSingletonType(resolver.local_context(),
                                    SemIR::BoundMethodType::TypeInstId),
                                    SemIR::BoundMethodType::TypeInstId),
@@ -1694,7 +1556,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver, SemIR::Call inst)
     return ResolveResult::Retry();
     return ResolveResult::Retry();
   }
   }
 
 
-  return ResolveAs<SemIR::Call>(
+  return ResolveAsDeduplicated<SemIR::Call>(
       resolver,
       resolver,
       {.type_id =
       {.type_id =
            resolver.local_context().types().GetTypeIdForTypeConstantId(type_id),
            resolver.local_context().types().GetTypeIdForTypeConstantId(type_id),
@@ -1815,9 +1677,10 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
   // Load constants for the definition.
   // Load constants for the definition.
   auto parent_scope_id =
   auto parent_scope_id =
       GetLocalNameScopeId(resolver, import_class.parent_scope_id);
       GetLocalNameScopeId(resolver, import_class.parent_scope_id);
-  LoadLocalPatternConstantIds(resolver,
-                              import_class.implicit_param_patterns_id);
-  LoadLocalPatternConstantIds(resolver, import_class.param_patterns_id);
+  auto implicit_param_patterns = GetLocalInstBlockContents(
+      resolver, import_class.implicit_param_patterns_id);
+  auto param_patterns =
+      GetLocalInstBlockContents(resolver, import_class.param_patterns_id);
   auto generic_data = GetLocalGenericData(resolver, import_class.generic_id);
   auto generic_data = GetLocalGenericData(resolver, import_class.generic_id);
   auto self_const_id = GetLocalConstantId(resolver, import_class.self_type_id);
   auto self_const_id = GetLocalConstantId(resolver, import_class.self_type_id);
   auto complete_type_witness_const_id =
   auto complete_type_witness_const_id =
@@ -1841,10 +1704,11 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
                        : SemIR::InstId::None;
                        : SemIR::InstId::None;
 
 
   new_class.parent_scope_id = parent_scope_id;
   new_class.parent_scope_id = parent_scope_id;
-  new_class.implicit_param_patterns_id = GetLocalParamPatternsId(
-      resolver, import_class.implicit_param_patterns_id);
-  new_class.param_patterns_id =
-      GetLocalParamPatternsId(resolver, import_class.param_patterns_id);
+  new_class.implicit_param_patterns_id = GetLocalCanonicalInstBlockId(
+      resolver, import_class.implicit_param_patterns_id,
+      implicit_param_patterns);
+  new_class.param_patterns_id = GetLocalCanonicalInstBlockId(
+      resolver, import_class.param_patterns_id, param_patterns);
   SetGenericData(resolver, import_class.generic_id, new_class.generic_id,
   SetGenericData(resolver, import_class.generic_id, new_class.generic_id,
                  generic_data);
                  generic_data);
   new_class.self_type_id =
   new_class.self_type_id =
@@ -1888,10 +1752,10 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
             class_const_inst.type_id());
             class_const_inst.type_id());
     auto specific_id =
     auto specific_id =
         GetOrAddLocalSpecific(resolver, inst.specific_id, specific_data);
         GetOrAddLocalSpecific(resolver, inst.specific_id, specific_data);
-    return ResolveAs<SemIR::ClassType>(resolver,
-                                       {.type_id = SemIR::TypeType::TypeId,
-                                        .class_id = generic_class_type.class_id,
-                                        .specific_id = specific_id});
+    return ResolveAsDeduplicated<SemIR::ClassType>(
+        resolver, {.type_id = SemIR::TypeType::TypeId,
+                   .class_id = generic_class_type.class_id,
+                   .specific_id = specific_id});
   }
   }
 }
 }
 
 
@@ -1905,7 +1769,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
   if (resolver.HasNewWork()) {
   if (resolver.HasNewWork()) {
     return ResolveResult::Retry();
     return ResolveResult::Retry();
   }
   }
-  return ResolveAs<SemIR::CompleteTypeWitness>(
+  return ResolveAsDeduplicated<SemIR::CompleteTypeWitness>(
       resolver, {.type_id = GetSingletonType(resolver.local_context(),
       resolver, {.type_id = GetSingletonType(resolver.local_context(),
                                              SemIR::WitnessType::TypeInstId),
                                              SemIR::WitnessType::TypeInstId),
                  .object_repr_type_inst_id = object_repr_type_inst_id});
                  .object_repr_type_inst_id = object_repr_type_inst_id});
@@ -1918,7 +1782,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
   if (resolver.HasNewWork()) {
   if (resolver.HasNewWork()) {
     return ResolveResult::Retry();
     return ResolveResult::Retry();
   }
   }
-  return ResolveAs<SemIR::ConstType>(
+  return ResolveAsDeduplicated<SemIR::ConstType>(
       resolver, {.type_id = SemIR::TypeType::TypeId, .inner_id = inner_id});
       resolver, {.type_id = SemIR::TypeType::TypeId, .inner_id = inner_id});
 }
 }
 
 
@@ -1935,14 +1799,12 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
   if (resolver.HasNewWork()) {
   if (resolver.HasNewWork()) {
     return ResolveResult::Retry();
     return ResolveResult::Retry();
   }
   }
-  auto inst_id = AddImportedInst<SemIR::FieldDecl>(
+  return ResolveAsUnique<SemIR::FieldDecl>(
       resolver, import_inst_id,
       resolver, import_inst_id,
       {.type_id = resolver.local_context().types().GetTypeIdForTypeConstantId(
       {.type_id = resolver.local_context().types().GetTypeIdForTypeConstantId(
            const_id),
            const_id),
        .name_id = GetLocalNameId(resolver, inst.name_id),
        .name_id = GetLocalNameId(resolver, inst.name_id),
        .index = inst.index});
        .index = inst.index});
-  return ResolveResult::Done(resolver.local_constant_values().Get(inst_id),
-                             inst_id);
 }
 }
 
 
 // Make a declaration of a function. This is done as a separate step from
 // Make a declaration of a function. This is done as a separate step from
@@ -2017,12 +1879,17 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
   }
   }
   auto parent_scope_id =
   auto parent_scope_id =
       GetLocalNameScopeId(resolver, import_function.parent_scope_id);
       GetLocalNameScopeId(resolver, import_function.parent_scope_id);
-  LoadLocalPatternConstantIds(resolver,
-                              import_function.implicit_param_patterns_id);
-  LoadLocalPatternConstantIds(resolver, import_function.param_patterns_id);
+  auto implicit_param_patterns = GetLocalInstBlockContents(
+      resolver, import_function.implicit_param_patterns_id);
+  auto param_patterns =
+      GetLocalInstBlockContents(resolver, import_function.param_patterns_id);
   auto generic_data = GetLocalGenericData(resolver, import_function.generic_id);
   auto generic_data = GetLocalGenericData(resolver, import_function.generic_id);
-  auto& new_function = resolver.local_functions().Get(function_id);
+  auto self_param_id =
+      GetLocalConstantInstId(resolver, import_function.self_param_id);
+  auto return_slot_pattern_id =
+      GetLocalConstantInstId(resolver, import_function.return_slot_pattern_id);
 
 
+  auto& new_function = resolver.local_functions().Get(function_id);
   if (resolver.HasNewWork()) {
   if (resolver.HasNewWork()) {
     return ResolveResult::Retry(function_const_id,
     return ResolveResult::Retry(function_const_id,
                                 new_function.first_decl_id());
                                 new_function.first_decl_id());
@@ -2030,14 +1897,13 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
 
 
   // Add the function declaration.
   // Add the function declaration.
   new_function.parent_scope_id = parent_scope_id;
   new_function.parent_scope_id = parent_scope_id;
-  SemIR::InstId self_param_id = SemIR::InstId::None;
-  new_function.implicit_param_patterns_id = GetLocalParamPatternsId(
-      resolver, import_function.implicit_param_patterns_id, &self_param_id);
+  new_function.implicit_param_patterns_id = GetLocalCanonicalInstBlockId(
+      resolver, import_function.implicit_param_patterns_id,
+      implicit_param_patterns);
   new_function.self_param_id = self_param_id;
   new_function.self_param_id = self_param_id;
-  new_function.param_patterns_id =
-      GetLocalParamPatternsId(resolver, import_function.param_patterns_id);
-  new_function.return_slot_pattern_id = GetLocalReturnSlotPatternId(
-      resolver, import_function.return_slot_pattern_id, return_type_const_id);
+  new_function.param_patterns_id = GetLocalCanonicalInstBlockId(
+      resolver, import_function.param_patterns_id, param_patterns);
+  new_function.return_slot_pattern_id = return_slot_pattern_id;
   SetGenericData(resolver, import_function.generic_id, new_function.generic_id,
   SetGenericData(resolver, import_function.generic_id, new_function.generic_id,
                  generic_data);
                  generic_data);
 
 
@@ -2059,7 +1925,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
     return ResolveResult::Retry();
     return ResolveResult::Retry();
   }
   }
   auto fn_type_id = resolver.local_insts().Get(fn_val_id).type_id();
   auto fn_type_id = resolver.local_insts().Get(fn_val_id).type_id();
-  return ResolveAs<SemIR::FunctionType>(
+  return ResolveAsDeduplicated<SemIR::FunctionType>(
       resolver, {.type_id = SemIR::TypeType::TypeId,
       resolver, {.type_id = SemIR::TypeType::TypeId,
                  .function_id = resolver.local_types()
                  .function_id = resolver.local_types()
                                     .GetAs<SemIR::FunctionType>(fn_type_id)
                                     .GetAs<SemIR::FunctionType>(fn_type_id)
@@ -2079,7 +1945,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
     return ResolveResult::Retry();
     return ResolveResult::Retry();
   }
   }
 
 
-  return ResolveAs<SemIR::FunctionTypeWithSelfType>(
+  return ResolveAsDeduplicated<SemIR::FunctionTypeWithSelfType>(
       resolver, {.type_id = SemIR::TypeType::TypeId,
       resolver, {.type_id = SemIR::TypeType::TypeId,
                  .interface_function_type_id = interface_function_type_id,
                  .interface_function_type_id = interface_function_type_id,
                  .self_id = self_id});
                  .self_id = self_id});
@@ -2206,7 +2072,8 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
   // Load constants for the definition.
   // Load constants for the definition.
   auto parent_scope_id =
   auto parent_scope_id =
       GetLocalNameScopeId(resolver, import_impl.parent_scope_id);
       GetLocalNameScopeId(resolver, import_impl.parent_scope_id);
-  LoadLocalPatternConstantIds(resolver, import_impl.implicit_param_patterns_id);
+  auto implicit_param_patterns = GetLocalInstBlockContents(
+      resolver, import_impl.implicit_param_patterns_id);
   auto generic_data = GetLocalGenericData(resolver, import_impl.generic_id);
   auto generic_data = GetLocalGenericData(resolver, import_impl.generic_id);
   auto self_const_id = GetLocalConstantId(
   auto self_const_id = GetLocalConstantId(
       resolver,
       resolver,
@@ -2221,8 +2088,9 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
   }
   }
 
 
   new_impl.parent_scope_id = parent_scope_id;
   new_impl.parent_scope_id = parent_scope_id;
-  new_impl.implicit_param_patterns_id =
-      GetLocalParamPatternsId(resolver, import_impl.implicit_param_patterns_id);
+  new_impl.implicit_param_patterns_id = GetLocalCanonicalInstBlockId(
+      resolver, import_impl.implicit_param_patterns_id,
+      implicit_param_patterns);
   SetGenericData(resolver, import_impl.generic_id, new_impl.generic_id,
   SetGenericData(resolver, import_impl.generic_id, new_impl.generic_id,
                  generic_data);
                  generic_data);
 
 
@@ -2374,9 +2242,10 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
 
 
   auto parent_scope_id =
   auto parent_scope_id =
       GetLocalNameScopeId(resolver, import_interface.parent_scope_id);
       GetLocalNameScopeId(resolver, import_interface.parent_scope_id);
-  LoadLocalPatternConstantIds(resolver,
-                              import_interface.implicit_param_patterns_id);
-  LoadLocalPatternConstantIds(resolver, import_interface.param_patterns_id);
+  auto implicit_param_patterns = GetLocalInstBlockContents(
+      resolver, import_interface.implicit_param_patterns_id);
+  auto param_patterns =
+      GetLocalInstBlockContents(resolver, import_interface.param_patterns_id);
   auto generic_data =
   auto generic_data =
       GetLocalGenericData(resolver, import_interface.generic_id);
       GetLocalGenericData(resolver, import_interface.generic_id);
 
 
@@ -2393,10 +2262,11 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
   }
   }
 
 
   new_interface.parent_scope_id = parent_scope_id;
   new_interface.parent_scope_id = parent_scope_id;
-  new_interface.implicit_param_patterns_id = GetLocalParamPatternsId(
-      resolver, import_interface.implicit_param_patterns_id);
-  new_interface.param_patterns_id =
-      GetLocalParamPatternsId(resolver, import_interface.param_patterns_id);
+  new_interface.implicit_param_patterns_id = GetLocalCanonicalInstBlockId(
+      resolver, import_interface.implicit_param_patterns_id,
+      implicit_param_patterns);
+  new_interface.param_patterns_id = GetLocalCanonicalInstBlockId(
+      resolver, import_interface.param_patterns_id, param_patterns);
   SetGenericData(resolver, import_interface.generic_id,
   SetGenericData(resolver, import_interface.generic_id,
                  new_interface.generic_id, generic_data);
                  new_interface.generic_id, generic_data);
 
 
@@ -2416,7 +2286,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
     return ResolveResult::Retry();
     return ResolveResult::Retry();
   }
   }
 
 
-  return ResolveAs<SemIR::FacetAccessType>(
+  return ResolveAsDeduplicated<SemIR::FacetAccessType>(
       resolver, {.type_id = SemIR::TypeType::TypeId,
       resolver, {.type_id = SemIR::TypeType::TypeId,
                  .facet_value_inst_id = facet_value_inst_id});
                  .facet_value_inst_id = facet_value_inst_id});
 }
 }
@@ -2471,7 +2341,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
   // TODO: Also process the other requirements.
   // TODO: Also process the other requirements.
   SemIR::FacetTypeId facet_type_id =
   SemIR::FacetTypeId facet_type_id =
       resolver.local_facet_types().Add(std::move(local_facet_type_info));
       resolver.local_facet_types().Add(std::move(local_facet_type_info));
-  return ResolveAs<SemIR::FacetType>(
+  return ResolveAsDeduplicated<SemIR::FacetType>(
       resolver,
       resolver,
       {.type_id = SemIR::TypeType::TypeId, .facet_type_id = facet_type_id});
       {.type_id = SemIR::TypeType::TypeId, .facet_type_id = facet_type_id});
 }
 }
@@ -2485,7 +2355,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
     return ResolveResult::Retry();
     return ResolveResult::Retry();
   }
   }
 
 
-  return ResolveAs<SemIR::FacetValue>(
+  return ResolveAsDeduplicated<SemIR::FacetValue>(
       resolver,
       resolver,
       {.type_id =
       {.type_id =
            resolver.local_context().types().GetTypeIdForTypeConstantId(type_id),
            resolver.local_context().types().GetTypeIdForTypeConstantId(type_id),
@@ -2517,7 +2387,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
       GetLocalSpecificInterface(resolver, import_specific_interface, data);
       GetLocalSpecificInterface(resolver, import_specific_interface, data);
   auto query_specific_interface_id =
   auto query_specific_interface_id =
       resolver.local_specific_interfaces().Add(specific_interface);
       resolver.local_specific_interfaces().Add(specific_interface);
-  return ResolveAs<SemIR::LookupImplWitness>(
+  return ResolveAsDeduplicated<SemIR::LookupImplWitness>(
       resolver, {.type_id = GetSingletonType(resolver.local_context(),
       resolver, {.type_id = GetSingletonType(resolver.local_context(),
                                              SemIR::WitnessType::TypeInstId),
                                              SemIR::WitnessType::TypeInstId),
                  .query_self_inst_id = query_self_inst_id,
                  .query_self_inst_id = query_self_inst_id,
@@ -2538,7 +2408,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
 
 
   auto specific_id =
   auto specific_id =
       GetOrAddLocalSpecific(resolver, inst.specific_id, specific_data);
       GetOrAddLocalSpecific(resolver, inst.specific_id, specific_data);
-  return ResolveAs<SemIR::ImplWitness>(
+  return ResolveAsDeduplicated<SemIR::ImplWitness>(
       resolver, {.type_id = GetSingletonType(resolver.local_context(),
       resolver, {.type_id = GetSingletonType(resolver.local_context(),
                                              SemIR::WitnessType::TypeInstId),
                                              SemIR::WitnessType::TypeInstId),
                  .witness_table_id = witness_table_id,
                  .witness_table_id = witness_table_id,
@@ -2554,7 +2424,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
     return ResolveResult::Retry();
     return ResolveResult::Retry();
   }
   }
 
 
-  return ResolveAs<SemIR::ImplWitnessAccess>(
+  return ResolveAsDeduplicated<SemIR::ImplWitnessAccess>(
       resolver,
       resolver,
       {.type_id =
       {.type_id =
            resolver.local_context().types().GetTypeIdForTypeConstantId(type_id),
            resolver.local_context().types().GetTypeIdForTypeConstantId(type_id),
@@ -2579,11 +2449,9 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
   auto elements_id = GetLocalImportRefInstBlock(resolver, inst.elements_id);
   auto elements_id = GetLocalImportRefInstBlock(resolver, inst.elements_id);
 
 
   // Create a corresponding instruction to represent the table.
   // Create a corresponding instruction to represent the table.
-  auto inst_id = AddImportedInst<SemIR::ImplWitnessTable>(
+  return ResolveAsUnique<SemIR::ImplWitnessTable>(
       resolver, import_inst_id,
       resolver, import_inst_id,
       {.elements_id = elements_id, .impl_id = impl_id});
       {.elements_id = elements_id, .impl_id = impl_id});
-  return ResolveResult::Done(resolver.local_constant_values().Get(inst_id),
-                             inst_id);
 }
 }
 
 
 static auto TryResolveTypedInst(ImportRefResolver& resolver,
 static auto TryResolveTypedInst(ImportRefResolver& resolver,
@@ -2600,7 +2468,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
                     : resolver.local_ints().AddSigned(
                     : resolver.local_ints().AddSigned(
                           resolver.import_ints().Get(inst.int_id));
                           resolver.import_ints().Get(inst.int_id));
 
 
-  return ResolveAs<SemIR::IntValue>(
+  return ResolveAsDeduplicated<SemIR::IntValue>(
       resolver,
       resolver,
       {.type_id =
       {.type_id =
            resolver.local_context().types().GetTypeIdForTypeConstantId(type_id),
            resolver.local_context().types().GetTypeIdForTypeConstantId(type_id),
@@ -2615,10 +2483,10 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
     return ResolveResult::Retry();
     return ResolveResult::Retry();
   }
   }
 
 
-  return ResolveAs<SemIR::IntType>(resolver,
-                                   {.type_id = SemIR::TypeType::TypeId,
-                                    .int_kind = inst.int_kind,
-                                    .bit_width_id = bit_width_id});
+  return ResolveAsDeduplicated<SemIR::IntType>(
+      resolver, {.type_id = SemIR::TypeType::TypeId,
+                 .int_kind = inst.int_kind,
+                 .bit_width_id = bit_width_id});
 }
 }
 
 
 static auto TryResolveTypedInst(ImportRefResolver& resolver,
 static auto TryResolveTypedInst(ImportRefResolver& resolver,
@@ -2668,7 +2536,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
     return ResolveResult::Retry();
     return ResolveResult::Retry();
   }
   }
 
 
-  return ResolveAs<SemIR::PatternType>(
+  return ResolveAsDeduplicated<SemIR::PatternType>(
       resolver, {.type_id = SemIR::TypeType::TypeId,
       resolver, {.type_id = SemIR::TypeType::TypeId,
                  .scrutinee_type_inst_id = scrutinee_type_inst_id});
                  .scrutinee_type_inst_id = scrutinee_type_inst_id});
 }
 }
@@ -2681,7 +2549,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
     return ResolveResult::Retry();
     return ResolveResult::Retry();
   }
   }
 
 
-  return ResolveAs<SemIR::PointerType>(
+  return ResolveAsDeduplicated<SemIR::PointerType>(
       resolver, {.type_id = SemIR::TypeType::TypeId, .pointee_id = pointee_id});
       resolver, {.type_id = SemIR::TypeType::TypeId, .pointee_id = pointee_id});
 }
 }
 
 
@@ -2697,12 +2565,27 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
     return ResolveResult::Retry();
     return ResolveResult::Retry();
   }
   }
 
 
-  return ResolveAs<SemIR::RequireCompleteType>(
+  return ResolveAsDeduplicated<SemIR::RequireCompleteType>(
       resolver, {.type_id = GetSingletonType(resolver.local_context(),
       resolver, {.type_id = GetSingletonType(resolver.local_context(),
                                              SemIR::WitnessType::TypeInstId),
                                              SemIR::WitnessType::TypeInstId),
                  .complete_type_inst_id = complete_type_inst_id});
                  .complete_type_inst_id = complete_type_inst_id});
 }
 }
 
 
+static auto TryResolveTypedInst(ImportRefResolver& resolver,
+                                SemIR::ReturnSlotPattern inst,
+                                SemIR::InstId import_inst_id) -> ResolveResult {
+  auto type_const_id = GetLocalConstantId(resolver, inst.type_id);
+  if (resolver.HasNewWork()) {
+    return ResolveResult::Retry();
+  }
+
+  return ResolveAsUnique<SemIR::ReturnSlotPattern>(
+      resolver, import_inst_id,
+      {.type_id = resolver.local_context().types().GetTypeIdForTypeConstantId(
+           type_const_id),
+       .type_inst_id = SemIR::TypeInstId::None});
+}
+
 static auto TryResolveTypedInst(ImportRefResolver& resolver,
 static auto TryResolveTypedInst(ImportRefResolver& resolver,
                                 SemIR::SpecificFunction inst) -> ResolveResult {
                                 SemIR::SpecificFunction inst) -> ResolveResult {
   auto type_const_id = GetLocalConstantId(resolver, inst.type_id);
   auto type_const_id = GetLocalConstantId(resolver, inst.type_id);
@@ -2716,7 +2599,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
       type_const_id);
       type_const_id);
   auto specific_id =
   auto specific_id =
       GetOrAddLocalSpecific(resolver, inst.specific_id, specific_data);
       GetOrAddLocalSpecific(resolver, inst.specific_id, specific_data);
-  return ResolveAs<SemIR::SpecificFunction>(
+  return ResolveAsDeduplicated<SemIR::SpecificFunction>(
       resolver,
       resolver,
       {.type_id = type_id, .callee_id = callee_id, .specific_id = specific_id});
       {.type_id = type_id, .callee_id = callee_id, .specific_id = specific_id});
 }
 }
@@ -2734,7 +2617,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
                                   SemIR::SpecificFunctionType::TypeInstId);
                                   SemIR::SpecificFunctionType::TypeInstId);
   auto specific_id =
   auto specific_id =
       GetOrAddLocalSpecific(resolver, inst.specific_id, specific_data);
       GetOrAddLocalSpecific(resolver, inst.specific_id, specific_data);
-  return ResolveAs<SemIR::SpecificImplFunction>(
+  return ResolveAsDeduplicated<SemIR::SpecificImplFunction>(
       resolver,
       resolver,
       {.type_id = type_id, .callee_id = callee_id, .specific_id = specific_id});
       {.type_id = type_id, .callee_id = callee_id, .specific_id = specific_id});
 }
 }
@@ -2763,7 +2646,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
         {.name_id = name_id, .type_inst_id = field_type_inst_id});
         {.name_id = name_id, .type_inst_id = field_type_inst_id});
   }
   }
 
 
-  return ResolveAs<SemIR::StructType>(
+  return ResolveAsDeduplicated<SemIR::StructType>(
       resolver, {.type_id = SemIR::TypeType::TypeId,
       resolver, {.type_id = SemIR::TypeType::TypeId,
                  .fields_id = resolver.local_struct_type_fields().AddCanonical(
                  .fields_id = resolver.local_struct_type_fields().AddCanonical(
                      new_fields)});
                      new_fields)});
@@ -2777,7 +2660,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
     return ResolveResult::Retry();
     return ResolveResult::Retry();
   }
   }
 
 
-  return ResolveAs<SemIR::StructValue>(
+  return ResolveAsDeduplicated<SemIR::StructValue>(
       resolver,
       resolver,
       {.type_id =
       {.type_id =
            resolver.local_context().types().GetTypeIdForTypeConstantId(type_id),
            resolver.local_context().types().GetTypeIdForTypeConstantId(type_id),
@@ -2803,7 +2686,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
     return ResolveResult::Retry();
     return ResolveResult::Retry();
   }
   }
 
 
-  return ResolveAs<SemIR::TupleType>(
+  return ResolveAsDeduplicated<SemIR::TupleType>(
       resolver, {.type_id = SemIR::TypeType::TypeId,
       resolver, {.type_id = SemIR::TypeType::TypeId,
                  .type_elements_id = GetLocalCanonicalInstBlockId(
                  .type_elements_id = GetLocalCanonicalInstBlockId(
                      resolver, inst.type_elements_id, type_inst_ids)});
                      resolver, inst.type_elements_id, type_inst_ids)});
@@ -2817,7 +2700,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
     return ResolveResult::Retry();
     return ResolveResult::Retry();
   }
   }
 
 
-  return ResolveAs<SemIR::TupleValue>(
+  return ResolveAsDeduplicated<SemIR::TupleValue>(
       resolver,
       resolver,
       {.type_id =
       {.type_id =
            resolver.local_context().types().GetTypeIdForTypeConstantId(type_id),
            resolver.local_context().types().GetTypeIdForTypeConstantId(type_id),
@@ -2837,12 +2720,28 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
     return ResolveResult::Retry();
     return ResolveResult::Retry();
   }
   }
 
 
-  return ResolveAs<SemIR::UnboundElementType>(
+  return ResolveAsDeduplicated<SemIR::UnboundElementType>(
       resolver, {.type_id = SemIR::TypeType::TypeId,
       resolver, {.type_id = SemIR::TypeType::TypeId,
                  .class_type_inst_id = class_const_inst_id,
                  .class_type_inst_id = class_const_inst_id,
                  .element_type_inst_id = elem_const_inst_id});
                  .element_type_inst_id = elem_const_inst_id});
 }
 }
 
 
+static auto TryResolveTypedInst(ImportRefResolver& resolver,
+                                SemIR::VarPattern inst,
+                                SemIR::InstId import_inst_id) -> ResolveResult {
+  auto type_const_id = GetLocalConstantId(resolver, inst.type_id);
+  auto subpattern_id = GetLocalConstantInstId(resolver, inst.subpattern_id);
+  if (resolver.HasNewWork()) {
+    return ResolveResult::Retry();
+  }
+
+  return ResolveAsUnique<SemIR::VarPattern>(
+      resolver, import_inst_id,
+      {.type_id = resolver.local_context().types().GetTypeIdForTypeConstantId(
+           type_const_id),
+       .subpattern_id = subpattern_id});
+}
+
 // Tries to resolve the InstId, returning a canonical constant when ready, or
 // Tries to resolve the InstId, returning a canonical constant when ready, or
 // `None` if more has been added to the stack. This is the same as
 // `None` if more has been added to the stack. This is the same as
 // TryResolveInst, except that it may resolve symbolic constants as canonical
 // TryResolveInst, except that it may resolve symbolic constants as canonical
@@ -2866,6 +2765,9 @@ static auto TryResolveInstCanonical(ImportRefResolver& resolver,
     case CARBON_KIND(SemIR::AdaptDecl inst): {
     case CARBON_KIND(SemIR::AdaptDecl inst): {
       return TryResolveTypedInst(resolver, inst, inst_id);
       return TryResolveTypedInst(resolver, inst, inst_id);
     }
     }
+    case CARBON_KIND(SemIR::AddrPattern inst): {
+      return TryResolveTypedInst(resolver, inst, inst_id);
+    }
     case CARBON_KIND(SemIR::ArrayType inst): {
     case CARBON_KIND(SemIR::ArrayType inst): {
       return TryResolveTypedInst(resolver, inst);
       return TryResolveTypedInst(resolver, inst);
     }
     }
@@ -2884,6 +2786,9 @@ static auto TryResolveInstCanonical(ImportRefResolver& resolver,
     case CARBON_KIND(SemIR::BindAlias inst): {
     case CARBON_KIND(SemIR::BindAlias inst): {
       return TryResolveTypedInst(resolver, inst);
       return TryResolveTypedInst(resolver, inst);
     }
     }
+    case CARBON_KIND(SemIR::BindingPattern inst): {
+      return TryResolveTypedInst(resolver, inst, inst_id);
+    }
     case SemIR::BindName::Kind: {
     case SemIR::BindName::Kind: {
       // TODO: Should we be resolving BindNames at all?
       // TODO: Should we be resolving BindNames at all?
       return ResolveResult::Done(SemIR::ConstantId::NotConstant);
       return ResolveResult::Done(SemIR::ConstantId::NotConstant);
@@ -2969,30 +2874,39 @@ static auto TryResolveInstCanonical(ImportRefResolver& resolver,
     case CARBON_KIND(SemIR::Namespace inst): {
     case CARBON_KIND(SemIR::Namespace inst): {
       return TryResolveTypedInst(resolver, inst, inst_id);
       return TryResolveTypedInst(resolver, inst, inst_id);
     }
     }
+    case CARBON_KIND(SemIR::OutParamPattern inst): {
+      return TryResolveTypedInst(resolver, inst, inst_id);
+    }
     case CARBON_KIND(SemIR::PatternType inst): {
     case CARBON_KIND(SemIR::PatternType inst): {
       return TryResolveTypedInst(resolver, inst);
       return TryResolveTypedInst(resolver, inst);
     }
     }
     case CARBON_KIND(SemIR::PointerType inst): {
     case CARBON_KIND(SemIR::PointerType inst): {
       return TryResolveTypedInst(resolver, inst);
       return TryResolveTypedInst(resolver, inst);
     }
     }
+    case CARBON_KIND(SemIR::RefParamPattern inst): {
+      return TryResolveTypedInst(resolver, inst, inst_id);
+    }
     case CARBON_KIND(SemIR::RequireCompleteType inst): {
     case CARBON_KIND(SemIR::RequireCompleteType inst): {
       return TryResolveTypedInst(resolver, inst);
       return TryResolveTypedInst(resolver, inst);
     }
     }
+    case CARBON_KIND(SemIR::ReturnSlotPattern inst): {
+      return TryResolveTypedInst(resolver, inst, inst_id);
+    }
     case CARBON_KIND(SemIR::SpecificFunction inst): {
     case CARBON_KIND(SemIR::SpecificFunction inst): {
       return TryResolveTypedInst(resolver, inst);
       return TryResolveTypedInst(resolver, inst);
     }
     }
     case CARBON_KIND(SemIR::SpecificImplFunction inst): {
     case CARBON_KIND(SemIR::SpecificImplFunction inst): {
       return TryResolveTypedInst(resolver, inst);
       return TryResolveTypedInst(resolver, inst);
     }
     }
-    case CARBON_KIND(SemIR::SymbolicBindingPattern inst): {
-      return TryResolveTypedInst(resolver, inst);
-    }
     case CARBON_KIND(SemIR::StructType inst): {
     case CARBON_KIND(SemIR::StructType inst): {
       return TryResolveTypedInst(resolver, inst);
       return TryResolveTypedInst(resolver, inst);
     }
     }
     case CARBON_KIND(SemIR::StructValue inst): {
     case CARBON_KIND(SemIR::StructValue inst): {
       return TryResolveTypedInst(resolver, inst);
       return TryResolveTypedInst(resolver, inst);
     }
     }
+    case CARBON_KIND(SemIR::SymbolicBindingPattern inst): {
+      return TryResolveTypedInst(resolver, inst, inst_id);
+    }
     case CARBON_KIND(SemIR::TupleType inst): {
     case CARBON_KIND(SemIR::TupleType inst): {
       return TryResolveTypedInst(resolver, inst);
       return TryResolveTypedInst(resolver, inst);
     }
     }
@@ -3002,6 +2916,12 @@ static auto TryResolveInstCanonical(ImportRefResolver& resolver,
     case CARBON_KIND(SemIR::UnboundElementType inst): {
     case CARBON_KIND(SemIR::UnboundElementType inst): {
       return TryResolveTypedInst(resolver, inst);
       return TryResolveTypedInst(resolver, inst);
     }
     }
+    case CARBON_KIND(SemIR::ValueParamPattern inst): {
+      return TryResolveTypedInst(resolver, inst, inst_id);
+    }
+    case CARBON_KIND(SemIR::VarPattern inst): {
+      return TryResolveTypedInst(resolver, inst, inst_id);
+    }
     case CARBON_KIND(SemIR::Vtable inst): {
     case CARBON_KIND(SemIR::Vtable inst): {
       return TryResolveTypedInst(resolver, inst);
       return TryResolveTypedInst(resolver, inst);
     }
     }

+ 88 - 68
toolchain/check/merge.cpp

@@ -203,13 +203,10 @@ static auto EntityHasParamError(Context& context, const DeclParams& info)
 // to provide a diagnostic.
 // to provide a diagnostic.
 static auto CheckRedeclParam(Context& context, bool is_implicit_param,
 static auto CheckRedeclParam(Context& context, bool is_implicit_param,
                              int32_t param_index,
                              int32_t param_index,
-                             SemIR::InstId new_param_pattern_id,
-                             SemIR::InstId prev_param_pattern_id,
+                             SemIR::InstId orig_new_param_pattern_id,
+                             SemIR::InstId orig_prev_param_pattern_id,
                              SemIR::SpecificId prev_specific_id, bool diagnose,
                              SemIR::SpecificId prev_specific_id, bool diagnose,
                              bool check_syntax, bool check_self) -> bool {
                              bool check_syntax, bool check_self) -> bool {
-  auto orig_new_param_pattern_id = new_param_pattern_id;
-  auto orig_prev_param_pattern_id = prev_param_pattern_id;
-
   // TODO: Consider differentiating between type and name mistakes. For now,
   // TODO: Consider differentiating between type and name mistakes. For now,
   // taking the simpler approach because I also think we may want to refactor
   // taking the simpler approach because I also think we may want to refactor
   // params.
   // params.
@@ -232,78 +229,101 @@ static auto CheckRedeclParam(Context& context, bool is_implicit_param,
         .Emit();
         .Emit();
   };
   };
 
 
-  auto new_param_pattern = context.insts().Get(new_param_pattern_id);
-  auto prev_param_pattern = context.insts().Get(prev_param_pattern_id);
-  if (new_param_pattern.kind() != prev_param_pattern.kind()) {
-    emit_diagnostic();
-    return false;
-  }
+  struct PatternPair {
+    SemIR::InstId prev_id;
+    SemIR::InstId new_id;
+  };
 
 
-  if (new_param_pattern.Is<SemIR::AddrPattern>()) {
-    new_param_pattern_id = new_param_pattern.As<SemIR::AddrPattern>().inner_id;
-    new_param_pattern = context.insts().Get(new_param_pattern_id);
-    prev_param_pattern_id =
-        prev_param_pattern.As<SemIR::AddrPattern>().inner_id;
-    prev_param_pattern = context.insts().Get(prev_param_pattern_id);
-    if (new_param_pattern.kind() != prev_param_pattern.kind()) {
-      emit_diagnostic();
-      return false;
-    }
-  }
+  llvm::SmallVector<PatternPair, 1> pattern_stack;
+
+  pattern_stack.push_back({.prev_id = orig_prev_param_pattern_id,
+                           .new_id = orig_new_param_pattern_id});
 
 
-  if (new_param_pattern.Is<SemIR::AnyParamPattern>()) {
-    new_param_pattern_id =
-        new_param_pattern.As<SemIR::ValueParamPattern>().subpattern_id;
-    new_param_pattern = context.insts().Get(new_param_pattern_id);
-    prev_param_pattern_id =
-        prev_param_pattern.As<SemIR::ValueParamPattern>().subpattern_id;
-    prev_param_pattern = context.insts().Get(prev_param_pattern_id);
+  do {
+    auto patterns = pattern_stack.pop_back_val();
+    auto new_param_pattern = context.insts().Get(patterns.new_id);
+    auto prev_param_pattern = context.insts().Get(patterns.prev_id);
     if (new_param_pattern.kind() != prev_param_pattern.kind()) {
     if (new_param_pattern.kind() != prev_param_pattern.kind()) {
       emit_diagnostic();
       emit_diagnostic();
       return false;
       return false;
     }
     }
-  }
 
 
-  auto new_name_id =
-      context.entity_names()
-          .Get(new_param_pattern.As<SemIR::AnyBindingPattern>().entity_name_id)
-          .name_id;
-  auto prev_name_id =
-      context.entity_names()
-          .Get(prev_param_pattern.As<SemIR::AnyBindingPattern>().entity_name_id)
-          .name_id;
-
-  if (!check_self && new_name_id == SemIR::NameId::SelfValue &&
-      prev_name_id == SemIR::NameId::SelfValue) {
-    return true;
-  }
+    switch (new_param_pattern.kind()) {
+      case SemIR::AddrPattern::Kind: {
+        pattern_stack.push_back(
+            {.prev_id = prev_param_pattern.As<SemIR::AddrPattern>().inner_id,
+             .new_id = new_param_pattern.As<SemIR::AddrPattern>().inner_id});
+        break;
+      }
+      case SemIR::OutParamPattern::Kind:
+      case SemIR::RefParamPattern::Kind:
+      case SemIR::ValueParamPattern::Kind: {
+        pattern_stack.push_back(
+            {.prev_id =
+                 prev_param_pattern.As<SemIR::AnyParamPattern>().subpattern_id,
+             .new_id =
+                 new_param_pattern.As<SemIR::AnyParamPattern>().subpattern_id});
+        break;
+      }
+      case SemIR::VarPattern::Kind:
+        pattern_stack.push_back(
+            {.prev_id =
+                 prev_param_pattern.As<SemIR::VarPattern>().subpattern_id,
+             .new_id =
+                 new_param_pattern.As<SemIR::VarPattern>().subpattern_id});
+        break;
+      case SemIR::BindingPattern::Kind:
+      case SemIR::SymbolicBindingPattern::Kind: {
+        auto new_name_id =
+            context.entity_names()
+                .Get(new_param_pattern.As<SemIR::AnyBindingPattern>()
+                         .entity_name_id)
+                .name_id;
+        auto prev_name_id =
+            context.entity_names()
+                .Get(prev_param_pattern.As<SemIR::AnyBindingPattern>()
+                         .entity_name_id)
+                .name_id;
+
+        if (!check_self && new_name_id == SemIR::NameId::SelfValue &&
+            prev_name_id == SemIR::NameId::SelfValue) {
+          break;
+        }
 
 
-  auto prev_param_type_id = SemIR::GetTypeOfInstInSpecific(
-      context.sem_ir(), prev_specific_id, prev_param_pattern_id);
-  if (!context.types().AreEqualAcrossDeclarations(new_param_pattern.type_id(),
-                                                  prev_param_type_id)) {
-    if (!diagnose) {
-      return false;
-    }
-    CARBON_DIAGNOSTIC(RedeclParamDiffersType, Error,
-                      "type {3} of {0:implicit |}parameter {1} in "
-                      "redeclaration differs from previous parameter type {2}",
-                      Diagnostics::BoolAsSelect, int32_t, SemIR::TypeId,
-                      SemIR::TypeId);
-    context.emitter()
-        .Build(orig_new_param_pattern_id, RedeclParamDiffersType,
-               is_implicit_param, param_index + 1, prev_param_type_id,
-               new_param_pattern.type_id())
-        .Note(orig_prev_param_pattern_id, RedeclParamPrevious,
-              is_implicit_param)
-        .Emit();
-    return false;
-  }
+        auto prev_param_type_id = SemIR::GetTypeOfInstInSpecific(
+            context.sem_ir(), prev_specific_id, patterns.prev_id);
+        if (!context.types().AreEqualAcrossDeclarations(
+                new_param_pattern.type_id(), prev_param_type_id)) {
+          if (!diagnose) {
+            return false;
+          }
+          CARBON_DIAGNOSTIC(
+              RedeclParamDiffersType, Error,
+              "type {3} of {0:implicit |}parameter {1} in "
+              "redeclaration differs from previous parameter type {2}",
+              Diagnostics::BoolAsSelect, int32_t, SemIR::TypeId, SemIR::TypeId);
+          context.emitter()
+              .Build(orig_new_param_pattern_id, RedeclParamDiffersType,
+                     is_implicit_param, param_index + 1, prev_param_type_id,
+                     new_param_pattern.type_id())
+              .Note(orig_prev_param_pattern_id, RedeclParamPrevious,
+                    is_implicit_param)
+              .Emit();
+          return false;
+        }
 
 
-  if (check_syntax && new_name_id != prev_name_id) {
-    emit_diagnostic();
-    return false;
-  }
+        if (check_syntax && new_name_id != prev_name_id) {
+          emit_diagnostic();
+          return false;
+        }
+        break;
+      }
+      default: {
+        CARBON_FATAL("Unexpected inst kind in parameter pattern: {0}",
+                     new_param_pattern.kind());
+      }
+    }
+  } while (!pattern_stack.empty());
 
 
   return true;
   return true;
 }
 }

+ 1 - 1
toolchain/check/testdata/builtins/float/make_type.carbon

@@ -97,12 +97,12 @@ var dyn: Float(dyn_size);
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %Float.type: type = fn_type @Float [concrete]
 // CHECK:STDOUT:   %Float.type: type = fn_type @Float [concrete]
 // CHECK:STDOUT:   %Float: %Float.type = struct_value () [concrete]
 // CHECK:STDOUT:   %Float: %Float.type = struct_value () [concrete]
+// CHECK:STDOUT:   %pattern_type.98f: type = pattern_type type [concrete]
 // CHECK:STDOUT:   %int_32: Core.IntLiteral = int_value 32 [concrete]
 // CHECK:STDOUT:   %int_32: Core.IntLiteral = int_value 32 [concrete]
 // CHECK:STDOUT:   %Int.type: type = generic_class_type @Int [concrete]
 // CHECK:STDOUT:   %Int.type: type = generic_class_type @Int [concrete]
 // CHECK:STDOUT:   %Int.generic: %Int.type = struct_value () [concrete]
 // CHECK:STDOUT:   %Int.generic: %Int.type = struct_value () [concrete]
 // CHECK:STDOUT:   %i32: type = class_type @Int, @Int(%int_32) [concrete]
 // CHECK:STDOUT:   %i32: type = class_type @Int, @Int(%int_32) [concrete]
 // CHECK:STDOUT:   %pattern_type.501: type = pattern_type %i32 [concrete]
 // CHECK:STDOUT:   %pattern_type.501: type = pattern_type %i32 [concrete]
-// CHECK:STDOUT:   %pattern_type.98f: type = pattern_type type [concrete]
 // CHECK:STDOUT:   %int_64.fab: Core.IntLiteral = int_value 64 [concrete]
 // CHECK:STDOUT:   %int_64.fab: Core.IntLiteral = int_value 64 [concrete]
 // CHECK:STDOUT:   %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete]
 // CHECK:STDOUT:   %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete]
 // CHECK:STDOUT:   %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete]
 // CHECK:STDOUT:   %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete]

+ 1 - 1
toolchain/check/testdata/class/generic/base_is_generic.carbon

@@ -291,7 +291,7 @@ fn H() {
 // CHECK:STDOUT:   %Main.import_ref.8e0 = import_ref Main//extend_generic_base, inst26 [no loc], unloaded
 // CHECK:STDOUT:   %Main.import_ref.8e0 = import_ref Main//extend_generic_base, inst26 [no loc], unloaded
 // CHECK:STDOUT:   %Main.import_ref.7f7: @Base.%Base.elem (%Base.elem.9af) = import_ref Main//extend_generic_base, loc5_8, loaded [concrete = %.e66]
 // CHECK:STDOUT:   %Main.import_ref.7f7: @Base.%Base.elem (%Base.elem.9af) = import_ref Main//extend_generic_base, loc5_8, loaded [concrete = %.e66]
 // CHECK:STDOUT:   %Main.import_ref.bd0: <witness> = import_ref Main//extend_generic_base, loc14_1, loaded [concrete = constants.%complete_type.b07]
 // CHECK:STDOUT:   %Main.import_ref.bd0: <witness> = import_ref Main//extend_generic_base, loc14_1, loaded [concrete = constants.%complete_type.b07]
-// CHECK:STDOUT:   %Main.import_ref.f6c = import_ref Main//extend_generic_base, inst77 [no loc], unloaded
+// CHECK:STDOUT:   %Main.import_ref.f6c = import_ref Main//extend_generic_base, inst76 [no loc], unloaded
 // CHECK:STDOUT:   %Main.import_ref.d24 = import_ref Main//extend_generic_base, loc13_27, unloaded
 // CHECK:STDOUT:   %Main.import_ref.d24 = import_ref Main//extend_generic_base, loc13_27, unloaded
 // CHECK:STDOUT:   %Main.import_ref.77a301.2: type = import_ref Main//extend_generic_base, loc13_26, loaded [concrete = constants.%Base.7a8]
 // CHECK:STDOUT:   %Main.import_ref.77a301.2: type = import_ref Main//extend_generic_base, loc13_26, loaded [concrete = constants.%Base.7a8]
 // CHECK:STDOUT:   %Core.Int: %Int.type = import_ref Core//prelude/types/int, Int, loaded [concrete = constants.%Int.generic]
 // CHECK:STDOUT:   %Core.Int: %Int.type = import_ref Core//prelude/types/int, Int, loaded [concrete = constants.%Int.generic]

+ 1 - 1
toolchain/check/testdata/class/generic/import.carbon

@@ -310,7 +310,7 @@ class Class(U:! type) {
 // CHECK:STDOUT:     import Core//prelude
 // CHECK:STDOUT:     import Core//prelude
 // CHECK:STDOUT:     import Core//prelude/...
 // CHECK:STDOUT:     import Core//prelude/...
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %Main.import_ref.773: @impl.a8d.%Convert.type (%Convert.type.9a6) = import_ref Main//foo, inst143 [indirect], loaded [symbolic = @impl.a8d.%Convert (constants.%Convert.458)]
+// CHECK:STDOUT:   %Main.import_ref.773: @impl.a8d.%Convert.type (%Convert.type.9a6) = import_ref Main//foo, inst140 [indirect], loaded [symbolic = @impl.a8d.%Convert (constants.%Convert.458)]
 // CHECK:STDOUT:   %ImplicitAs.impl_witness_table.1ad = impl_witness_table (%Main.import_ref.773), @impl.a8d [concrete]
 // CHECK:STDOUT:   %ImplicitAs.impl_witness_table.1ad = impl_witness_table (%Main.import_ref.773), @impl.a8d [concrete]
 // CHECK:STDOUT:   %Main.import_ref.5ab3ec.1: type = import_ref Main//foo, loc4_13, loaded [symbolic = @Class.%T.1 (constants.%T)]
 // CHECK:STDOUT:   %Main.import_ref.5ab3ec.1: type = import_ref Main//foo, loc4_13, loaded [symbolic = @Class.%T.1 (constants.%T)]
 // CHECK:STDOUT:   %Main.import_ref.5ab3ec.2: type = import_ref Main//foo, loc6_21, loaded [symbolic = @CompleteClass.%T (constants.%T)]
 // CHECK:STDOUT:   %Main.import_ref.5ab3ec.2: type = import_ref Main//foo, loc6_21, loaded [symbolic = @CompleteClass.%T (constants.%T)]

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

@@ -234,11 +234,11 @@ fn Run() {
 // CHECK:STDOUT:   %ImplicitAs.impl_witness_table.e36 = impl_witness_table (%Core.import_ref.a86), @impl.c81 [concrete]
 // CHECK:STDOUT:   %ImplicitAs.impl_witness_table.e36 = impl_witness_table (%Core.import_ref.a86), @impl.c81 [concrete]
 // CHECK:STDOUT:   %.d33: %Field.elem = field_decl x, element0 [concrete]
 // CHECK:STDOUT:   %.d33: %Field.elem = field_decl x, element0 [concrete]
 // CHECK:STDOUT:   %Main.import_ref.8f24d3.2: <witness> = import_ref Main//a, loc16_1, loaded [concrete = constants.%complete_type.357]
 // CHECK:STDOUT:   %Main.import_ref.8f24d3.2: <witness> = import_ref Main//a, loc16_1, loaded [concrete = constants.%complete_type.357]
-// CHECK:STDOUT:   %Main.import_ref.39e731.1 = import_ref Main//a, inst60 [no loc], unloaded
+// CHECK:STDOUT:   %Main.import_ref.39e731.1 = import_ref Main//a, inst59 [no loc], unloaded
 // CHECK:STDOUT:   %Main.import_ref.760: %F.type = import_ref Main//a, loc14_21, loaded [concrete = constants.%F]
 // CHECK:STDOUT:   %Main.import_ref.760: %F.type = import_ref Main//a, loc14_21, loaded [concrete = constants.%F]
 // CHECK:STDOUT:   %Main.import_ref.26e: %G.type = import_ref Main//a, loc15_27, loaded [concrete = constants.%G]
 // CHECK:STDOUT:   %Main.import_ref.26e: %G.type = import_ref Main//a, loc15_27, loaded [concrete = constants.%G]
 // CHECK:STDOUT:   %Main.import_ref.8f24d3.3: <witness> = import_ref Main//a, loc16_1, loaded [concrete = constants.%complete_type.357]
 // CHECK:STDOUT:   %Main.import_ref.8f24d3.3: <witness> = import_ref Main//a, loc16_1, loaded [concrete = constants.%complete_type.357]
-// CHECK:STDOUT:   %Main.import_ref.39e731.2 = import_ref Main//a, inst60 [no loc], unloaded
+// CHECK:STDOUT:   %Main.import_ref.39e731.2 = import_ref Main//a, inst59 [no loc], unloaded
 // CHECK:STDOUT:   %Main.import_ref.42a = import_ref Main//a, loc14_21, unloaded
 // CHECK:STDOUT:   %Main.import_ref.42a = import_ref Main//a, loc14_21, unloaded
 // CHECK:STDOUT:   %Main.import_ref.67a = import_ref Main//a, loc15_27, unloaded
 // CHECK:STDOUT:   %Main.import_ref.67a = import_ref Main//a, loc15_27, unloaded
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }

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

@@ -195,7 +195,7 @@ fn Run() {
 // CHECK:STDOUT:   %Main.import_ref.e67: %Base.elem = import_ref Main//a, loc8_8, loaded [concrete = %.720]
 // CHECK:STDOUT:   %Main.import_ref.e67: %Base.elem = import_ref Main//a, loc8_8, loaded [concrete = %.720]
 // CHECK:STDOUT:   %Main.import_ref.2e4 = import_ref Main//a, loc9_13, unloaded
 // CHECK:STDOUT:   %Main.import_ref.2e4 = import_ref Main//a, loc9_13, unloaded
 // CHECK:STDOUT:   %Main.import_ref.c5f: <witness> = import_ref Main//a, loc14_1, loaded [concrete = constants.%complete_type.15c]
 // CHECK:STDOUT:   %Main.import_ref.c5f: <witness> = import_ref Main//a, loc14_1, loaded [concrete = constants.%complete_type.15c]
-// CHECK:STDOUT:   %Main.import_ref.9a9 = import_ref Main//a, inst74 [no loc], unloaded
+// CHECK:STDOUT:   %Main.import_ref.9a9 = import_ref Main//a, inst73 [no loc], unloaded
 // CHECK:STDOUT:   %Main.import_ref.7e5 = import_ref Main//a, loc13_20, unloaded
 // CHECK:STDOUT:   %Main.import_ref.7e5 = import_ref Main//a, loc13_20, unloaded
 // CHECK:STDOUT:   %Main.import_ref.a21640.2: type = import_ref Main//a, loc13_16, loaded [concrete = constants.%Base]
 // CHECK:STDOUT:   %Main.import_ref.a21640.2: type = import_ref Main//a, loc13_16, loaded [concrete = constants.%Base]
 // CHECK:STDOUT:   %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/operators/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic]
 // CHECK:STDOUT:   %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/operators/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic]

+ 1 - 1
toolchain/check/testdata/function/builtin/no_prelude/call_from_operator.carbon

@@ -596,9 +596,9 @@ var arr: array(i32, (1 as i32) + (2 as i32)) = (3, 4, (3 as i32) + (4 as i32));
 // CHECK:STDOUT:   %Self.65a: %As.type.eed = bind_symbolic_name Self, 1 [symbolic]
 // CHECK:STDOUT:   %Self.65a: %As.type.eed = bind_symbolic_name Self, 1 [symbolic]
 // CHECK:STDOUT:   %Convert.type.843: type = fn_type @Convert.1, @As(%T) [symbolic]
 // CHECK:STDOUT:   %Convert.type.843: type = fn_type @Convert.1, @As(%T) [symbolic]
 // CHECK:STDOUT:   %Convert.95f: %Convert.type.843 = struct_value () [symbolic]
 // CHECK:STDOUT:   %Convert.95f: %Convert.type.843 = struct_value () [symbolic]
+// CHECK:STDOUT:   %pattern_type.7dc: type = pattern_type %T [symbolic]
 // CHECK:STDOUT:   %Self.as_type.04d: type = facet_access_type %Self.65a [symbolic]
 // CHECK:STDOUT:   %Self.as_type.04d: type = facet_access_type %Self.65a [symbolic]
 // CHECK:STDOUT:   %pattern_type.ca4: type = pattern_type %Self.as_type.04d [symbolic]
 // CHECK:STDOUT:   %pattern_type.ca4: type = pattern_type %Self.as_type.04d [symbolic]
-// CHECK:STDOUT:   %pattern_type.7dc: type = pattern_type %T [symbolic]
 // CHECK:STDOUT:   %As.assoc_type.760: type = assoc_entity_type @As, @As(%T) [symbolic]
 // CHECK:STDOUT:   %As.assoc_type.760: type = assoc_entity_type @As, @As(%T) [symbolic]
 // CHECK:STDOUT:   %assoc0.076: %As.assoc_type.760 = assoc_entity element0, imports.%Core.import_ref.708 [symbolic]
 // CHECK:STDOUT:   %assoc0.076: %As.assoc_type.760 = assoc_entity element0, imports.%Core.import_ref.708 [symbolic]
 // CHECK:STDOUT:   %As.type.a6d: type = facet_type <@As, @As(%i32.builtin)> [concrete]
 // CHECK:STDOUT:   %As.type.a6d: type = facet_type <@As, @As(%i32.builtin)> [concrete]

+ 1 - 1
toolchain/check/testdata/function/declaration/import.carbon

@@ -865,9 +865,9 @@ import library "extern_api";
 // CHECK:STDOUT:   %pattern_type.501: type = pattern_type %i32 [concrete]
 // CHECK:STDOUT:   %pattern_type.501: type = pattern_type %i32 [concrete]
 // CHECK:STDOUT:   %C.type: type = fn_type @C [concrete]
 // CHECK:STDOUT:   %C.type: type = fn_type @C [concrete]
 // CHECK:STDOUT:   %C: %C.type = struct_value () [concrete]
 // CHECK:STDOUT:   %C: %C.type = struct_value () [concrete]
-// CHECK:STDOUT:   %tuple.type.dd4: type = tuple_type (%i32) [concrete]
 // CHECK:STDOUT:   %struct_type.c: type = struct_type {.c: %i32} [concrete]
 // CHECK:STDOUT:   %struct_type.c: type = struct_type {.c: %i32} [concrete]
 // CHECK:STDOUT:   %pattern_type.f5f: type = pattern_type %struct_type.c [concrete]
 // CHECK:STDOUT:   %pattern_type.f5f: type = pattern_type %struct_type.c [concrete]
+// CHECK:STDOUT:   %tuple.type.dd4: type = tuple_type (%i32) [concrete]
 // CHECK:STDOUT:   %D.type: type = fn_type @D [concrete]
 // CHECK:STDOUT:   %D.type: type = fn_type @D [concrete]
 // CHECK:STDOUT:   %D: %D.type = struct_value () [concrete]
 // CHECK:STDOUT:   %D: %D.type = struct_value () [concrete]
 // CHECK:STDOUT:   %E.type: type = fn_type @E [concrete]
 // CHECK:STDOUT:   %E.type: type = fn_type @E [concrete]

+ 2 - 2
toolchain/check/testdata/impl/no_prelude/compound.carbon

@@ -318,9 +318,9 @@ fn InstanceCallFail() {
 // CHECK:STDOUT:   %Self.519: %ImplicitAs.type.d62 = bind_symbolic_name Self, 1 [symbolic]
 // CHECK:STDOUT:   %Self.519: %ImplicitAs.type.d62 = bind_symbolic_name Self, 1 [symbolic]
 // CHECK:STDOUT:   %Convert.type.275: type = fn_type @Convert, @ImplicitAs(%Dest) [symbolic]
 // CHECK:STDOUT:   %Convert.type.275: type = fn_type @Convert, @ImplicitAs(%Dest) [symbolic]
 // CHECK:STDOUT:   %Convert.42e: %Convert.type.275 = struct_value () [symbolic]
 // CHECK:STDOUT:   %Convert.42e: %Convert.type.275 = struct_value () [symbolic]
+// CHECK:STDOUT:   %pattern_type.7dc: type = pattern_type %Dest [symbolic]
 // CHECK:STDOUT:   %Self.as_type: type = facet_access_type %Self.519 [symbolic]
 // CHECK:STDOUT:   %Self.as_type: type = facet_access_type %Self.519 [symbolic]
 // CHECK:STDOUT:   %pattern_type.f3e: type = pattern_type %Self.as_type [symbolic]
 // CHECK:STDOUT:   %pattern_type.f3e: type = pattern_type %Self.as_type [symbolic]
-// CHECK:STDOUT:   %pattern_type.7dc: type = pattern_type %Dest [symbolic]
 // CHECK:STDOUT:   %ImplicitAs.assoc_type.ca0: type = assoc_entity_type @ImplicitAs, @ImplicitAs(%Dest) [symbolic]
 // CHECK:STDOUT:   %ImplicitAs.assoc_type.ca0: type = assoc_entity_type @ImplicitAs, @ImplicitAs(%Dest) [symbolic]
 // CHECK:STDOUT:   %assoc0.9f5: %ImplicitAs.assoc_type.ca0 = assoc_entity element0, imports.%Core.import_ref.1c7 [symbolic]
 // CHECK:STDOUT:   %assoc0.9f5: %ImplicitAs.assoc_type.ca0 = assoc_entity element0, imports.%Core.import_ref.1c7 [symbolic]
 // CHECK:STDOUT:   %ImplicitAs.type.c38: type = facet_type <@ImplicitAs, @ImplicitAs(%NonInstance2.type)> [concrete]
 // CHECK:STDOUT:   %ImplicitAs.type.c38: type = facet_type <@ImplicitAs, @ImplicitAs(%NonInstance2.type)> [concrete]
@@ -500,9 +500,9 @@ fn InstanceCallFail() {
 // CHECK:STDOUT:   %Self.519: %ImplicitAs.type.d62 = bind_symbolic_name Self, 1 [symbolic]
 // CHECK:STDOUT:   %Self.519: %ImplicitAs.type.d62 = bind_symbolic_name Self, 1 [symbolic]
 // CHECK:STDOUT:   %Convert.type.275: type = fn_type @Convert, @ImplicitAs(%Dest) [symbolic]
 // CHECK:STDOUT:   %Convert.type.275: type = fn_type @Convert, @ImplicitAs(%Dest) [symbolic]
 // CHECK:STDOUT:   %Convert.42e: %Convert.type.275 = struct_value () [symbolic]
 // CHECK:STDOUT:   %Convert.42e: %Convert.type.275 = struct_value () [symbolic]
+// CHECK:STDOUT:   %pattern_type.7dc: type = pattern_type %Dest [symbolic]
 // CHECK:STDOUT:   %Self.as_type: type = facet_access_type %Self.519 [symbolic]
 // CHECK:STDOUT:   %Self.as_type: type = facet_access_type %Self.519 [symbolic]
 // CHECK:STDOUT:   %pattern_type.f3e: type = pattern_type %Self.as_type [symbolic]
 // CHECK:STDOUT:   %pattern_type.f3e: type = pattern_type %Self.as_type [symbolic]
-// CHECK:STDOUT:   %pattern_type.7dc: type = pattern_type %Dest [symbolic]
 // CHECK:STDOUT:   %ImplicitAs.assoc_type.ca0: type = assoc_entity_type @ImplicitAs, @ImplicitAs(%Dest) [symbolic]
 // CHECK:STDOUT:   %ImplicitAs.assoc_type.ca0: type = assoc_entity_type @ImplicitAs, @ImplicitAs(%Dest) [symbolic]
 // CHECK:STDOUT:   %assoc0.9f5: %ImplicitAs.assoc_type.ca0 = assoc_entity element0, imports.%Core.import_ref.1c7 [symbolic]
 // CHECK:STDOUT:   %assoc0.9f5: %ImplicitAs.assoc_type.ca0 = assoc_entity element0, imports.%Core.import_ref.1c7 [symbolic]
 // CHECK:STDOUT:   %ImplicitAs.type.c69: type = facet_type <@ImplicitAs, @ImplicitAs(%NonInstance3.type)> [concrete]
 // CHECK:STDOUT:   %ImplicitAs.type.c69: type = facet_type <@ImplicitAs, @ImplicitAs(%NonInstance3.type)> [concrete]

+ 2 - 2
toolchain/check/testdata/impl/no_prelude/import_compound.carbon

@@ -466,9 +466,9 @@ fn InstanceCallImportFail() {
 // CHECK:STDOUT:   %Self.519: %ImplicitAs.type.d62 = bind_symbolic_name Self, 1 [symbolic]
 // CHECK:STDOUT:   %Self.519: %ImplicitAs.type.d62 = bind_symbolic_name Self, 1 [symbolic]
 // CHECK:STDOUT:   %Convert.type.275: type = fn_type @Convert, @ImplicitAs(%Dest) [symbolic]
 // CHECK:STDOUT:   %Convert.type.275: type = fn_type @Convert, @ImplicitAs(%Dest) [symbolic]
 // CHECK:STDOUT:   %Convert.42e: %Convert.type.275 = struct_value () [symbolic]
 // CHECK:STDOUT:   %Convert.42e: %Convert.type.275 = struct_value () [symbolic]
+// CHECK:STDOUT:   %pattern_type.7dc: type = pattern_type %Dest [symbolic]
 // CHECK:STDOUT:   %Self.as_type: type = facet_access_type %Self.519 [symbolic]
 // CHECK:STDOUT:   %Self.as_type: type = facet_access_type %Self.519 [symbolic]
 // CHECK:STDOUT:   %pattern_type.f3e: type = pattern_type %Self.as_type [symbolic]
 // CHECK:STDOUT:   %pattern_type.f3e: type = pattern_type %Self.as_type [symbolic]
-// CHECK:STDOUT:   %pattern_type.7dc: type = pattern_type %Dest [symbolic]
 // CHECK:STDOUT:   %ImplicitAs.assoc_type.ca0: type = assoc_entity_type @ImplicitAs, @ImplicitAs(%Dest) [symbolic]
 // CHECK:STDOUT:   %ImplicitAs.assoc_type.ca0: type = assoc_entity_type @ImplicitAs, @ImplicitAs(%Dest) [symbolic]
 // CHECK:STDOUT:   %assoc0.9f5: %ImplicitAs.assoc_type.ca0 = assoc_entity element0, imports.%Core.import_ref.1c7 [symbolic]
 // CHECK:STDOUT:   %assoc0.9f5: %ImplicitAs.assoc_type.ca0 = assoc_entity element0, imports.%Core.import_ref.1c7 [symbolic]
 // CHECK:STDOUT:   %ImplicitAs.type.a4a: type = facet_type <@ImplicitAs, @ImplicitAs(%NonInstance.type)> [concrete]
 // CHECK:STDOUT:   %ImplicitAs.type.a4a: type = facet_type <@ImplicitAs, @ImplicitAs(%NonInstance.type)> [concrete]
@@ -652,9 +652,9 @@ fn InstanceCallImportFail() {
 // CHECK:STDOUT:   %Self.519: %ImplicitAs.type.d62 = bind_symbolic_name Self, 1 [symbolic]
 // CHECK:STDOUT:   %Self.519: %ImplicitAs.type.d62 = bind_symbolic_name Self, 1 [symbolic]
 // CHECK:STDOUT:   %Convert.type.275: type = fn_type @Convert, @ImplicitAs(%Dest) [symbolic]
 // CHECK:STDOUT:   %Convert.type.275: type = fn_type @Convert, @ImplicitAs(%Dest) [symbolic]
 // CHECK:STDOUT:   %Convert.42e: %Convert.type.275 = struct_value () [symbolic]
 // CHECK:STDOUT:   %Convert.42e: %Convert.type.275 = struct_value () [symbolic]
+// CHECK:STDOUT:   %pattern_type.7dc: type = pattern_type %Dest [symbolic]
 // CHECK:STDOUT:   %Self.as_type: type = facet_access_type %Self.519 [symbolic]
 // CHECK:STDOUT:   %Self.as_type: type = facet_access_type %Self.519 [symbolic]
 // CHECK:STDOUT:   %pattern_type.f3e: type = pattern_type %Self.as_type [symbolic]
 // CHECK:STDOUT:   %pattern_type.f3e: type = pattern_type %Self.as_type [symbolic]
-// CHECK:STDOUT:   %pattern_type.7dc: type = pattern_type %Dest [symbolic]
 // CHECK:STDOUT:   %ImplicitAs.assoc_type.ca0: type = assoc_entity_type @ImplicitAs, @ImplicitAs(%Dest) [symbolic]
 // CHECK:STDOUT:   %ImplicitAs.assoc_type.ca0: type = assoc_entity_type @ImplicitAs, @ImplicitAs(%Dest) [symbolic]
 // CHECK:STDOUT:   %assoc0.9f5: %ImplicitAs.assoc_type.ca0 = assoc_entity element0, imports.%Core.import_ref.1c7 [symbolic]
 // CHECK:STDOUT:   %assoc0.9f5: %ImplicitAs.assoc_type.ca0 = assoc_entity element0, imports.%Core.import_ref.1c7 [symbolic]
 // CHECK:STDOUT:   %ImplicitAs.type.a4a: type = facet_type <@ImplicitAs, @ImplicitAs(%NonInstance.type)> [concrete]
 // CHECK:STDOUT:   %ImplicitAs.type.a4a: type = facet_type <@ImplicitAs, @ImplicitAs(%NonInstance.type)> [concrete]

+ 1 - 1
toolchain/check/testdata/interface/min_prelude/fail_member_lookup.carbon

@@ -166,9 +166,9 @@ fn G(U:! Different) -> U.(Interface.T);
 // CHECK:STDOUT:   %Self.519: %ImplicitAs.type.d62 = bind_symbolic_name Self, 1 [symbolic]
 // CHECK:STDOUT:   %Self.519: %ImplicitAs.type.d62 = bind_symbolic_name Self, 1 [symbolic]
 // CHECK:STDOUT:   %Convert.type.275: type = fn_type @Convert, @ImplicitAs(%Dest) [symbolic]
 // CHECK:STDOUT:   %Convert.type.275: type = fn_type @Convert, @ImplicitAs(%Dest) [symbolic]
 // CHECK:STDOUT:   %Convert.42e: %Convert.type.275 = struct_value () [symbolic]
 // CHECK:STDOUT:   %Convert.42e: %Convert.type.275 = struct_value () [symbolic]
+// CHECK:STDOUT:   %pattern_type.7dc: type = pattern_type %Dest [symbolic]
 // CHECK:STDOUT:   %Self.as_type: type = facet_access_type %Self.519 [symbolic]
 // CHECK:STDOUT:   %Self.as_type: type = facet_access_type %Self.519 [symbolic]
 // CHECK:STDOUT:   %pattern_type.f3e: type = pattern_type %Self.as_type [symbolic]
 // CHECK:STDOUT:   %pattern_type.f3e: type = pattern_type %Self.as_type [symbolic]
-// CHECK:STDOUT:   %pattern_type.7dc: type = pattern_type %Dest [symbolic]
 // CHECK:STDOUT:   %ImplicitAs.assoc_type.ca0: type = assoc_entity_type @ImplicitAs, @ImplicitAs(%Dest) [symbolic]
 // CHECK:STDOUT:   %ImplicitAs.assoc_type.ca0: type = assoc_entity_type @ImplicitAs, @ImplicitAs(%Dest) [symbolic]
 // CHECK:STDOUT:   %assoc0.9f5: %ImplicitAs.assoc_type.ca0 = assoc_entity element0, imports.%Core.import_ref.1c7 [symbolic]
 // CHECK:STDOUT:   %assoc0.9f5: %ImplicitAs.assoc_type.ca0 = assoc_entity element0, imports.%Core.import_ref.1c7 [symbolic]
 // CHECK:STDOUT:   %ImplicitAs.type.dc8: type = facet_type <@ImplicitAs, @ImplicitAs(type)> [concrete]
 // CHECK:STDOUT:   %ImplicitAs.type.dc8: type = facet_type <@ImplicitAs, @ImplicitAs(type)> [concrete]

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

@@ -46,7 +46,7 @@ fn F() {
 // CHECK:STDOUT: file {
 // CHECK:STDOUT: file {
 // CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
 // CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
 // CHECK:STDOUT:     .Core = imports.%Core
 // CHECK:STDOUT:     .Core = imports.%Core
-// CHECK:STDOUT:     .n = <unexpected>.inst45.loc15_5
+// CHECK:STDOUT:     .n = <unexpected>.inst44.loc15_5
 // CHECK:STDOUT:     .F = %F.decl
 // CHECK:STDOUT:     .F = %F.decl
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Core.import = import Core
 // CHECK:STDOUT:   %Core.import = import Core

+ 2 - 2
toolchain/check/testdata/operators/overloaded/no_prelude/index.carbon

@@ -267,10 +267,10 @@ fn F() { ()[()]; }
 // CHECK:STDOUT:   %Self.30a: %IndexWith.type.bd2 = bind_symbolic_name Self, 1 [symbolic]
 // CHECK:STDOUT:   %Self.30a: %IndexWith.type.bd2 = bind_symbolic_name Self, 1 [symbolic]
 // CHECK:STDOUT:   %At.type.cf4: type = fn_type @At.1, @IndexWith(%SubscriptType) [symbolic]
 // CHECK:STDOUT:   %At.type.cf4: type = fn_type @At.1, @IndexWith(%SubscriptType) [symbolic]
 // CHECK:STDOUT:   %At.281: %At.type.cf4 = struct_value () [symbolic]
 // CHECK:STDOUT:   %At.281: %At.type.cf4 = struct_value () [symbolic]
-// CHECK:STDOUT:   %pattern_type.7dc: type = pattern_type %SubscriptType [symbolic]
+// CHECK:STDOUT:   %pattern_type.cb1: type = pattern_type %empty_tuple.type [concrete]
 // CHECK:STDOUT:   %Self.as_type: type = facet_access_type %Self.30a [symbolic]
 // CHECK:STDOUT:   %Self.as_type: type = facet_access_type %Self.30a [symbolic]
 // CHECK:STDOUT:   %pattern_type.e1a: type = pattern_type %Self.as_type [symbolic]
 // CHECK:STDOUT:   %pattern_type.e1a: type = pattern_type %Self.as_type [symbolic]
-// CHECK:STDOUT:   %pattern_type.cb1: type = pattern_type %empty_tuple.type [concrete]
+// CHECK:STDOUT:   %pattern_type.7dc: type = pattern_type %SubscriptType [symbolic]
 // CHECK:STDOUT:   %IndexWith.assoc_type.290: type = assoc_entity_type @IndexWith, @IndexWith(%SubscriptType) [symbolic]
 // CHECK:STDOUT:   %IndexWith.assoc_type.290: type = assoc_entity_type @IndexWith, @IndexWith(%SubscriptType) [symbolic]
 // CHECK:STDOUT:   %assoc0.af8: %IndexWith.assoc_type.290 = assoc_entity element0, imports.%Core.import_ref.e99 [symbolic]
 // CHECK:STDOUT:   %assoc0.af8: %IndexWith.assoc_type.290 = assoc_entity element0, imports.%Core.import_ref.e99 [symbolic]
 // CHECK:STDOUT:   %IndexWith.type.a51: type = facet_type <@IndexWith, @IndexWith(%empty_tuple.type)> [concrete]
 // CHECK:STDOUT:   %IndexWith.type.a51: type = facet_type <@IndexWith, @IndexWith(%empty_tuple.type)> [concrete]

+ 2 - 2
toolchain/check/testdata/return/min_prelude/import_convert_function.carbon

@@ -296,9 +296,9 @@ fn F0(n: i32) -> P.D {
 // CHECK:STDOUT:   %Self.519: %ImplicitAs.type.d62 = bind_symbolic_name Self, 1 [symbolic]
 // CHECK:STDOUT:   %Self.519: %ImplicitAs.type.d62 = bind_symbolic_name Self, 1 [symbolic]
 // CHECK:STDOUT:   %Convert.type.275: type = fn_type @Convert.1, @ImplicitAs(%T) [symbolic]
 // CHECK:STDOUT:   %Convert.type.275: type = fn_type @Convert.1, @ImplicitAs(%T) [symbolic]
 // CHECK:STDOUT:   %Convert.42e: %Convert.type.275 = struct_value () [symbolic]
 // CHECK:STDOUT:   %Convert.42e: %Convert.type.275 = struct_value () [symbolic]
+// CHECK:STDOUT:   %pattern_type.7dc: type = pattern_type %T [symbolic]
 // CHECK:STDOUT:   %Self.as_type: type = facet_access_type %Self.519 [symbolic]
 // CHECK:STDOUT:   %Self.as_type: type = facet_access_type %Self.519 [symbolic]
 // CHECK:STDOUT:   %pattern_type.f3e: type = pattern_type %Self.as_type [symbolic]
 // CHECK:STDOUT:   %pattern_type.f3e: type = pattern_type %Self.as_type [symbolic]
-// CHECK:STDOUT:   %pattern_type.7dc: type = pattern_type %T [symbolic]
 // CHECK:STDOUT:   %ImplicitAs.assoc_type.ca0: type = assoc_entity_type @ImplicitAs, @ImplicitAs(%T) [symbolic]
 // CHECK:STDOUT:   %ImplicitAs.assoc_type.ca0: type = assoc_entity_type @ImplicitAs, @ImplicitAs(%T) [symbolic]
 // CHECK:STDOUT:   %assoc0.9f5: %ImplicitAs.assoc_type.ca0 = assoc_entity element0, imports.%Core.import_ref.1c7 [symbolic]
 // CHECK:STDOUT:   %assoc0.9f5: %ImplicitAs.assoc_type.ca0 = assoc_entity element0, imports.%Core.import_ref.1c7 [symbolic]
 // CHECK:STDOUT:   %ImplicitAs.type.61e: type = facet_type <@ImplicitAs, @ImplicitAs(%i32.builtin)> [concrete]
 // CHECK:STDOUT:   %ImplicitAs.type.61e: type = facet_type <@ImplicitAs, @ImplicitAs(%i32.builtin)> [concrete]
@@ -1101,9 +1101,9 @@ fn F0(n: i32) -> P.D {
 // CHECK:STDOUT:   %Self.519: %ImplicitAs.type.d62 = bind_symbolic_name Self, 1 [symbolic]
 // CHECK:STDOUT:   %Self.519: %ImplicitAs.type.d62 = bind_symbolic_name Self, 1 [symbolic]
 // CHECK:STDOUT:   %Convert.type.275: type = fn_type @Convert.1, @ImplicitAs(%T) [symbolic]
 // CHECK:STDOUT:   %Convert.type.275: type = fn_type @Convert.1, @ImplicitAs(%T) [symbolic]
 // CHECK:STDOUT:   %Convert.42e: %Convert.type.275 = struct_value () [symbolic]
 // CHECK:STDOUT:   %Convert.42e: %Convert.type.275 = struct_value () [symbolic]
+// CHECK:STDOUT:   %pattern_type.7dc: type = pattern_type %T [symbolic]
 // CHECK:STDOUT:   %Self.as_type: type = facet_access_type %Self.519 [symbolic]
 // CHECK:STDOUT:   %Self.as_type: type = facet_access_type %Self.519 [symbolic]
 // CHECK:STDOUT:   %pattern_type.f3e: type = pattern_type %Self.as_type [symbolic]
 // CHECK:STDOUT:   %pattern_type.f3e: type = pattern_type %Self.as_type [symbolic]
-// CHECK:STDOUT:   %pattern_type.7dc: type = pattern_type %T [symbolic]
 // CHECK:STDOUT:   %ImplicitAs.assoc_type.ca0: type = assoc_entity_type @ImplicitAs, @ImplicitAs(%T) [symbolic]
 // CHECK:STDOUT:   %ImplicitAs.assoc_type.ca0: type = assoc_entity_type @ImplicitAs, @ImplicitAs(%T) [symbolic]
 // CHECK:STDOUT:   %assoc0.9f5: %ImplicitAs.assoc_type.ca0 = assoc_entity element0, imports.%Core.import_ref.1c7 [symbolic]
 // CHECK:STDOUT:   %assoc0.9f5: %ImplicitAs.assoc_type.ca0 = assoc_entity element0, imports.%Core.import_ref.1c7 [symbolic]
 // CHECK:STDOUT:   %ImplicitAs.type.61e: type = facet_type <@ImplicitAs, @ImplicitAs(%i32.builtin)> [concrete]
 // CHECK:STDOUT:   %ImplicitAs.type.61e: type = facet_type <@ImplicitAs, @ImplicitAs(%i32.builtin)> [concrete]

+ 5 - 5
toolchain/check/testdata/struct/import.carbon

@@ -347,12 +347,12 @@ var c_bad: C({.a = 3, .b = 4}) = F();
 // CHECK:STDOUT:     import Core//prelude
 // CHECK:STDOUT:     import Core//prelude
 // CHECK:STDOUT:     import Core//prelude/...
 // CHECK:STDOUT:     import Core//prelude/...
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %Implicit.import_ref.773: @impl.a8d.%Convert.type (%Convert.type.9a6) = import_ref Implicit//default, inst119 [indirect], loaded [symbolic = @impl.a8d.%Convert (constants.%Convert.458)]
+// CHECK:STDOUT:   %Implicit.import_ref.773: @impl.a8d.%Convert.type (%Convert.type.9a6) = import_ref Implicit//default, inst116 [indirect], loaded [symbolic = @impl.a8d.%Convert (constants.%Convert.458)]
 // CHECK:STDOUT:   %ImplicitAs.impl_witness_table.1ad = impl_witness_table (%Implicit.import_ref.773), @impl.a8d [concrete]
 // CHECK:STDOUT:   %ImplicitAs.impl_witness_table.1ad = impl_witness_table (%Implicit.import_ref.773), @impl.a8d [concrete]
 // CHECK:STDOUT:   %Core.Int: %Int.type = import_ref Core//prelude/types/int, Int, loaded [concrete = constants.%Int.generic]
 // CHECK:STDOUT:   %Core.Int: %Int.type = import_ref Core//prelude/types/int, Int, loaded [concrete = constants.%Int.generic]
 // CHECK:STDOUT:   %Implicit.import_ref.c81: %struct_type.a.b.5ca = import_ref Implicit//default, loc8_9, loaded [symbolic = @C.%S (constants.%S)]
 // CHECK:STDOUT:   %Implicit.import_ref.c81: %struct_type.a.b.5ca = import_ref Implicit//default, loc8_9, loaded [symbolic = @C.%S (constants.%S)]
 // CHECK:STDOUT:   %Implicit.import_ref.8f2: <witness> = import_ref Implicit//default, loc8_34, loaded [concrete = constants.%complete_type.357]
 // CHECK:STDOUT:   %Implicit.import_ref.8f2: <witness> = import_ref Implicit//default, loc8_34, loaded [concrete = constants.%complete_type.357]
-// CHECK:STDOUT:   %Implicit.import_ref.b8b = import_ref Implicit//default, inst1423 [no loc], unloaded
+// CHECK:STDOUT:   %Implicit.import_ref.b8b = import_ref Implicit//default, inst1418 [no loc], unloaded
 // CHECK:STDOUT:   %Core.ImplicitAs: %ImplicitAs.type.595 = import_ref Core//prelude/operators/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic]
 // CHECK:STDOUT:   %Core.ImplicitAs: %ImplicitAs.type.595 = import_ref Core//prelude/operators/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic]
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
@@ -526,7 +526,7 @@ var c_bad: C({.a = 3, .b = 4}) = F();
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Implicit.import_ref.c81: %struct_type.a.b = import_ref Implicit//default, loc8_9, loaded [symbolic = @C.%S (constants.%S)]
 // CHECK:STDOUT:   %Implicit.import_ref.c81: %struct_type.a.b = import_ref Implicit//default, loc8_9, loaded [symbolic = @C.%S (constants.%S)]
 // CHECK:STDOUT:   %Implicit.import_ref.8f2: <witness> = import_ref Implicit//default, loc8_34, loaded [concrete = constants.%complete_type.357]
 // CHECK:STDOUT:   %Implicit.import_ref.8f2: <witness> = import_ref Implicit//default, loc8_34, loaded [concrete = constants.%complete_type.357]
-// CHECK:STDOUT:   %Implicit.import_ref.b8b = import_ref Implicit//default, inst1423 [no loc], unloaded
+// CHECK:STDOUT:   %Implicit.import_ref.b8b = import_ref Implicit//default, inst1418 [no loc], unloaded
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
 // CHECK:STDOUT: file {
@@ -643,11 +643,11 @@ var c_bad: C({.a = 3, .b = 4}) = F();
 // CHECK:STDOUT:     import Core//prelude
 // CHECK:STDOUT:     import Core//prelude
 // CHECK:STDOUT:     import Core//prelude/...
 // CHECK:STDOUT:     import Core//prelude/...
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %Implicit.import_ref.773: @impl.a8d.%Convert.type (%Convert.type.9a6) = import_ref Implicit//default, inst119 [indirect], loaded [symbolic = @impl.a8d.%Convert (constants.%Convert.458)]
+// CHECK:STDOUT:   %Implicit.import_ref.773: @impl.a8d.%Convert.type (%Convert.type.9a6) = import_ref Implicit//default, inst116 [indirect], loaded [symbolic = @impl.a8d.%Convert (constants.%Convert.458)]
 // CHECK:STDOUT:   %ImplicitAs.impl_witness_table.1ad = impl_witness_table (%Implicit.import_ref.773), @impl.a8d [concrete]
 // CHECK:STDOUT:   %ImplicitAs.impl_witness_table.1ad = impl_witness_table (%Implicit.import_ref.773), @impl.a8d [concrete]
 // CHECK:STDOUT:   %Implicit.import_ref.c81: %struct_type.a.b.5ca = import_ref Implicit//default, loc8_9, loaded [symbolic = @C.%S (constants.%S)]
 // CHECK:STDOUT:   %Implicit.import_ref.c81: %struct_type.a.b.5ca = import_ref Implicit//default, loc8_9, loaded [symbolic = @C.%S (constants.%S)]
 // CHECK:STDOUT:   %Implicit.import_ref.8f2: <witness> = import_ref Implicit//default, loc8_34, loaded [concrete = constants.%complete_type.357]
 // CHECK:STDOUT:   %Implicit.import_ref.8f2: <witness> = import_ref Implicit//default, loc8_34, loaded [concrete = constants.%complete_type.357]
-// CHECK:STDOUT:   %Implicit.import_ref.b8b = import_ref Implicit//default, inst1423 [no loc], unloaded
+// CHECK:STDOUT:   %Implicit.import_ref.b8b = import_ref Implicit//default, inst1418 [no loc], unloaded
 // CHECK:STDOUT:   %Core.ImplicitAs: %ImplicitAs.type.595 = import_ref Core//prelude/operators/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic]
 // CHECK:STDOUT:   %Core.ImplicitAs: %ImplicitAs.type.595 = import_ref Core//prelude/operators/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic]
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:

+ 5 - 5
toolchain/check/testdata/tuple/import.carbon

@@ -377,12 +377,12 @@ var c_bad: C((3, 4)) = F();
 // CHECK:STDOUT:     import Core//prelude
 // CHECK:STDOUT:     import Core//prelude
 // CHECK:STDOUT:     import Core//prelude/...
 // CHECK:STDOUT:     import Core//prelude/...
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %Implicit.import_ref.773: @impl.a8d.%Convert.type (%Convert.type.9a6) = import_ref Implicit//default, inst121 [indirect], loaded [symbolic = @impl.a8d.%Convert (constants.%Convert.458)]
+// CHECK:STDOUT:   %Implicit.import_ref.773: @impl.a8d.%Convert.type (%Convert.type.9a6) = import_ref Implicit//default, inst118 [indirect], loaded [symbolic = @impl.a8d.%Convert (constants.%Convert.458)]
 // CHECK:STDOUT:   %ImplicitAs.impl_witness_table.1ad = impl_witness_table (%Implicit.import_ref.773), @impl.a8d [concrete]
 // CHECK:STDOUT:   %ImplicitAs.impl_witness_table.1ad = impl_witness_table (%Implicit.import_ref.773), @impl.a8d [concrete]
 // CHECK:STDOUT:   %Core.Int: %Int.type = import_ref Core//prelude/types/int, Int, loaded [concrete = constants.%Int.generic]
 // CHECK:STDOUT:   %Core.Int: %Int.type = import_ref Core//prelude/types/int, Int, loaded [concrete = constants.%Int.generic]
 // CHECK:STDOUT:   %Implicit.import_ref.48d: %tuple.type.c2c = import_ref Implicit//default, loc7_9, loaded [symbolic = @C.%X (constants.%X)]
 // CHECK:STDOUT:   %Implicit.import_ref.48d: %tuple.type.c2c = import_ref Implicit//default, loc7_9, loaded [symbolic = @C.%X (constants.%X)]
 // CHECK:STDOUT:   %Implicit.import_ref.8f2: <witness> = import_ref Implicit//default, loc7_26, loaded [concrete = constants.%complete_type.357]
 // CHECK:STDOUT:   %Implicit.import_ref.8f2: <witness> = import_ref Implicit//default, loc7_26, loaded [concrete = constants.%complete_type.357]
-// CHECK:STDOUT:   %Implicit.import_ref.964 = import_ref Implicit//default, inst1460 [no loc], unloaded
+// CHECK:STDOUT:   %Implicit.import_ref.964 = import_ref Implicit//default, inst1455 [no loc], unloaded
 // CHECK:STDOUT:   %Core.ImplicitAs: %ImplicitAs.type.595 = import_ref Core//prelude/operators/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic]
 // CHECK:STDOUT:   %Core.ImplicitAs: %ImplicitAs.type.595 = import_ref Core//prelude/operators/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic]
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
@@ -572,7 +572,7 @@ var c_bad: C((3, 4)) = F();
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Implicit.import_ref.48d: %tuple.type.c2c = import_ref Implicit//default, loc7_9, loaded [symbolic = @C.%X (constants.%X)]
 // CHECK:STDOUT:   %Implicit.import_ref.48d: %tuple.type.c2c = import_ref Implicit//default, loc7_9, loaded [symbolic = @C.%X (constants.%X)]
 // CHECK:STDOUT:   %Implicit.import_ref.8f2: <witness> = import_ref Implicit//default, loc7_26, loaded [concrete = constants.%complete_type.357]
 // CHECK:STDOUT:   %Implicit.import_ref.8f2: <witness> = import_ref Implicit//default, loc7_26, loaded [concrete = constants.%complete_type.357]
-// CHECK:STDOUT:   %Implicit.import_ref.964 = import_ref Implicit//default, inst1460 [no loc], unloaded
+// CHECK:STDOUT:   %Implicit.import_ref.964 = import_ref Implicit//default, inst1455 [no loc], unloaded
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
 // CHECK:STDOUT: file {
@@ -690,11 +690,11 @@ var c_bad: C((3, 4)) = F();
 // CHECK:STDOUT:     import Core//prelude
 // CHECK:STDOUT:     import Core//prelude
 // CHECK:STDOUT:     import Core//prelude/...
 // CHECK:STDOUT:     import Core//prelude/...
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %Implicit.import_ref.773: @impl.a8d.%Convert.type (%Convert.type.9a6) = import_ref Implicit//default, inst121 [indirect], loaded [symbolic = @impl.a8d.%Convert (constants.%Convert.458)]
+// CHECK:STDOUT:   %Implicit.import_ref.773: @impl.a8d.%Convert.type (%Convert.type.9a6) = import_ref Implicit//default, inst118 [indirect], loaded [symbolic = @impl.a8d.%Convert (constants.%Convert.458)]
 // CHECK:STDOUT:   %ImplicitAs.impl_witness_table.1ad = impl_witness_table (%Implicit.import_ref.773), @impl.a8d [concrete]
 // CHECK:STDOUT:   %ImplicitAs.impl_witness_table.1ad = impl_witness_table (%Implicit.import_ref.773), @impl.a8d [concrete]
 // CHECK:STDOUT:   %Implicit.import_ref.48d: %tuple.type.c2c = import_ref Implicit//default, loc7_9, loaded [symbolic = @C.%X (constants.%X)]
 // CHECK:STDOUT:   %Implicit.import_ref.48d: %tuple.type.c2c = import_ref Implicit//default, loc7_9, loaded [symbolic = @C.%X (constants.%X)]
 // CHECK:STDOUT:   %Implicit.import_ref.8f2: <witness> = import_ref Implicit//default, loc7_26, loaded [concrete = constants.%complete_type.357]
 // CHECK:STDOUT:   %Implicit.import_ref.8f2: <witness> = import_ref Implicit//default, loc7_26, loaded [concrete = constants.%complete_type.357]
-// CHECK:STDOUT:   %Implicit.import_ref.964 = import_ref Implicit//default, inst1460 [no loc], unloaded
+// CHECK:STDOUT:   %Implicit.import_ref.964 = import_ref Implicit//default, inst1455 [no loc], unloaded
 // CHECK:STDOUT:   %Core.ImplicitAs: %ImplicitAs.type.595 = import_ref Core//prelude/operators/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic]
 // CHECK:STDOUT:   %Core.ImplicitAs: %ImplicitAs.type.595 = import_ref Core//prelude/operators/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic]
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:

+ 259 - 0
toolchain/check/testdata/var/min_prelude/var_pattern.carbon

@@ -37,6 +37,28 @@ fn G() {
   F(v, ());
   F(v, ());
 }
 }
 
 
+// --- public_function.carbon
+
+package P;
+
+fn F(x: (), var y: ());
+
+// --- public_function.impl.carbon
+
+impl package P;
+
+fn F(x: (), var y: ()) {}
+
+// --- use_public_function.carbon
+
+library "[[@TEST_NAME]]";
+
+import P;
+
+fn G() {
+  P.F((), ());
+}
+
 // --- fail_nested.carbon
 // --- fail_nested.carbon
 
 
 library "[[@TEST_NAME]]";
 library "[[@TEST_NAME]]";
@@ -94,6 +116,17 @@ class C {
   fn F[var self: Self]();
   fn F[var self: Self]();
 }
 }
 
 
+// --- nested_match.carbon
+
+library "[[@TEST_NAME]]";
+
+fn F() -> ();
+
+fn G() {
+  // The SemIR should contain only two temporary_storage insts, not three.
+  let (x: (), var y: (), z: ()) = (F(), F(), F());
+}
+
 // CHECK:STDOUT: --- basic.carbon
 // CHECK:STDOUT: --- basic.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT: constants {
@@ -274,6 +307,152 @@ class C {
 // CHECK:STDOUT:   return
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
+// CHECK:STDOUT: --- public_function.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
+// CHECK:STDOUT:   %pattern_type: type = pattern_type %empty_tuple.type [concrete]
+// CHECK:STDOUT:   %F.type: type = fn_type @F [concrete]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [concrete] {
+// CHECK:STDOUT:     import Core//prelude
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Core = imports.%Core
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core.import = import Core
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {
+// CHECK:STDOUT:     %x.patt: %pattern_type = binding_pattern x [concrete]
+// CHECK:STDOUT:     %x.param_patt: %pattern_type = value_param_pattern %x.patt, call_param0 [concrete]
+// CHECK:STDOUT:     %y.patt: %pattern_type = binding_pattern y [concrete]
+// CHECK:STDOUT:     %y.param_patt: %pattern_type = ref_param_pattern %y.patt, call_param1 [concrete]
+// CHECK:STDOUT:     %.loc4_13: %pattern_type = var_pattern %y.param_patt [concrete]
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %x.param: %empty_tuple.type = value_param call_param0
+// CHECK:STDOUT:     %.loc4_10.1: type = splice_block %.loc4_10.3 [concrete = constants.%empty_tuple.type] {
+// CHECK:STDOUT:       %.loc4_10.2: %empty_tuple.type = tuple_literal ()
+// CHECK:STDOUT:       %.loc4_10.3: type = converted %.loc4_10.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type]
+// CHECK:STDOUT:     }
+// CHECK:STDOUT:     %x: %empty_tuple.type = bind_name x, %x.param
+// CHECK:STDOUT:     %y.param: ref %empty_tuple.type = ref_param call_param1
+// CHECK:STDOUT:     %.loc4_21.1: type = splice_block %.loc4_21.3 [concrete = constants.%empty_tuple.type] {
+// CHECK:STDOUT:       %.loc4_21.2: %empty_tuple.type = tuple_literal ()
+// CHECK:STDOUT:       %.loc4_21.3: type = converted %.loc4_21.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type]
+// CHECK:STDOUT:     }
+// CHECK:STDOUT:     %y: ref %empty_tuple.type = bind_name y, %y.param
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F(%x.param: %empty_tuple.type, %y.param: %empty_tuple.type);
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- public_function.impl.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
+// CHECK:STDOUT:   %pattern_type: type = pattern_type %empty_tuple.type [concrete]
+// CHECK:STDOUT:   %F.type: type = fn_type @F [concrete]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [concrete] {
+// CHECK:STDOUT:     import Core//prelude
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:     .Core = imports.%Core
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %P.import = import P
+// CHECK:STDOUT:   %default.import = import <none>
+// CHECK:STDOUT:   %Core.import = import Core
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {
+// CHECK:STDOUT:     %x.patt: %pattern_type = binding_pattern x [concrete]
+// CHECK:STDOUT:     %x.param_patt: %pattern_type = value_param_pattern %x.patt, call_param0 [concrete]
+// CHECK:STDOUT:     %y.patt: %pattern_type = binding_pattern y [concrete]
+// CHECK:STDOUT:     %y.param_patt: %pattern_type = ref_param_pattern %y.patt, call_param1 [concrete]
+// CHECK:STDOUT:     %.loc4_13: %pattern_type = var_pattern %y.param_patt [concrete]
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %x.param: %empty_tuple.type = value_param call_param0
+// CHECK:STDOUT:     %.loc4_10.1: type = splice_block %.loc4_10.3 [concrete = constants.%empty_tuple.type] {
+// CHECK:STDOUT:       %.loc4_10.2: %empty_tuple.type = tuple_literal ()
+// CHECK:STDOUT:       %.loc4_10.3: type = converted %.loc4_10.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type]
+// CHECK:STDOUT:     }
+// CHECK:STDOUT:     %x: %empty_tuple.type = bind_name x, %x.param
+// CHECK:STDOUT:     %y.param: ref %empty_tuple.type = ref_param call_param1
+// CHECK:STDOUT:     %.loc4_21.1: type = splice_block %.loc4_21.3 [concrete = constants.%empty_tuple.type] {
+// CHECK:STDOUT:       %.loc4_21.2: %empty_tuple.type = tuple_literal ()
+// CHECK:STDOUT:       %.loc4_21.3: type = converted %.loc4_21.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type]
+// CHECK:STDOUT:     }
+// CHECK:STDOUT:     %y: ref %empty_tuple.type = bind_name y, %y.param
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F(%x.param: %empty_tuple.type, %y.param: %empty_tuple.type) [from "public_function.carbon"] {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- use_public_function.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %G.type: type = fn_type @G [concrete]
+// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
+// CHECK:STDOUT:   %G: %G.type = struct_value () [concrete]
+// CHECK:STDOUT:   %F.type: type = fn_type @F [concrete]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [concrete]
+// CHECK:STDOUT:   %empty_tuple: %empty_tuple.type = tuple_value () [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [concrete] {
+// CHECK:STDOUT:     import Core//prelude
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %P: <namespace> = namespace file.%P.import, [concrete] {
+// CHECK:STDOUT:     .F = %P.F
+// CHECK:STDOUT:     import P//default
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %P.F: %F.type = import_ref P//default, F, loaded [concrete = constants.%F]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Core = imports.%Core
+// CHECK:STDOUT:     .P = imports.%P
+// CHECK:STDOUT:     .G = %G.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core.import = import Core
+// CHECK:STDOUT:   %P.import = import P
+// CHECK:STDOUT:   %G.decl: %G.type = fn_decl @G [concrete = constants.%G] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @G() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %P.ref: <namespace> = name_ref P, imports.%P [concrete = imports.%P]
+// CHECK:STDOUT:   %F.ref: %F.type = name_ref F, imports.%P.F [concrete = constants.%F]
+// CHECK:STDOUT:   %.loc7_8.1: %empty_tuple.type = tuple_literal ()
+// CHECK:STDOUT:   %.loc7_12.1: %empty_tuple.type = tuple_literal ()
+// CHECK:STDOUT:   %empty_tuple: %empty_tuple.type = tuple_value () [concrete = constants.%empty_tuple]
+// CHECK:STDOUT:   %.loc7_8.2: %empty_tuple.type = converted %.loc7_8.1, %empty_tuple [concrete = constants.%empty_tuple]
+// CHECK:STDOUT:   %.1: ref %empty_tuple.type = temporary_storage
+// CHECK:STDOUT:   %.loc7_12.2: init %empty_tuple.type = tuple_init () to %.1 [concrete = constants.%empty_tuple]
+// CHECK:STDOUT:   %.2: init %empty_tuple.type = converted %.loc7_12.1, %.loc7_12.2 [concrete = constants.%empty_tuple]
+// CHECK:STDOUT:   assign %.1, %.2
+// CHECK:STDOUT:   %F.call: init %empty_tuple.type = call %F.ref(%.loc7_8.2, %.1)
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F [from "public_function.carbon"];
+// CHECK:STDOUT:
 // CHECK:STDOUT: --- fail_nested.carbon
 // CHECK:STDOUT: --- fail_nested.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT: constants {
@@ -417,3 +596,83 @@ class C {
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F(%self.param: %C);
 // CHECK:STDOUT: fn @F(%self.param: %C);
 // CHECK:STDOUT:
 // CHECK:STDOUT:
+// CHECK:STDOUT: --- nested_match.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
+// CHECK:STDOUT:   %pattern_type.cb1: type = pattern_type %empty_tuple.type [concrete]
+// CHECK:STDOUT:   %F.type: type = fn_type @F [concrete]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [concrete]
+// CHECK:STDOUT:   %G.type: type = fn_type @G [concrete]
+// CHECK:STDOUT:   %G: %G.type = struct_value () [concrete]
+// CHECK:STDOUT:   %tuple.type: type = tuple_type (%empty_tuple.type, %empty_tuple.type, %empty_tuple.type) [concrete]
+// CHECK:STDOUT:   %pattern_type.8c1: type = pattern_type %tuple.type [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [concrete] {
+// CHECK:STDOUT:     import Core//prelude
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Core = imports.%Core
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:     .G = %G.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core.import = import Core
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {
+// CHECK:STDOUT:     %return.patt: %pattern_type.cb1 = return_slot_pattern [concrete]
+// CHECK:STDOUT:     %return.param_patt: %pattern_type.cb1 = out_param_pattern %return.patt, call_param0 [concrete]
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %.loc4_12.1: %empty_tuple.type = tuple_literal ()
+// CHECK:STDOUT:     %.loc4_12.2: type = converted %.loc4_12.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type]
+// CHECK:STDOUT:     %return.param: ref %empty_tuple.type = out_param call_param0
+// CHECK:STDOUT:     %return: ref %empty_tuple.type = return_slot %return.param
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %G.decl: %G.type = fn_decl @G [concrete = constants.%G] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() -> %empty_tuple.type;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @G() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   name_binding_decl {
+// CHECK:STDOUT:     %x.patt: %pattern_type.cb1 = binding_pattern x [concrete]
+// CHECK:STDOUT:     %y.patt: %pattern_type.cb1 = binding_pattern y [concrete]
+// CHECK:STDOUT:     %.loc8_15: %pattern_type.cb1 = var_pattern %y.patt [concrete]
+// CHECK:STDOUT:     %z.patt: %pattern_type.cb1 = binding_pattern z [concrete]
+// CHECK:STDOUT:     %.loc8_31: %pattern_type.8c1 = tuple_pattern (%x.patt, %.loc8_15, %z.patt) [concrete]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %y.var: ref %empty_tuple.type = var y
+// CHECK:STDOUT:   %F.ref.loc8_36: %F.type = name_ref F, file.%F.decl [concrete = constants.%F]
+// CHECK:STDOUT:   %F.call.loc8_38: init %empty_tuple.type = call %F.ref.loc8_36()
+// CHECK:STDOUT:   %F.ref.loc8_41: %F.type = name_ref F, file.%F.decl [concrete = constants.%F]
+// CHECK:STDOUT:   %F.call.loc8_43: init %empty_tuple.type = call %F.ref.loc8_41()
+// CHECK:STDOUT:   %F.ref.loc8_46: %F.type = name_ref F, file.%F.decl [concrete = constants.%F]
+// CHECK:STDOUT:   %F.call.loc8_48: init %empty_tuple.type = call %F.ref.loc8_46()
+// CHECK:STDOUT:   %.loc8_49: %tuple.type = tuple_literal (%F.call.loc8_38, %F.call.loc8_43, %F.call.loc8_48)
+// CHECK:STDOUT:   %.loc8_12.1: type = splice_block %.loc8_12.3 [concrete = constants.%empty_tuple.type] {
+// CHECK:STDOUT:     %.loc8_12.2: %empty_tuple.type = tuple_literal ()
+// CHECK:STDOUT:     %.loc8_12.3: type = converted %.loc8_12.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc8_38.1: ref %empty_tuple.type = temporary_storage
+// CHECK:STDOUT:   %.loc8_38.2: ref %empty_tuple.type = temporary %.loc8_38.1, %F.call.loc8_38
+// CHECK:STDOUT:   %x: ref %empty_tuple.type = bind_name x, %.loc8_38.2
+// CHECK:STDOUT:   assign %y.var, %F.call.loc8_43
+// CHECK:STDOUT:   %.loc8_23.1: type = splice_block %.loc8_23.3 [concrete = constants.%empty_tuple.type] {
+// CHECK:STDOUT:     %.loc8_23.2: %empty_tuple.type = tuple_literal ()
+// CHECK:STDOUT:     %.loc8_23.3: type = converted %.loc8_23.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %y: ref %empty_tuple.type = bind_name y, %y.var
+// CHECK:STDOUT:   %.loc8_30.1: type = splice_block %.loc8_30.3 [concrete = constants.%empty_tuple.type] {
+// CHECK:STDOUT:     %.loc8_30.2: %empty_tuple.type = tuple_literal ()
+// CHECK:STDOUT:     %.loc8_30.3: type = converted %.loc8_30.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc8_48.1: ref %empty_tuple.type = temporary_storage
+// CHECK:STDOUT:   %.loc8_48.2: ref %empty_tuple.type = temporary %.loc8_48.1, %F.call.loc8_48
+// CHECK:STDOUT:   %z: ref %empty_tuple.type = bind_name z, %.loc8_48.2
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 2 - 2
toolchain/sem_ir/function.h

@@ -56,8 +56,8 @@ struct FunctionFields {
   // is not virtual (ie: (virtual_modifier == None) == (virtual_index == -1)).
   // is not virtual (ie: (virtual_modifier == None) == (virtual_index == -1)).
   int32_t virtual_index = -1;
   int32_t virtual_index = -1;
 
 
-  // The implicit self parameter, if any, in implicit_param_patterns_id from
-  // EntityWithParamsBase.
+  // The implicit self parameter pattern, if any, in
+  // implicit_param_patterns_id from EntityWithParamsBase.
   InstId self_param_id = InstId::None;
   InstId self_param_id = InstId::None;
 
 
   // The following member is set on the first call to the function, or at the
   // The following member is set on the first call to the function, or at the

+ 5 - 1
toolchain/sem_ir/inst.h

@@ -79,7 +79,7 @@ struct InstLikeTypeInfo<InstCat> : InstLikeTypeInfoBase<InstCat> {
   static_assert(HasKindMemberAsField<InstCat>,
   static_assert(HasKindMemberAsField<InstCat>,
                 "Instruction category should have a kind field");
                 "Instruction category should have a kind field");
   static auto GetKind(InstCat cat) -> InstKind { return cat.kind; }
   static auto GetKind(InstCat cat) -> InstKind { return cat.kind; }
-  static auto IsKind(InstKind kind) -> bool {
+  static constexpr auto IsKind(InstKind kind) -> bool {
     for (InstKind k : InstCat::Kinds) {
     for (InstKind k : InstCat::Kinds) {
       if (k == kind) {
       if (k == kind) {
         return true;
         return true;
@@ -100,6 +100,10 @@ struct InstLikeTypeInfo<InstCat> : InstLikeTypeInfoBase<InstCat> {
   }
   }
 };
 };
 
 
+// HasInstCategory is true if T::Kind is an element of InstCat::Kinds.
+template <typename InstCat, typename T>
+concept HasInstCategory = InstLikeTypeInfo<InstCat>::IsKind(T::Kind);
+
 // A type is InstLike if InstLikeTypeInfo is defined for it.
 // A type is InstLike if InstLikeTypeInfo is defined for it.
 template <typename T>
 template <typename T>
 concept InstLikeType = requires { sizeof(InstLikeTypeInfo<T>); };
 concept InstLikeType = requires { sizeof(InstLikeTypeInfo<T>); };