Ver Fonte

Get specific interfaces with correct specific from named constraints (#6435)

When forming an IdentifiedFacetType, we collect interfaces named by
require decls in named constraints that the facet type refers to. These
interfaces come with a specific, but the require decl is inside an named
constraint which may be generic. So we need the specific being applied
to the containing named constraint to also be applied to the require
decl and its target interfaces.

This uncovered that the facet type in require decls was not being
imported correctly, as it was not being attached to the require decl's
generic. This is fixed by making import of RequireImplsDecl multiphase,
so that the decl instruction exists before we resolve the facet type
within it. And by pointing the generic importing machinery to the
RequireImplsDecl, and from there to the RequireImpls structure to get
the generic id.

Then `ImplStore::GetOrAddLookupBucket` can use an IdentifiedFacetType to
correctly get the interface being impl'd, both in the local and the
imported named constraint case. Which allows us to correctly diagnose
redeclarations in the impl file of an impl of an interface through a
named constraint. And to correctly _not_ diagnose them when the specific
in the generic named constraint differs from other decls.
Dana Jansens há 5 meses atrás
pai
commit
0cf2448505

+ 2 - 2
toolchain/check/handle_require.cpp

@@ -183,8 +183,8 @@ static auto ValidateRequire(Context& context, SemIR::LocId loc_id,
     return std::nullopt;
   }
 
-  auto identified_facet_type_id =
-      RequireIdentifiedFacetType(context, *constraint_facet_type, [&] {
+  auto identified_facet_type_id = RequireIdentifiedFacetType(
+      context, SemIR::LocId(constraint_inst_id), *constraint_facet_type, [&] {
         CARBON_DIAGNOSTIC(
             RequireImplsUnidentifiedFacetType, Error,
             "facet type {0} cannot be identified in `require` declaration",

+ 8 - 7
toolchain/check/impl.cpp

@@ -297,13 +297,14 @@ auto CheckConstraintIsInterface(Context& context, SemIR::InstId impl_decl_id,
     return SemIR::SpecificInterface::None;
   }
 
-  auto identified_id = RequireIdentifiedFacetType(context, *facet_type, [&] {
-    CARBON_DIAGNOSTIC(ImplOfUnidentifiedFacetType, Error,
-                      "facet type {0} cannot be identified in `impl as`",
-                      InstIdAsType);
-    return context.emitter().Build(impl_decl_id, ImplOfUnidentifiedFacetType,
-                                   constraint_id);
-  });
+  auto identified_id = RequireIdentifiedFacetType(
+      context, SemIR::LocId(constraint_id), *facet_type, [&] {
+        CARBON_DIAGNOSTIC(ImplOfUnidentifiedFacetType, Error,
+                          "facet type {0} cannot be identified in `impl as`",
+                          InstIdAsType);
+        return context.emitter().Build(
+            impl_decl_id, ImplOfUnidentifiedFacetType, constraint_id);
+      });
   if (!identified_id.has_value()) {
     return SemIR::SpecificInterface::None;
   }

+ 5 - 4
toolchain/check/impl_lookup.cpp

@@ -202,7 +202,7 @@ static auto GetInterfacesFromConstantId(
   const auto& facet_type_info =
       context.facet_types().Get(facet_type_inst.facet_type_id);
   auto identified_id =
-      RequireIdentifiedFacetType(context, facet_type_inst, [&] {
+      RequireIdentifiedFacetType(context, loc_id, facet_type_inst, [&] {
         CARBON_DIAGNOSTIC(ImplLookupInUnidentifiedFacetType, Error,
                           "facet type {0} can not be identified", InstIdAsType);
         return context.emitter().Build(
@@ -323,7 +323,8 @@ static auto GetWitnessIdForImpl(Context& context, SemIR::LocId loc_id,
 // is allowed to be a non-canonical facet value in order to find a concrete
 // witness, so it's not referenced as a constant value.
 static auto LookupImplWitnessInSelfFacetValue(
-    Context& context, SemIR::InstId self_facet_value_inst_id,
+    Context& context, SemIR::LocId loc_id,
+    SemIR::InstId self_facet_value_inst_id,
     SemIR::SpecificInterface query_specific_interface) -> EvalImplLookupResult {
   auto facet_type = context.types().TryGetAs<SemIR::FacetType>(
       context.insts().Get(self_facet_value_inst_id).type_id());
@@ -336,7 +337,7 @@ static auto LookupImplWitnessInSelfFacetValue(
   // `FacetValue` witnesses are the output of an impl lookup, which finds and
   // returns witnesses in the same order.
   auto identified_id =
-      RequireIdentifiedFacetType(context, *facet_type, nullptr);
+      RequireIdentifiedFacetType(context, loc_id, *facet_type, nullptr);
   // This should not be possible as FacetValue is constructed by a conversion
   // to a facet type, which performs impl lookup for that facet type, and
   // lookup only succeeds for complete facet types.
@@ -879,7 +880,7 @@ auto EvalLookupSingleImplWitness(Context& context, SemIR::LocId loc_id,
       context.specific_interfaces().Get(eval_query.query_specific_interface_id);
 
   auto facet_lookup_result = LookupImplWitnessInSelfFacetValue(
-      context, self_facet_value_inst_id, query_specific_interface);
+      context, loc_id, self_facet_value_inst_id, query_specific_interface);
   if (facet_lookup_result.has_final_value()) {
     return facet_lookup_result;
   }

+ 59 - 29
toolchain/check/import_ref.cpp

@@ -15,6 +15,7 @@
 #include "toolchain/base/shared_value_stores.h"
 #include "toolchain/check/context.h"
 #include "toolchain/check/eval.h"
+#include "toolchain/check/facet_type.h"
 #include "toolchain/check/generic.h"
 #include "toolchain/check/import.h"
 #include "toolchain/check/inst.h"
@@ -494,7 +495,7 @@ static auto SetIndirectConstantValues(
 // Adds an import_ref instruction for an instruction that we have already loaded
 // from an imported IR, with a known constant value. This is useful when the
 // instruction has a symbolic constant value, in order to produce an instruction
-// that hold that symbolic constant.
+// that holds that symbolic constant.
 static auto AddLoadedImportRef(ImportContext& context,
                                SemIR::TypeId local_type_id,
                                SemIR::InstId import_inst_id,
@@ -864,13 +865,15 @@ static auto GetLocalConstantId(ImportRefResolver& resolver,
   auto import_decl_inst_id = resolver.import_generics().Get(generic_id).decl_id;
   auto import_decl_inst =
       resolver.import_insts().GetWithAttachedType(import_decl_inst_id);
-  if (import_decl_inst.Is<SemIR::ImplDecl>()) {
-    // For an impl declaration, the imported entity can be found via the
-    // declaration.
+  if (import_decl_inst.Is<SemIR::ImplDecl>() ||
+      import_decl_inst.Is<SemIR::RequireImplsDecl>()) {
+    // For these decl types, the imported entity can be found via the
+    // declaration's operands.
     return GetLocalConstantId(resolver, import_decl_inst_id);
   }
   // For all other kinds of declaration, the imported entity can be found via
   // the type of the declaration.
+  CARBON_CHECK(import_decl_inst.type_id().has_value());
   return GetLocalConstantId(resolver, import_decl_inst.type_id());
 }
 
@@ -904,6 +907,11 @@ static auto GetLocalGenericId(ImportContext& context,
     case CARBON_KIND(SemIR::ImplDecl impl_decl): {
       return context.local_impls().Get(impl_decl.impl_id).generic_id;
     }
+    case CARBON_KIND(SemIR::RequireImplsDecl require_decl): {
+      return context.local_require_impls()
+          .Get(require_decl.require_impls_id)
+          .generic_id;
+    }
     default: {
       CARBON_FATAL("Unexpected inst for generic declaration: {0}", inst);
     }
@@ -2562,6 +2570,13 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
       resolver, import_impl.constraint_id, constraint_const_id);
   new_impl.interface = GetLocalSpecificInterface(
       resolver, import_impl.interface, specific_interface_data);
+  // Create a local IdentifiedFacetType for the imported facet type, since impl
+  // declarations always identify the facet type.
+  if (auto facet_type = resolver.local_insts().TryGetAs<SemIR::FacetType>(
+          resolver.local_constant_values().GetInstId(constraint_const_id))) {
+    RequireIdentifiedFacetType(resolver.local_context(), SemIR::LocId::None,
+                               *facet_type, nullptr);
+  }
   if (import_impl.is_complete()) {
     AddImplDefinition(resolver, import_impl, new_impl);
   }
@@ -2579,10 +2594,45 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
 }
 
 static auto TryResolveTypedInst(ImportRefResolver& resolver,
-                                SemIR::RequireImplsDecl inst) -> ResolveResult {
+                                SemIR::RequireImplsDecl inst,
+                                SemIR::ConstantId require_decl_const_id)
+    -> ResolveResult {
   const auto& import_require =
       resolver.import_require_impls().Get(inst.require_impls_id);
 
+  auto require_decl_id = SemIR::InstId::None;
+  auto require_impls_id = SemIR::RequireImplsId::None;
+  if (!require_decl_const_id.has_value()) {
+    // Phase one: Make the decl and structure with placeholder values to be
+    // filled in. Begin the generic so instructions can be attached to it.
+    SemIR::RequireImplsDecl require_decl = {
+        .require_impls_id = SemIR::RequireImplsId::None,
+        .decl_block_id = SemIR::InstBlockId::Empty};
+    auto require_decl_id = AddPlaceholderImportedInst(
+        resolver, import_require.decl_id, require_decl);
+    require_impls_id = resolver.local_require_impls().Add(
+        {.self_id = SemIR::TypeInstId::None,
+         .facet_type_inst_id = SemIR::TypeInstId::None,
+         .facet_type_id = SemIR::FacetTypeId::None,
+         .decl_id = require_decl_id,
+         .parent_scope_id = SemIR::NameScopeId::None,
+         .generic_id = MakeIncompleteGeneric(resolver, require_decl_id,
+                                             import_require.generic_id)});
+
+    // Write the RequireImplsId into the RequireImplsDecl.
+    require_decl.require_impls_id = require_impls_id;
+    require_decl_const_id =
+        ReplacePlaceholderImportedInst(resolver, require_decl_id, require_decl);
+  } else {
+    // Phase two: Get the `require_decl_id` and `require_impls_id` from the
+    // RequireImplsDecl constructed in phase one.
+    require_decl_id =
+        resolver.local_constant_values().GetInstId(require_decl_const_id);
+    require_impls_id = resolver.local_insts()
+                           .GetAs<SemIR::RequireImplsDecl>(require_decl_id)
+                           .require_impls_id;
+  }
+
   // Load dependent constants.
   auto parent_scope_id =
       GetLocalNameScopeId(resolver, import_require.parent_scope_id);
@@ -2592,29 +2642,9 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
       GetLocalConstantId(resolver, import_require.facet_type_inst_id);
 
   if (resolver.HasNewWork()) {
-    return ResolveResult::Retry();
+    return ResolveResult::Retry(require_decl_const_id, require_decl_id);
   }
 
-  // Make the decl and structure with placeholder values to be filled in.
-  SemIR::RequireImplsDecl require_decl = {
-      .require_impls_id = SemIR::RequireImplsId::None,
-      .decl_block_id = SemIR::InstBlockId::Empty};
-  auto require_decl_id = AddPlaceholderImportedInst(
-      resolver, import_require.decl_id, require_decl);
-  auto require_impls_id = resolver.local_require_impls().Add(
-      {.self_id = SemIR::TypeInstId::None,
-       .facet_type_inst_id = SemIR::TypeInstId::None,
-       .facet_type_id = SemIR::FacetTypeId::None,
-       .decl_id = require_decl_id,
-       .parent_scope_id = SemIR::NameScopeId::None,
-       .generic_id = MakeIncompleteGeneric(resolver, require_decl_id,
-                                           import_require.generic_id)});
-
-  // Write the RequireImplsId into the RequireImplsDecl.
-  require_decl.require_impls_id = require_impls_id;
-  auto require_decl_const_id =
-      ReplacePlaceholderImportedInst(resolver, require_decl_id, require_decl);
-
   // Fill in the RequireImpls structure.
   auto& new_require = resolver.local_require_impls().Get(require_impls_id);
   new_require.self_id = AddLoadedImportRefForType(
@@ -3604,6 +3634,9 @@ static auto TryResolveInstCanonical(ImportRefResolver& resolver,
     case CARBON_KIND(SemIR::NamedConstraintDecl inst): {
       return TryResolveTypedInst(resolver, inst, const_id);
     }
+    case CARBON_KIND(SemIR::RequireImplsDecl inst): {
+      return TryResolveTypedInst(resolver, inst, const_id);
+    }
     default:
       break;
   }
@@ -3774,9 +3807,6 @@ static auto TryResolveInstCanonical(ImportRefResolver& resolver,
     case CARBON_KIND(SemIR::RequireCompleteType inst): {
       return TryResolveTypedInst(resolver, inst);
     }
-    case CARBON_KIND(SemIR::RequireImplsDecl inst): {
-      return TryResolveTypedInst(resolver, inst);
-    }
     case CARBON_KIND(SemIR::RequireSpecificDefinition inst): {
       return TryResolveTypedInst(resolver, inst);
     }

+ 2 - 2
toolchain/check/testdata/facet/require_import.carbon

@@ -53,10 +53,10 @@ fn F(A:! X, B:! Y) {}
 // CHECK:STDOUT:   %Main.Y: type = import_ref Main//a, Y, loaded [concrete = constants.%Y.type]
 // CHECK:STDOUT:   %Main.X: type = import_ref Main//a, X, loaded [concrete = constants.%X.type]
 // CHECK:STDOUT:   %Main.import_ref.462 = import_ref Main//a, loc3_13, unloaded
-// CHECK:STDOUT:   %Main.import_ref.b7c: type = import_ref Main//a, loc10_18, loaded [symbolic = constants.%Self.binding.as_type.1a1]
+// CHECK:STDOUT:   %Main.import_ref.b7c: type = import_ref Main//a, loc10_18, loaded [symbolic = @X.require0.%Self.binding.as_type (constants.%Self.binding.as_type.1a1)]
 // CHECK:STDOUT:   %Main.import_ref.dec: %X.type = import_ref Main//a, loc9_14, loaded [symbolic = constants.%Self.861]
 // CHECK:STDOUT:   %Main.import_ref.cae = import_ref Main//a, loc9_14, unloaded
-// CHECK:STDOUT:   %Main.import_ref.3e6: type = import_ref Main//a, loc6_11, loaded [symbolic = constants.%Self.binding.as_type.384]
+// CHECK:STDOUT:   %Main.import_ref.3e6: type = import_ref Main//a, loc6_11, loaded [symbolic = @Y.require1.%Self.binding.as_type (constants.%Self.binding.as_type.384)]
 // CHECK:STDOUT:   %Main.import_ref.35b: %Y.type = import_ref Main//a, loc5_13, loaded [symbolic = constants.%Self.29b]
 // CHECK:STDOUT:   %Main.import_ref.581 = import_ref Main//a, loc5_13, unloaded
 // CHECK:STDOUT: }

+ 61 - 0
toolchain/check/testdata/generic/identify_specific_facet_type.carbon

@@ -0,0 +1,61 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon
+//
+// AUTOUPDATE
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/generic/identify_specific_facet_type.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/generic/identify_specific_facet_type.carbon
+
+// --- fail_error_in_identifying_require_facet_type.carbon
+library "[[@TEST_NAME]]";
+
+// Identification tries to form the specific interface `K(array(i32, -1))` which
+// is invalid. So the constraint J is seen as having 0 interfaces to impl.
+
+interface K(T:! type) {}
+constraint J(N:! i32) {
+  // CHECK:STDERR: fail_error_in_identifying_require_facet_type.carbon:[[@LINE+3]]:30: error: array bound of -1 is negative [ArrayBoundNegative]
+  // CHECK:STDERR:   require impls K(array(i32, N));
+  // CHECK:STDERR:                              ^
+  require impls K(array(i32, N));
+}
+
+// CHECK:STDERR: fail_error_in_identifying_require_facet_type.carbon:[[@LINE+8]]:12: note: in `require` used here [ResolvingSpecificHere]
+// CHECK:STDERR: impl {} as J(-1) {}
+// CHECK:STDERR:            ^~~~~
+// CHECK:STDERR:
+// CHECK:STDERR: fail_error_in_identifying_require_facet_type.carbon:[[@LINE+4]]:1: error: impl as 0 interfaces, expected 1 [ImplOfNotOneInterface]
+// CHECK:STDERR: impl {} as J(-1) {}
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+impl {} as J(-1) {}
+
+// --- fail_error_in_identifying_extend_require_facet_type.carbon
+library "[[@TEST_NAME]]";
+
+// Identification tries to form the specific interface `K(array(i32, -1))` which
+// is invalid. So the constraint J is seen as having 0 interfaces to impl.
+//
+// `extend require` is identified in the same way as the non-extend case.
+
+interface K(T:! type) {}
+constraint J(N:! i32) {
+  // CHECK:STDERR: fail_error_in_identifying_extend_require_facet_type.carbon:[[@LINE+3]]:37: error: array bound of -1 is negative [ArrayBoundNegative]
+  // CHECK:STDERR:   extend require impls K(array(i32, N));
+  // CHECK:STDERR:                                     ^
+  extend require impls K(array(i32, N));
+}
+
+// CHECK:STDERR: fail_error_in_identifying_extend_require_facet_type.carbon:[[@LINE+8]]:12: note: in `require` used here [ResolvingSpecificHere]
+// CHECK:STDERR: impl {} as J(-1) {}
+// CHECK:STDERR:            ^~~~~
+// CHECK:STDERR:
+// CHECK:STDERR: fail_error_in_identifying_extend_require_facet_type.carbon:[[@LINE+4]]:1: error: impl as 0 interfaces, expected 1 [ImplOfNotOneInterface]
+// CHECK:STDERR: impl {} as J(-1) {}
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
+impl {} as J(-1) {}

+ 8 - 2
toolchain/check/testdata/impl/impl_as_named_constraint.carbon

@@ -128,7 +128,7 @@ constraint C {
 // CHECK:STDERR:
 impl () as C {}
 
-// --- todo_fail_duplicate_through_generic_constraint.carbon
+// --- fail_duplicate_through_generic_constraint.carbon
 library "[[@TEST_NAME]]";
 
 interface A(T:! type) {}
@@ -140,5 +140,11 @@ constraint B(X:! type, Y:! type) {
 // This should impl () as A({}).
 impl () as B((), {}) {}
 
-// This should be considered a duplicate.
+// CHECK:STDERR: fail_duplicate_through_generic_constraint.carbon:[[@LINE+7]]:1: error: found non-final `impl` with the same type structure as another non-final `impl` [ImplNonFinalSameTypeStructure]
+// CHECK:STDERR: impl () as A({}) {}
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~
+// CHECK:STDERR: fail_duplicate_through_generic_constraint.carbon:[[@LINE-5]]:1: note: other `impl` here [ImplNonFinalSameTypeStructureNote]
+// CHECK:STDERR: impl () as B((), {}) {}
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
 impl () as A({}) {}

+ 100 - 112
toolchain/check/testdata/impl/import_generic.carbon

@@ -84,15 +84,12 @@ impl forall [T:! type] C as I(T*);
 // CHECK:STDERR:
 impl forall [T:! type] C as I(T*) {}
 
-// TODO: These should be `error: redeclaration of imported impl`.
-// GetOrAddLookupBucket is putting the impl of a named constraint in the wrong
-// bucket.
-// CHECK:STDERR: fail_import_generic.impl.carbon:[[@LINE+4]]:1: error: impl declared but not defined [ImplMissingDefinition]
+// CHECK:STDERR: fail_import_generic.impl.carbon:[[@LINE+4]]:1: error: redeclaration of imported impl [RedeclImportedImpl]
 // CHECK:STDERR: impl forall [T:! type] C as N(T);
 // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // CHECK:STDERR:
 impl forall [T:! type] C as N(T);
-// CHECK:STDERR: fail_import_generic.impl.carbon:[[@LINE+4]]:1: error: impl declared but not defined [ImplMissingDefinition]
+// CHECK:STDERR: fail_import_generic.impl.carbon:[[@LINE+4]]:1: error: redeclaration of imported impl [RedeclImportedImpl]
 // CHECK:STDERR: impl forall [T:! type] C as N(T*);
 // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // CHECK:STDERR:
@@ -170,16 +167,15 @@ impl forall [T:! type] D as J(T*);
 // CHECK:STDERR:
 impl forall [T:! type] D as J(T*) {}
 
-// TODO: This should be `error: redeclaration of imported impl`.
+// CHECK:STDERR: fail_import_generic_decl.impl.carbon:[[@LINE+4]]:1: error: redeclaration of imported impl [RedeclImportedImpl]
+// CHECK:STDERR: impl forall [T:! type] D as N(T) {}
+// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// CHECK:STDERR:
 impl forall [T:! type] D as N(T) {}
 
-// TODO: This should be `error: redeclaration of imported impl`.
-// CHECK:STDERR: fail_import_generic_decl.impl.carbon:[[@LINE+7]]:1: error: found non-final `impl` with the same type structure as another non-final `impl` [ImplNonFinalSameTypeStructure]
+// CHECK:STDERR: fail_import_generic_decl.impl.carbon:[[@LINE+4]]:1: error: redeclaration of imported impl [RedeclImportedImpl]
 // CHECK:STDERR: impl forall [T:! type] D as N(T*) {}
 // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// CHECK:STDERR: fail_import_generic_decl.impl.carbon:[[@LINE-6]]:1: note: other `impl` here [ImplNonFinalSameTypeStructureNote]
-// CHECK:STDERR: impl forall [T:! type] D as N(T) {}
-// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // CHECK:STDERR:
 impl forall [T:! type] D as N(T*) {}
 
@@ -327,7 +323,7 @@ impl forall [T:! type] D as N(T*) {}
 // CHECK:STDOUT:   %Main.J: %J.type.2b8 = import_ref Main//basic_import_generic_interface, J, loaded [concrete = constants.%J.generic]
 // CHECK:STDOUT:   %Main.import_ref.769 = import_ref Main//basic_import_generic_interface, loc3_23, unloaded
 // CHECK:STDOUT:   %Main.import_ref.efcd44.1: type = import_ref Main//basic_import_generic_interface, loc3_13, loaded [symbolic = @I.%T (constants.%T)]
-// CHECK:STDOUT:   %Main.import_ref.ce5: type = import_ref Main//basic_import_generic_interface, loc5_18, loaded [symbolic = constants.%Self.binding.as_type]
+// CHECK:STDOUT:   %Main.import_ref.ce5: type = import_ref Main//basic_import_generic_interface, loc5_18, loaded [symbolic = @J.require0.%Self.binding.as_type (constants.%Self.binding.as_type)]
 // CHECK:STDOUT:   %Main.import_ref.efcd44.2: type = import_ref Main//basic_import_generic_interface, loc4_13, loaded [symbolic = @J.%T (constants.%T)]
 // CHECK:STDOUT:   %Main.import_ref.cc4: @J.%J.type (%J.type.8ec) = import_ref Main//basic_import_generic_interface, loc4_23, loaded [symbolic = @J.%Self (constants.%Self.f68)]
 // CHECK:STDOUT:   %Main.import_ref.b3b = import_ref Main//basic_import_generic_interface, loc4_23, unloaded
@@ -572,8 +568,8 @@ impl forall [T:! type] D as N(T*) {}
 // CHECK:STDOUT:   %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self.aa1546.1 [symbolic]
 // CHECK:STDOUT:   %require_complete: <witness> = require_complete_type %I.type.070 [symbolic]
 // CHECK:STDOUT:   %J.type.b8d23b.2: type = facet_type <@J, @J(%empty_struct_type)> [concrete]
-// CHECK:STDOUT:   %Self.aa1546.2: %J.type.b8d23b.2 = symbolic_binding Self, 1 [symbolic]
 // CHECK:STDOUT:   %I.type.399: type = facet_type <@I, @I(%empty_struct_type)> [concrete]
+// CHECK:STDOUT:   %Self.aa1546.2: %J.type.b8d23b.2 = symbolic_binding Self, 1 [symbolic]
 // CHECK:STDOUT:   %Self.32d: %I.type.399 = symbolic_binding Self, 1 [symbolic]
 // CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %I.type.399 [concrete]
 // CHECK:STDOUT:   %I.impl_witness: <witness> = impl_witness file.%I.impl_witness_table [concrete]
@@ -584,7 +580,7 @@ impl forall [T:! type] D as N(T*) {}
 // CHECK:STDOUT:   %Main.J: %J.type.267 = import_ref Main//basic_import_generic_constraint, J, loaded [concrete = constants.%empty_struct.b6f]
 // CHECK:STDOUT:   %Main.import_ref.769 = import_ref Main//basic_import_generic_constraint, loc3_23, unloaded
 // CHECK:STDOUT:   %Main.import_ref.efcd44.1: type = import_ref Main//basic_import_generic_constraint, loc3_13, loaded [symbolic = @I.%T (constants.%T)]
-// CHECK:STDOUT:   %Main.import_ref.f92: type = import_ref Main//basic_import_generic_constraint, loc5_18, loaded [symbolic = constants.%Self.binding.as_type]
+// CHECK:STDOUT:   %Main.import_ref.f92: type = import_ref Main//basic_import_generic_constraint, loc5_18, loaded [symbolic = @J.require0.%Self.binding.as_type (constants.%Self.binding.as_type)]
 // CHECK:STDOUT:   %Main.import_ref.efcd44.2: type = import_ref Main//basic_import_generic_constraint, loc4_14, loaded [symbolic = @J.%T (constants.%T)]
 // CHECK:STDOUT:   %Main.import_ref.d4d: @J.%J.type (%J.type.b8d23b.1) = import_ref Main//basic_import_generic_constraint, loc4_24, loaded [symbolic = @J.%Self (constants.%Self.aa1546.1)]
 // CHECK:STDOUT:   %Main.import_ref.388 = import_ref Main//basic_import_generic_constraint, loc4_24, unloaded
@@ -689,6 +685,14 @@ impl forall [T:! type] D as N(T*) {}
 // CHECK:STDOUT:   %require_complete => constants.%complete_type
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: specific @J.require0(constants.%empty_struct_type, constants.%Self.aa1546.1) {
+// CHECK:STDOUT:   %T => constants.%empty_struct_type
+// CHECK:STDOUT:   %J.type => constants.%J.type.b8d23b.2
+// CHECK:STDOUT:   %Self => constants.%Self.aa1546.1
+// CHECK:STDOUT:   %Self.binding.as_type => constants.%Self.binding.as_type
+// CHECK:STDOUT:   %I.type => constants.%I.type.399
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
 // CHECK:STDOUT: specific @I(constants.%empty_struct_type) {
 // CHECK:STDOUT:   %T => constants.%empty_struct_type
 // CHECK:STDOUT:
@@ -944,9 +948,7 @@ impl forall [T:! type] D as N(T*) {}
 // CHECK:STDOUT:   %N.type.b8d23b.1: type = facet_type <@N, @N(%T)> [symbolic]
 // CHECK:STDOUT:   %Self.aa1: %N.type.b8d23b.1 = symbolic_binding Self, 1 [symbolic]
 // CHECK:STDOUT:   %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self.aa1 [symbolic]
-// CHECK:STDOUT:   %I.impl_witness.524962.1: <witness> = impl_witness file.%I.impl_witness_table.loc35, @C.as.I.impl.3f8440.1(%T) [symbolic]
 // CHECK:STDOUT:   %N.type.b8d23b.2: type = facet_type <@N, @N(%ptr)> [symbolic]
-// CHECK:STDOUT:   %I.impl_witness.524962.2: <witness> = impl_witness file.%I.impl_witness_table.loc40, @C.as.I.impl.3f8440.2(%T) [symbolic]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -967,7 +969,7 @@ impl forall [T:! type] D as N(T*) {}
 // CHECK:STDOUT:   %Main.import_ref.29aca8.2: type = import_ref Main//import_generic, loc12_24, loaded [concrete = constants.%C]
 // CHECK:STDOUT:   %Main.import_ref.7b6: type = import_ref Main//import_generic, loc12_33, loaded [symbolic = @C.as.I.impl.1fddff.1.%I.type (constants.%I.type.229)]
 // CHECK:STDOUT:   %Main.import_ref.efcd44.3: type = import_ref Main//import_generic, loc12_14, loaded [symbolic = @C.as.I.impl.1fddff.1.%T (constants.%T)]
-// CHECK:STDOUT:   %Main.import_ref.f92: type = import_ref Main//import_generic, loc15_18, loaded [symbolic = constants.%Self.binding.as_type]
+// CHECK:STDOUT:   %Main.import_ref.f92: type = import_ref Main//import_generic, loc15_18, loaded [symbolic = @N.require0.%Self.binding.as_type (constants.%Self.binding.as_type)]
 // CHECK:STDOUT:   %Main.import_ref.efcd44.4: type = import_ref Main//import_generic, loc14_14, loaded [symbolic = @N.%T (constants.%T)]
 // CHECK:STDOUT:   %Main.import_ref.d4d: @N.%N.type (%N.type.b8d23b.1) = import_ref Main//import_generic, loc14_24, loaded [symbolic = @N.%Self (constants.%Self.aa1)]
 // CHECK:STDOUT:   %Main.import_ref.388 = import_ref Main//import_generic, loc14_24, unloaded
@@ -1029,26 +1031,22 @@ impl forall [T:! type] D as N(T*) {}
 // CHECK:STDOUT:   } {
 // CHECK:STDOUT:     %C.ref: type = name_ref C, imports.%Main.C [concrete = constants.%C]
 // CHECK:STDOUT:     %N.ref: %N.type.673 = name_ref N, imports.%Main.N [concrete = constants.%empty_struct]
-// CHECK:STDOUT:     %T.ref: type = name_ref T, %T.loc35_14.1 [symbolic = %T.loc35_14.2 (constants.%T)]
-// CHECK:STDOUT:     %N.type.loc35_32.1: type = facet_type <@N, @N(constants.%T)> [symbolic = %N.type.loc35_32.2 (constants.%N.type.b8d23b.1)]
+// CHECK:STDOUT:     %T.ref: type = name_ref T, %T.loc32_14.1 [symbolic = %T.loc32_14.2 (constants.%T)]
+// CHECK:STDOUT:     %N.type.loc32_32.1: type = facet_type <@N, @N(constants.%T)> [symbolic = %N.type.loc32_32.2 (constants.%N.type.b8d23b.1)]
 // CHECK:STDOUT:     %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self]
-// CHECK:STDOUT:     %T.loc35_14.1: type = symbolic_binding T, 0 [symbolic = %T.loc35_14.2 (constants.%T)]
+// CHECK:STDOUT:     %T.loc32_14.1: type = symbolic_binding T, 0 [symbolic = %T.loc32_14.2 (constants.%T)]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %I.impl_witness_table.loc35 = impl_witness_table (), @C.as.I.impl.3f8440.1 [concrete]
-// CHECK:STDOUT:   %I.impl_witness.loc35: <witness> = impl_witness %I.impl_witness_table.loc35, @C.as.I.impl.3f8440.1(constants.%T) [symbolic = @C.as.I.impl.3f8440.1.%I.impl_witness (constants.%I.impl_witness.524962.1)]
 // CHECK:STDOUT:   impl_decl @C.as.I.impl.3f8440.2 [concrete] {
 // CHECK:STDOUT:     %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete]
 // CHECK:STDOUT:   } {
 // CHECK:STDOUT:     %C.ref: type = name_ref C, imports.%Main.C [concrete = constants.%C]
 // CHECK:STDOUT:     %N.ref: %N.type.673 = name_ref N, imports.%Main.N [concrete = constants.%empty_struct]
-// CHECK:STDOUT:     %T.ref: type = name_ref T, %T.loc40_14.1 [symbolic = %T.loc40_14.2 (constants.%T)]
-// CHECK:STDOUT:     %ptr.loc40_32.1: type = ptr_type %T.ref [symbolic = %ptr.loc40_32.2 (constants.%ptr)]
-// CHECK:STDOUT:     %N.type.loc40_33.1: type = facet_type <@N, @N(constants.%ptr)> [symbolic = %N.type.loc40_33.2 (constants.%N.type.b8d23b.2)]
+// CHECK:STDOUT:     %T.ref: type = name_ref T, %T.loc37_14.1 [symbolic = %T.loc37_14.2 (constants.%T)]
+// CHECK:STDOUT:     %ptr.loc37_32.1: type = ptr_type %T.ref [symbolic = %ptr.loc37_32.2 (constants.%ptr)]
+// CHECK:STDOUT:     %N.type.loc37_33.1: type = facet_type <@N, @N(constants.%ptr)> [symbolic = %N.type.loc37_33.2 (constants.%N.type.b8d23b.2)]
 // CHECK:STDOUT:     %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self]
-// CHECK:STDOUT:     %T.loc40_14.1: type = symbolic_binding T, 0 [symbolic = %T.loc40_14.2 (constants.%T)]
+// CHECK:STDOUT:     %T.loc37_14.1: type = symbolic_binding T, 0 [symbolic = %T.loc37_14.2 (constants.%T)]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %I.impl_witness_table.loc40 = impl_witness_table (), @C.as.I.impl.3f8440.2 [concrete]
-// CHECK:STDOUT:   %I.impl_witness.loc40: <witness> = impl_witness %I.impl_witness_table.loc40, @C.as.I.impl.3f8440.2(constants.%T) [symbolic = @C.as.I.impl.3f8440.2.%I.impl_witness (constants.%I.impl_witness.524962.2)]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: generic interface @I(imports.%Main.import_ref.efcd44.1: type) [from "import_generic.carbon"] {
@@ -1164,21 +1162,19 @@ impl forall [T:! type] D as N(T*) {}
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: generic impl @C.as.I.impl.3f8440.1(%T.loc35_14.1: type) {
-// CHECK:STDOUT:   %T.loc35_14.2: type = symbolic_binding T, 0 [symbolic = %T.loc35_14.2 (constants.%T)]
-// CHECK:STDOUT:   %N.type.loc35_32.2: type = facet_type <@N, @N(%T.loc35_14.2)> [symbolic = %N.type.loc35_32.2 (constants.%N.type.b8d23b.1)]
-// CHECK:STDOUT:   %I.impl_witness: <witness> = impl_witness file.%I.impl_witness_table.loc35, @C.as.I.impl.3f8440.1(%T.loc35_14.2) [symbolic = %I.impl_witness (constants.%I.impl_witness.524962.1)]
+// CHECK:STDOUT: generic impl @C.as.I.impl.3f8440.1(%T.loc32_14.1: type) {
+// CHECK:STDOUT:   %T.loc32_14.2: type = symbolic_binding T, 0 [symbolic = %T.loc32_14.2 (constants.%T)]
+// CHECK:STDOUT:   %N.type.loc32_32.2: type = facet_type <@N, @N(%T.loc32_14.2)> [symbolic = %N.type.loc32_32.2 (constants.%N.type.b8d23b.1)]
 // CHECK:STDOUT:
-// CHECK:STDOUT:   impl: %C.ref as %N.type.loc35_32.1;
+// CHECK:STDOUT:   impl: %C.ref as %N.type.loc32_32.1;
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: generic impl @C.as.I.impl.3f8440.2(%T.loc40_14.1: type) {
-// CHECK:STDOUT:   %T.loc40_14.2: type = symbolic_binding T, 0 [symbolic = %T.loc40_14.2 (constants.%T)]
-// CHECK:STDOUT:   %ptr.loc40_32.2: type = ptr_type %T.loc40_14.2 [symbolic = %ptr.loc40_32.2 (constants.%ptr)]
-// CHECK:STDOUT:   %N.type.loc40_33.2: type = facet_type <@N, @N(%ptr.loc40_32.2)> [symbolic = %N.type.loc40_33.2 (constants.%N.type.b8d23b.2)]
-// CHECK:STDOUT:   %I.impl_witness: <witness> = impl_witness file.%I.impl_witness_table.loc40, @C.as.I.impl.3f8440.2(%T.loc40_14.2) [symbolic = %I.impl_witness (constants.%I.impl_witness.524962.2)]
+// CHECK:STDOUT: generic impl @C.as.I.impl.3f8440.2(%T.loc37_14.1: type) {
+// CHECK:STDOUT:   %T.loc37_14.2: type = symbolic_binding T, 0 [symbolic = %T.loc37_14.2 (constants.%T)]
+// CHECK:STDOUT:   %ptr.loc37_32.2: type = ptr_type %T.loc37_14.2 [symbolic = %ptr.loc37_32.2 (constants.%ptr)]
+// CHECK:STDOUT:   %N.type.loc37_33.2: type = facet_type <@N, @N(%ptr.loc37_32.2)> [symbolic = %N.type.loc37_33.2 (constants.%N.type.b8d23b.2)]
 // CHECK:STDOUT:
-// CHECK:STDOUT:   impl: %C.ref as %N.type.loc40_33.1;
+// CHECK:STDOUT:   impl: %C.ref as %N.type.loc37_33.1;
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: class @C [from "import_generic.carbon"] {
@@ -1253,20 +1249,26 @@ impl forall [T:! type] D as N(T*) {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @C.as.I.impl.3f8440.1(constants.%T) {
-// CHECK:STDOUT:   %T.loc35_14.2 => constants.%T
-// CHECK:STDOUT:   %N.type.loc35_32.2 => constants.%N.type.b8d23b.1
-// CHECK:STDOUT:   %I.impl_witness => constants.%I.impl_witness.524962.1
+// CHECK:STDOUT:   %T.loc32_14.2 => constants.%T
+// CHECK:STDOUT:   %N.type.loc32_32.2 => constants.%N.type.b8d23b.1
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @N(constants.%ptr) {
 // CHECK:STDOUT:   %T => constants.%ptr
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: specific @N.require0(constants.%ptr, constants.%Self.aa1) {
+// CHECK:STDOUT:   %T => constants.%ptr
+// CHECK:STDOUT:   %N.type => constants.%N.type.b8d23b.2
+// CHECK:STDOUT:   %Self => constants.%Self.aa1
+// CHECK:STDOUT:   %Self.binding.as_type => constants.%Self.binding.as_type
+// CHECK:STDOUT:   %I.type => constants.%I.type.229
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
 // CHECK:STDOUT: specific @C.as.I.impl.3f8440.2(constants.%T) {
-// CHECK:STDOUT:   %T.loc40_14.2 => constants.%T
-// CHECK:STDOUT:   %ptr.loc40_32.2 => constants.%ptr
-// CHECK:STDOUT:   %N.type.loc40_33.2 => constants.%N.type.b8d23b.2
-// CHECK:STDOUT:   %I.impl_witness => constants.%I.impl_witness.524962.2
+// CHECK:STDOUT:   %T.loc37_14.2 => constants.%T
+// CHECK:STDOUT:   %ptr.loc37_32.2 => constants.%ptr
+// CHECK:STDOUT:   %N.type.loc37_33.2 => constants.%N.type.b8d23b.2
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- import_generic_with_different_specific.carbon
@@ -1460,8 +1462,8 @@ impl forall [T:! type] D as N(T*) {}
 // CHECK:STDOUT:   %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self.aa1546.1 [symbolic]
 // CHECK:STDOUT:   %ptr.3f2: type = ptr_type %ptr.4f0 [symbolic]
 // CHECK:STDOUT:   %N.type.b8d23b.2: type = facet_type <@N, @N(%ptr.3f2)> [symbolic]
-// CHECK:STDOUT:   %Self.aa1546.2: %N.type.b8d23b.2 = symbolic_binding Self, 1 [symbolic]
 // CHECK:STDOUT:   %I.type.71a: type = facet_type <@I, @I(%ptr.3f2)> [symbolic]
+// CHECK:STDOUT:   %Self.aa1546.2: %N.type.b8d23b.2 = symbolic_binding Self, 1 [symbolic]
 // CHECK:STDOUT:   %require_complete.08c: <witness> = require_complete_type %I.type.71a [symbolic]
 // CHECK:STDOUT:   %require_complete.a37: <witness> = require_complete_type %N.type.b8d23b.2 [symbolic]
 // CHECK:STDOUT:   %I.impl_witness.524: <witness> = impl_witness file.%I.impl_witness_table.loc6, @C.as.I.impl.3f8(%T) [symbolic]
@@ -1480,7 +1482,7 @@ impl forall [T:! type] D as N(T*) {}
 // CHECK:STDOUT:   %Main.import_ref.29a: type = import_ref Main//import_generic_with_different_specific, loc7_24, loaded [concrete = constants.%C]
 // CHECK:STDOUT:   %Main.import_ref.46444d.1: type = import_ref Main//import_generic_with_different_specific, loc7_32, loaded [symbolic = @C.as.I.impl.f3e.%I.type (constants.%I.type.070)]
 // CHECK:STDOUT:   %Main.import_ref.efcd44.2: type = import_ref Main//import_generic_with_different_specific, loc7_14, loaded [symbolic = @C.as.I.impl.f3e.%T (constants.%T)]
-// CHECK:STDOUT:   %Main.import_ref.f92: type = import_ref Main//import_generic_with_different_specific, loc10_18, loaded [symbolic = constants.%Self.binding.as_type]
+// CHECK:STDOUT:   %Main.import_ref.f92: type = import_ref Main//import_generic_with_different_specific, loc10_18, loaded [symbolic = @N.require0.%Self.binding.as_type (constants.%Self.binding.as_type)]
 // CHECK:STDOUT:   %Main.import_ref.efcd44.3: type = import_ref Main//import_generic_with_different_specific, loc9_14, loaded [symbolic = @N.%T (constants.%T)]
 // CHECK:STDOUT:   %Main.import_ref.d4d: @N.%N.type (%N.type.b8d23b.1) = import_ref Main//import_generic_with_different_specific, loc9_24, loaded [symbolic = @N.%Self (constants.%Self.aa1546.1)]
 // CHECK:STDOUT:   %Main.import_ref.388 = import_ref Main//import_generic_with_different_specific, loc9_24, unloaded
@@ -1673,6 +1675,18 @@ impl forall [T:! type] D as N(T*) {}
 // CHECK:STDOUT:   %require_complete => constants.%require_complete.08c
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: specific @N.require0(constants.%ptr.3f2, constants.%Self.aa1546.1) {
+// CHECK:STDOUT:   %T => constants.%ptr.3f2
+// CHECK:STDOUT:   %N.type => constants.%N.type.b8d23b.2
+// CHECK:STDOUT:   %Self => constants.%Self.aa1546.1
+// CHECK:STDOUT:   %Self.binding.as_type => constants.%Self.binding.as_type
+// CHECK:STDOUT:   %I.type => constants.%I.type.71a
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @I(constants.%ptr.3f2) {
+// CHECK:STDOUT:   %T => constants.%ptr.3f2
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
 // CHECK:STDOUT: specific @C.as.I.impl.3f8(constants.%T) {
 // CHECK:STDOUT:   %T.loc6_14.2 => constants.%T
 // CHECK:STDOUT:   %ptr.loc6_32.2 => constants.%ptr.4f0
@@ -1682,10 +1696,6 @@ impl forall [T:! type] D as N(T*) {}
 // CHECK:STDOUT:   %I.impl_witness => constants.%I.impl_witness.524
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: specific @I(constants.%ptr.3f2) {
-// CHECK:STDOUT:   %T => constants.%ptr.3f2
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
 // CHECK:STDOUT: --- fail_import_generic_decl.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
@@ -1899,16 +1909,10 @@ impl forall [T:! type] D as N(T*) {}
 // CHECK:STDOUT:   %N.type.673: type = generic_named_constaint_type @N [concrete]
 // CHECK:STDOUT:   %empty_struct: %N.type.673 = struct_value () [concrete]
 // CHECK:STDOUT:   %N.type.b8d23b.1: type = facet_type <@N, @N(%T)> [symbolic]
-// CHECK:STDOUT:   %Self.aa1546.1: %N.type.b8d23b.1 = symbolic_binding Self, 1 [symbolic]
-// CHECK:STDOUT:   %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self.aa1546.1 [symbolic]
-// CHECK:STDOUT:   %require_complete.387: <witness> = require_complete_type %J.type.8ec [symbolic]
-// CHECK:STDOUT:   %require_complete.a37d6c.1: <witness> = require_complete_type %N.type.b8d23b.1 [symbolic]
-// CHECK:STDOUT:   %J.impl_witness.b5245e.1: <witness> = impl_witness file.%J.impl_witness_table.loc29, @D.as.J.impl.3b0484.1(%T) [symbolic]
+// CHECK:STDOUT:   %Self.aa1: %N.type.b8d23b.1 = symbolic_binding Self, 1 [symbolic]
+// CHECK:STDOUT:   %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self.aa1 [symbolic]
+// CHECK:STDOUT:   %require_complete: <witness> = require_complete_type %J.type.8ec [symbolic]
 // CHECK:STDOUT:   %N.type.b8d23b.2: type = facet_type <@N, @N(%ptr)> [symbolic]
-// CHECK:STDOUT:   %Self.aa1546.2: %N.type.b8d23b.2 = symbolic_binding Self, 1 [symbolic]
-// CHECK:STDOUT:   %require_complete.d4d: <witness> = require_complete_type %J.type.4fa [symbolic]
-// CHECK:STDOUT:   %require_complete.a37d6c.2: <witness> = require_complete_type %N.type.b8d23b.2 [symbolic]
-// CHECK:STDOUT:   %J.impl_witness.b5245e.2: <witness> = impl_witness file.%J.impl_witness_table.loc39, @D.as.J.impl.3b0484.2(%T) [symbolic]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
@@ -1927,9 +1931,9 @@ impl forall [T:! type] D as N(T*) {}
 // CHECK:STDOUT:   %Main.import_ref.aa9f8a.2: type = import_ref Main//import_generic_decl, loc21_24, loaded [concrete = constants.%D]
 // CHECK:STDOUT:   %Main.import_ref.a00: type = import_ref Main//import_generic_decl, loc21_33, loaded [symbolic = @D.as.J.impl.265db6.1.%J.type (constants.%J.type.4fa)]
 // CHECK:STDOUT:   %Main.import_ref.efcd44.3: type = import_ref Main//import_generic_decl, loc21_14, loaded [symbolic = @D.as.J.impl.265db6.1.%T (constants.%T)]
-// CHECK:STDOUT:   %Main.import_ref.f92: type = import_ref Main//import_generic_decl, loc8_18, loaded [symbolic = constants.%Self.binding.as_type]
+// CHECK:STDOUT:   %Main.import_ref.f92: type = import_ref Main//import_generic_decl, loc8_18, loaded [symbolic = @N.require0.%Self.binding.as_type (constants.%Self.binding.as_type)]
 // CHECK:STDOUT:   %Main.import_ref.efcd44.4: type = import_ref Main//import_generic_decl, loc7_14, loaded [symbolic = @N.%T (constants.%T)]
-// CHECK:STDOUT:   %Main.import_ref.d4d: @N.%N.type (%N.type.b8d23b.1) = import_ref Main//import_generic_decl, loc7_24, loaded [symbolic = @N.%Self (constants.%Self.aa1546.1)]
+// CHECK:STDOUT:   %Main.import_ref.d4d: @N.%N.type (%N.type.b8d23b.1) = import_ref Main//import_generic_decl, loc7_24, loaded [symbolic = @N.%Self (constants.%Self.aa1)]
 // CHECK:STDOUT:   %Main.import_ref.388 = import_ref Main//import_generic_decl, loc7_24, unloaded
 // CHECK:STDOUT:   %Main.import_ref.efcd44.5: type = import_ref Main//import_generic_decl, loc7_14, loaded [symbolic = @N.%T (constants.%T)]
 // CHECK:STDOUT: }
@@ -1989,26 +1993,22 @@ impl forall [T:! type] D as N(T*) {}
 // CHECK:STDOUT:   } {
 // CHECK:STDOUT:     %D.ref: type = name_ref D, imports.%Main.D [concrete = constants.%D]
 // CHECK:STDOUT:     %N.ref: %N.type.673 = name_ref N, imports.%Main.N [concrete = constants.%empty_struct]
-// CHECK:STDOUT:     %T.ref: type = name_ref T, %T.loc29_14.1 [symbolic = %T.loc29_14.2 (constants.%T)]
-// CHECK:STDOUT:     %N.type.loc29_32.1: type = facet_type <@N, @N(constants.%T)> [symbolic = %N.type.loc29_32.2 (constants.%N.type.b8d23b.1)]
+// CHECK:STDOUT:     %T.ref: type = name_ref T, %T.loc32_14.1 [symbolic = %T.loc32_14.2 (constants.%T)]
+// CHECK:STDOUT:     %N.type.loc32_32.1: type = facet_type <@N, @N(constants.%T)> [symbolic = %N.type.loc32_32.2 (constants.%N.type.b8d23b.1)]
 // CHECK:STDOUT:     %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self]
-// CHECK:STDOUT:     %T.loc29_14.1: type = symbolic_binding T, 0 [symbolic = %T.loc29_14.2 (constants.%T)]
+// CHECK:STDOUT:     %T.loc32_14.1: type = symbolic_binding T, 0 [symbolic = %T.loc32_14.2 (constants.%T)]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %J.impl_witness_table.loc29 = impl_witness_table (), @D.as.J.impl.3b0484.1 [concrete]
-// CHECK:STDOUT:   %J.impl_witness.loc29: <witness> = impl_witness %J.impl_witness_table.loc29, @D.as.J.impl.3b0484.1(constants.%T) [symbolic = @D.as.J.impl.3b0484.1.%J.impl_witness (constants.%J.impl_witness.b5245e.1)]
 // CHECK:STDOUT:   impl_decl @D.as.J.impl.3b0484.2 [concrete] {
 // CHECK:STDOUT:     %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete]
 // CHECK:STDOUT:   } {
 // CHECK:STDOUT:     %D.ref: type = name_ref D, imports.%Main.D [concrete = constants.%D]
 // CHECK:STDOUT:     %N.ref: %N.type.673 = name_ref N, imports.%Main.N [concrete = constants.%empty_struct]
-// CHECK:STDOUT:     %T.ref: type = name_ref T, %T.loc39_14.1 [symbolic = %T.loc39_14.2 (constants.%T)]
-// CHECK:STDOUT:     %ptr.loc39_32.1: type = ptr_type %T.ref [symbolic = %ptr.loc39_32.2 (constants.%ptr)]
-// CHECK:STDOUT:     %N.type.loc39_33.1: type = facet_type <@N, @N(constants.%ptr)> [symbolic = %N.type.loc39_33.2 (constants.%N.type.b8d23b.2)]
+// CHECK:STDOUT:     %T.ref: type = name_ref T, %T.loc38_14.1 [symbolic = %T.loc38_14.2 (constants.%T)]
+// CHECK:STDOUT:     %ptr.loc38_32.1: type = ptr_type %T.ref [symbolic = %ptr.loc38_32.2 (constants.%ptr)]
+// CHECK:STDOUT:     %N.type.loc38_33.1: type = facet_type <@N, @N(constants.%ptr)> [symbolic = %N.type.loc38_33.2 (constants.%N.type.b8d23b.2)]
 // CHECK:STDOUT:     %.Self: %type = symbolic_binding .Self [symbolic_self = constants.%.Self]
-// CHECK:STDOUT:     %T.loc39_14.1: type = symbolic_binding T, 0 [symbolic = %T.loc39_14.2 (constants.%T)]
+// CHECK:STDOUT:     %T.loc38_14.1: type = symbolic_binding T, 0 [symbolic = %T.loc38_14.2 (constants.%T)]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %J.impl_witness_table.loc39 = impl_witness_table (), @D.as.J.impl.3b0484.2 [concrete]
-// CHECK:STDOUT:   %J.impl_witness.loc39: <witness> = impl_witness %J.impl_witness_table.loc39, @D.as.J.impl.3b0484.2(constants.%T) [symbolic = @D.as.J.impl.3b0484.2.%J.impl_witness (constants.%J.impl_witness.b5245e.2)]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: generic interface @J(imports.%Main.import_ref.efcd44.1: type) [from "fail_import_generic_decl.carbon"] {
@@ -2032,9 +2032,9 @@ impl forall [T:! type] D as N(T*) {}
 // CHECK:STDOUT:
 // CHECK:STDOUT: !definition:
 // CHECK:STDOUT:   %N.type: type = facet_type <@N, @N(%T)> [symbolic = %N.type (constants.%N.type.b8d23b.1)]
-// CHECK:STDOUT:   %Self: @N.%N.type (%N.type.b8d23b.1) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.aa1546.1)]
+// CHECK:STDOUT:   %Self: @N.%N.type (%N.type.b8d23b.1) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.aa1)]
 // CHECK:STDOUT:   %J.type: type = facet_type <@J, @J(%T)> [symbolic = %J.type (constants.%J.type.8ec)]
-// CHECK:STDOUT:   %require_complete: <witness> = require_complete_type %J.type [symbolic = %require_complete (constants.%require_complete.387)]
+// CHECK:STDOUT:   %require_complete: <witness> = require_complete_type %J.type [symbolic = %require_complete (constants.%require_complete)]
 // CHECK:STDOUT:
 // CHECK:STDOUT:   constraint {
 // CHECK:STDOUT:   !members:
@@ -2050,7 +2050,7 @@ impl forall [T:! type] D as N(T*) {}
 // CHECK:STDOUT: generic require @N.require0(imports.%Main.import_ref.efcd44.4: type, imports.%Main.import_ref.d4d: @N.%N.type (%N.type.b8d23b.1)) [from "fail_import_generic_decl.carbon"] {
 // CHECK:STDOUT:   %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)]
 // CHECK:STDOUT:   %N.type: type = facet_type <@N, @N(%T)> [symbolic = %N.type (constants.%N.type.b8d23b.1)]
-// CHECK:STDOUT:   %Self: @N.require0.%N.type (%N.type.b8d23b.1) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.aa1546.1)]
+// CHECK:STDOUT:   %Self: @N.require0.%N.type (%N.type.b8d23b.1) = symbolic_binding Self, 1 [symbolic = %Self (constants.%Self.aa1)]
 // CHECK:STDOUT:   %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)]
 // CHECK:STDOUT:   %J.type: type = facet_type <@J, @J(%T)> [symbolic = %J.type (constants.%J.type.8ec)]
 // CHECK:STDOUT: }
@@ -2112,32 +2112,28 @@ impl forall [T:! type] D as N(T*) {}
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: generic impl @D.as.J.impl.3b0484.1(%T.loc29_14.1: type) {
-// CHECK:STDOUT:   %T.loc29_14.2: type = symbolic_binding T, 0 [symbolic = %T.loc29_14.2 (constants.%T)]
-// CHECK:STDOUT:   %N.type.loc29_32.2: type = facet_type <@N, @N(%T.loc29_14.2)> [symbolic = %N.type.loc29_32.2 (constants.%N.type.b8d23b.1)]
-// CHECK:STDOUT:   %require_complete: <witness> = require_complete_type %N.type.loc29_32.2 [symbolic = %require_complete (constants.%require_complete.a37d6c.1)]
-// CHECK:STDOUT:   %J.impl_witness: <witness> = impl_witness file.%J.impl_witness_table.loc29, @D.as.J.impl.3b0484.1(%T.loc29_14.2) [symbolic = %J.impl_witness (constants.%J.impl_witness.b5245e.1)]
+// CHECK:STDOUT: generic impl @D.as.J.impl.3b0484.1(%T.loc32_14.1: type) {
+// CHECK:STDOUT:   %T.loc32_14.2: type = symbolic_binding T, 0 [symbolic = %T.loc32_14.2 (constants.%T)]
+// CHECK:STDOUT:   %N.type.loc32_32.2: type = facet_type <@N, @N(%T.loc32_14.2)> [symbolic = %N.type.loc32_32.2 (constants.%N.type.b8d23b.1)]
 // CHECK:STDOUT:
 // CHECK:STDOUT: !definition:
 // CHECK:STDOUT:
-// CHECK:STDOUT:   impl: %D.ref as %N.type.loc29_32.1 {
+// CHECK:STDOUT:   impl: %D.ref as %N.type.loc32_32.1 {
 // CHECK:STDOUT:   !members:
-// CHECK:STDOUT:     witness = file.%J.impl_witness.loc29
+// CHECK:STDOUT:     witness = <error>
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: generic impl @D.as.J.impl.3b0484.2(%T.loc39_14.1: type) {
-// CHECK:STDOUT:   %T.loc39_14.2: type = symbolic_binding T, 0 [symbolic = %T.loc39_14.2 (constants.%T)]
-// CHECK:STDOUT:   %ptr.loc39_32.2: type = ptr_type %T.loc39_14.2 [symbolic = %ptr.loc39_32.2 (constants.%ptr)]
-// CHECK:STDOUT:   %N.type.loc39_33.2: type = facet_type <@N, @N(%ptr.loc39_32.2)> [symbolic = %N.type.loc39_33.2 (constants.%N.type.b8d23b.2)]
-// CHECK:STDOUT:   %require_complete: <witness> = require_complete_type %N.type.loc39_33.2 [symbolic = %require_complete (constants.%require_complete.a37d6c.2)]
-// CHECK:STDOUT:   %J.impl_witness: <witness> = impl_witness file.%J.impl_witness_table.loc39, @D.as.J.impl.3b0484.2(%T.loc39_14.2) [symbolic = %J.impl_witness (constants.%J.impl_witness.b5245e.2)]
+// CHECK:STDOUT: generic impl @D.as.J.impl.3b0484.2(%T.loc38_14.1: type) {
+// CHECK:STDOUT:   %T.loc38_14.2: type = symbolic_binding T, 0 [symbolic = %T.loc38_14.2 (constants.%T)]
+// CHECK:STDOUT:   %ptr.loc38_32.2: type = ptr_type %T.loc38_14.2 [symbolic = %ptr.loc38_32.2 (constants.%ptr)]
+// CHECK:STDOUT:   %N.type.loc38_33.2: type = facet_type <@N, @N(%ptr.loc38_32.2)> [symbolic = %N.type.loc38_33.2 (constants.%N.type.b8d23b.2)]
 // CHECK:STDOUT:
 // CHECK:STDOUT: !definition:
 // CHECK:STDOUT:
-// CHECK:STDOUT:   impl: %D.ref as %N.type.loc39_33.1 {
+// CHECK:STDOUT:   impl: %D.ref as %N.type.loc38_33.1 {
 // CHECK:STDOUT:   !members:
-// CHECK:STDOUT:     witness = file.%J.impl_witness.loc39
+// CHECK:STDOUT:     witness = <error>
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -2197,44 +2193,36 @@ impl forall [T:! type] D as N(T*) {}
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @N(constants.%T) {
 // CHECK:STDOUT:   %T => constants.%T
-// CHECK:STDOUT:
-// CHECK:STDOUT: !definition:
-// CHECK:STDOUT:   %N.type => constants.%N.type.b8d23b.1
-// CHECK:STDOUT:   %Self => constants.%Self.aa1546.1
-// CHECK:STDOUT:   %J.type => constants.%J.type.8ec
-// CHECK:STDOUT:   %require_complete => constants.%require_complete.387
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: specific @N.require0(constants.%T, constants.%Self.aa1546.1) {
+// CHECK:STDOUT: specific @N.require0(constants.%T, constants.%Self.aa1) {
 // CHECK:STDOUT:   %T => constants.%T
 // CHECK:STDOUT:   %N.type => constants.%N.type.b8d23b.1
-// CHECK:STDOUT:   %Self => constants.%Self.aa1546.1
+// CHECK:STDOUT:   %Self => constants.%Self.aa1
 // CHECK:STDOUT:   %Self.binding.as_type => constants.%Self.binding.as_type
 // CHECK:STDOUT:   %J.type => constants.%J.type.8ec
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @D.as.J.impl.3b0484.1(constants.%T) {
-// CHECK:STDOUT:   %T.loc29_14.2 => constants.%T
-// CHECK:STDOUT:   %N.type.loc29_32.2 => constants.%N.type.b8d23b.1
-// CHECK:STDOUT:   %require_complete => constants.%require_complete.a37d6c.1
-// CHECK:STDOUT:   %J.impl_witness => constants.%J.impl_witness.b5245e.1
+// CHECK:STDOUT:   %T.loc32_14.2 => constants.%T
+// CHECK:STDOUT:   %N.type.loc32_32.2 => constants.%N.type.b8d23b.1
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @N(constants.%ptr) {
 // CHECK:STDOUT:   %T => constants.%ptr
+// CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: !definition:
+// CHECK:STDOUT: specific @N.require0(constants.%ptr, constants.%Self.aa1) {
+// CHECK:STDOUT:   %T => constants.%ptr
 // CHECK:STDOUT:   %N.type => constants.%N.type.b8d23b.2
-// CHECK:STDOUT:   %Self => constants.%Self.aa1546.2
+// CHECK:STDOUT:   %Self => constants.%Self.aa1
+// CHECK:STDOUT:   %Self.binding.as_type => constants.%Self.binding.as_type
 // CHECK:STDOUT:   %J.type => constants.%J.type.4fa
-// CHECK:STDOUT:   %require_complete => constants.%require_complete.d4d
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @D.as.J.impl.3b0484.2(constants.%T) {
-// CHECK:STDOUT:   %T.loc39_14.2 => constants.%T
-// CHECK:STDOUT:   %ptr.loc39_32.2 => constants.%ptr
-// CHECK:STDOUT:   %N.type.loc39_33.2 => constants.%N.type.b8d23b.2
-// CHECK:STDOUT:   %require_complete => constants.%require_complete.a37d6c.2
-// CHECK:STDOUT:   %J.impl_witness => constants.%J.impl_witness.b5245e.2
+// CHECK:STDOUT:   %T.loc38_14.2 => constants.%T
+// CHECK:STDOUT:   %ptr.loc38_32.2 => constants.%ptr
+// CHECK:STDOUT:   %N.type.loc38_33.2 => constants.%N.type.b8d23b.2
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 1 - 1
toolchain/check/testdata/named_constraint/import_constraint_decl.carbon

@@ -94,7 +94,7 @@ impl C as B {}
 // CHECK:STDOUT:   %Main.I = import_ref Main//b, I, unloaded
 // CHECK:STDOUT:   %Main.B: type = import_ref Main//b, B, loaded [concrete = constants.%B.type]
 // CHECK:STDOUT:   %Main.import_ref.8df = import_ref Main//b, loc3_13, unloaded
-// CHECK:STDOUT:   %Main.import_ref.b7c: type = import_ref Main//b, loc5_18, loaded [symbolic = constants.%Self.binding.as_type]
+// CHECK:STDOUT:   %Main.import_ref.b7c: type = import_ref Main//b, loc5_18, loaded [symbolic = @B.require0.%Self.binding.as_type (constants.%Self.binding.as_type)]
 // CHECK:STDOUT:   %Main.import_ref.dec: %B.type = import_ref Main//b, loc4_14, loaded [symbolic = constants.%Self.861]
 // CHECK:STDOUT:   %Main.import_ref.cae = import_ref Main//b, loc4_14, unloaded
 // CHECK:STDOUT: }

+ 71 - 8
toolchain/check/type_completion.cpp

@@ -806,7 +806,58 @@ static auto RequireIdentifiedNamedConstraints(
   return true;
 }
 
-auto RequireIdentifiedFacetType(Context& context,
+// Get the specific of a RequireImpls from the specific of its enclosing
+// interface or named constraint. Since a `require` declaration can not
+// introduce new generic bindings, the specific for the RequireImpls can be
+// constructed from the enclosing one.
+static auto GetRequireImplsSpecificFromEnclosingSpecific(
+    Context& context, SemIR::LocId loc_id, const SemIR::RequireImpls& require,
+    SemIR::SpecificId enclosing_specific_id) -> SemIR::SpecificId {
+  auto enclosing_specific_args_id =
+      context.specifics().GetArgsOrEmpty(enclosing_specific_id);
+  auto enclosing_specific_args =
+      context.inst_blocks().Get(enclosing_specific_args_id);
+  llvm::SmallVector<SemIR::InstId> arg_ids;
+  arg_ids.reserve(enclosing_specific_args.size() + 1);
+  // Start with the args from the enclosing specific.
+  llvm::append_range(arg_ids, enclosing_specific_args);
+
+  // Specifics inside an interface/constraint also include the `Self` of the
+  // enclosing entity. We copy that `Self` from the self-specific of the
+  // RequireImpls generic.
+  const auto& require_generic = context.generics().Get(require.generic_id);
+  const auto& require_self_specific =
+      context.specifics().Get(require_generic.self_specific_id);
+  auto require_self_specific_args =
+      context.inst_blocks().Get(require_self_specific.args_id);
+  // The last argument of a `require` generic is always `Self`, as `require` can
+  // not have any parameters of its own, only enclosing parameters.
+  auto self_inst_id = require_self_specific_args.back();
+  CARBON_CHECK(context.insts().Is<SemIR::SymbolicBinding>(self_inst_id));
+  arg_ids.push_back(self_inst_id);
+
+  return MakeSpecific(context, loc_id, require.generic_id, arg_ids);
+}
+
+// Returns the `facet_type` mapped into `specific_id`. If an error results, it
+// returns None. In particular, this can surface as a monomorphization error
+// where the facet type was valid as a symbolic but becomes invalid with some
+// concrete specific.
+static auto TryGetFacetTypeInSpecific(Context& context,
+                                      SemIR::InstId facet_type,
+                                      SemIR::SpecificId specific_id)
+    -> SemIR::FacetTypeId {
+  auto const_facet_type = SemIR::GetConstantValueInSpecific(
+      context.sem_ir(), specific_id, facet_type);
+  auto facet_type_in_specific = context.insts().TryGetAs<SemIR::FacetType>(
+      context.constant_values().GetInstId(const_facet_type));
+  if (!facet_type_in_specific.has_value()) {
+    return SemIR::FacetTypeId::None;
+  }
+  return facet_type_in_specific->facet_type_id;
+}
+
+auto RequireIdentifiedFacetType(Context& context, SemIR::LocId loc_id,
                                 const SemIR::FacetType& facet_type,
                                 MakeDiagnosticBuilderFn diagnoser)
     -> SemIR::IdentifiedFacetTypeId {
@@ -852,16 +903,22 @@ auto RequireIdentifiedFacetType(Context& context,
     }
     llvm::append_range(self_impls, facet_type_info.self_impls_constraints);
 
-    for (auto extend : facet_type_info.extend_named_constraints) {
+    for (auto extends : facet_type_info.extend_named_constraints) {
       const auto& constraint =
-          context.named_constraints().Get(extend.named_constraint_id);
+          context.named_constraints().Get(extends.named_constraint_id);
       for (auto require_impls_id : context.require_impls_blocks().Get(
                constraint.require_impls_block_id)) {
         const auto& require = context.require_impls().Get(require_impls_id);
-        if (facet_type_extends && require.extend_self) {
-          extend_facet_types.push_back(require.facet_type_id);
-        } else {
-          impls_facet_types.push_back(require.facet_type_id);
+        auto require_specific_id = GetRequireImplsSpecificFromEnclosingSpecific(
+            context, loc_id, require, extends.specific_id);
+        auto facet_type_id = TryGetFacetTypeInSpecific(
+            context, require.facet_type_inst_id, require_specific_id);
+        if (facet_type_id.has_value()) {
+          if (facet_type_extends && require.extend_self) {
+            extend_facet_types.push_back(facet_type_id);
+          } else {
+            impls_facet_types.push_back(facet_type_id);
+          }
         }
       }
     }
@@ -872,7 +929,13 @@ auto RequireIdentifiedFacetType(Context& context,
       for (auto require_impls_id : context.require_impls_blocks().Get(
                constraint.require_impls_block_id)) {
         const auto& require = context.require_impls().Get(require_impls_id);
-        impls_facet_types.push_back(require.facet_type_id);
+        auto require_specific_id = GetRequireImplsSpecificFromEnclosingSpecific(
+            context, loc_id, require, impls.specific_id);
+        auto facet_type_id = TryGetFacetTypeInSpecific(
+            context, require.facet_type_inst_id, require_specific_id);
+        if (facet_type_id.has_value()) {
+          impls_facet_types.push_back(facet_type_id);
+        }
       }
     }
   }

+ 1 - 1
toolchain/check/type_completion.h

@@ -72,7 +72,7 @@ auto AsConcreteType(Context& context, SemIR::TypeId type_id,
 // Requires the named constraints in the facet type to be complete, so that the
 // set of interfaces the facet type requires is known. Diagnoses an error and
 // returns None if any named constraint is not complete.
-auto RequireIdentifiedFacetType(Context& context,
+auto RequireIdentifiedFacetType(Context& context, SemIR::LocId loc_id,
                                 const SemIR::FacetType& facet_type,
                                 MakeDiagnosticBuilderFn diagnoser)
     -> SemIR::IdentifiedFacetTypeId;

+ 17 - 23
toolchain/sem_ir/impl.cpp

@@ -5,6 +5,7 @@
 #include "toolchain/sem_ir/impl.h"
 
 #include "toolchain/base/kind_switch.h"
+#include "toolchain/sem_ir/facet_type_info.h"
 #include "toolchain/sem_ir/file.h"
 #include "toolchain/sem_ir/specific_interface.h"
 #include "toolchain/sem_ir/specific_named_constraint.h"
@@ -16,33 +17,26 @@ ImplStore::ImplStore(File& sem_ir)
 
 auto ImplStore::GetOrAddLookupBucket(const Impl& impl) -> LookupBucketRef {
   auto self_id = sem_ir_.constant_values().GetConstantInstId(impl.self_id);
-  InterfaceId interface_id = InterfaceId::None;
-  SpecificId specific_id = SpecificId::None;
-  auto facet_type_id = TypeId::ForTypeConstant(
+  auto impl_as_interface = SpecificInterface::None;
+  auto facet_type_type_id = TypeId::ForTypeConstant(
       sem_ir_.constant_values().Get(impl.constraint_id));
-  if (auto facet_type = sem_ir_.types().TryGetAs<FacetType>(facet_type_id)) {
-    const FacetTypeInfo& facet_type_info =
-        sem_ir_.facet_types().Get(facet_type->facet_type_id);
-    if (auto single = facet_type_info.TryAsSingleExtend()) {
-      CARBON_KIND_SWITCH(*single) {
-        case CARBON_KIND(SemIR::SpecificInterface interface): {
-          interface_id = interface.interface_id;
-          specific_id = interface.specific_id;
-          break;
-        }
-        case CARBON_KIND(SemIR::SpecificNamedConstraint _): {
-          // TODO: Handle named constraints which resolve to a single interface
-          // in the IdentifiedFacetType.
-          break;
-        }
+  if (auto facet_type =
+          sem_ir_.types().TryGetAs<FacetType>(facet_type_type_id)) {
+    auto identified_id =
+        sem_ir_.identified_facet_types().TryGetId(facet_type->facet_type_id);
+    if (identified_id.has_value()) {
+      const auto& identified =
+          sem_ir_.identified_facet_types().Get(identified_id);
+      if (identified.is_valid_impl_as_target()) {
+        impl_as_interface = identified.impl_as_target_interface();
       }
     }
   }
-  return LookupBucketRef(
-      *this, lookup_
-                 .Insert(std::tuple{self_id, interface_id, specific_id},
-                         [] { return ImplOrLookupBucketId::None; })
-                 .value());
+  return LookupBucketRef(*this,
+                         lookup_
+                             .Insert(std::pair{self_id, impl_as_interface},
+                                     [] { return ImplOrLookupBucketId::None; })
+                             .value());
 }
 
 }  // namespace Carbon::SemIR

+ 3 - 2
toolchain/sem_ir/impl.h

@@ -5,6 +5,8 @@
 #ifndef CARBON_TOOLCHAIN_SEM_IR_IMPL_H_
 #define CARBON_TOOLCHAIN_SEM_IR_IMPL_H_
 
+#include <utility>
+
 #include "common/map.h"
 #include "toolchain/base/value_store.h"
 #include "toolchain/sem_ir/entity_with_params_base.h"
@@ -208,8 +210,7 @@ class ImplStore {
  private:
   File& sem_ir_;
   ValueStore<ImplId, Impl> values_;
-  Map<std::tuple<InstId, InterfaceId, SpecificId>, ImplOrLookupBucketId>
-      lookup_;
+  Map<std::pair<InstId, SpecificInterface>, ImplOrLookupBucketId> lookup_;
   // Buckets with at least 2 entries, which will be rare; see LookupBucketRef.
   llvm::SmallVector<llvm::SmallVector<ImplId, 2>> lookup_buckets_;
 };