Просмотр исходного кода

Deduce through FacetValue (#5158)

When the parameter is a deduced symbolic FacetValue, refering to a
BindSymbolicName, and the argument is a concrete FacetValue that would
match the FacetType requirements on the BindSymbolicName's type, we
currently do not deduce that the argument matches the parameter.

The argument is not _converted_ to the parameter type because they are
both FacetValues of the same FacetType type. However they are also not
equal constant values so the argument is not saved as a deduced match
for the parameter.

In order to accept the FacetValue, we need to consider them as
`deduce_through`, which attempts to deduce each of the fields in the
argument FacetValue against the fields in the parameter FacetValue.
This deduces that the argument's concrete type matches the symbolic
BindSymbolicName and its witnesses are the same.

Since the parameter is a FacetValue, its argument is not the type that
needs to be recorded as the deduced type for the binding. The
BindSymbolicName inside the parameter is the place that we need to find
the deduced type for the binding. So simply walking into the FacetValue
gets us to that position, where we eventually record the deduced
argument type as being the concrete type from the original argument
FacetValue.

Similarly, when determining what interfaces are satisfied by a
FacetValue for deduce, we want to use the full type available in the
FacetValue rather than just those from its FacetType. Determining
availability of interfaces here is equivalent to converting, and we want
converting a FacetValue to always work on the full available type info.
Only API access (member lookup) is restricted by a FacetValue to the
interfaces provided by its FacetType type.

---------

Co-authored-by: Richard Smith <richard@metafoo.co.uk>
Dana Jansens 1 год назад
Родитель
Сommit
11ae0e27ab

+ 2 - 0
toolchain/check/BUILD

@@ -48,6 +48,7 @@ cc_library(
         "subst.cpp",
         "type.cpp",
         "type_completion.cpp",
+        "type_structure.cpp",
     ],
     hdrs = [
         "action.h",
@@ -89,6 +90,7 @@ cc_library(
         "subst.h",
         "type.h",
         "type_completion.h",
+        "type_structure.h",
     ],
     deps = [
         ":node_stack",

+ 47 - 11
toolchain/check/impl_lookup.cpp

@@ -4,6 +4,9 @@
 
 #include "toolchain/check/impl_lookup.h"
 
+#include <algorithm>
+
+#include "toolchain/base/kind_switch.h"
 #include "toolchain/check/deduce.h"
 #include "toolchain/check/diagnostic_helpers.h"
 #include "toolchain/check/generic.h"
@@ -11,6 +14,7 @@
 #include "toolchain/check/inst.h"
 #include "toolchain/check/type.h"
 #include "toolchain/check/type_completion.h"
+#include "toolchain/check/type_structure.h"
 #include "toolchain/sem_ir/ids.h"
 #include "toolchain/sem_ir/impl.h"
 #include "toolchain/sem_ir/inst.h"
@@ -300,14 +304,26 @@ static auto FindWitnessInFacet(
   return SemIR::InstId::None;
 }
 
+// Finds the best impl among all available impls that provides the
+// `specific_interface` for the type in `type_const_id`, and returns a witness
+// for that impl. Returns `None` if no match was found.
 static auto FindWitnessInImpls(
     Context& context, SemIR::LocId loc_id,
     SemIR::ConstantId query_self_const_id,
     const SemIR::SpecificInterface& specific_interface) -> SemIR::InstId {
   auto& stack = context.impl_lookup_stack();
-  // TODO: Build this candidate list by matching against type structures to
-  // narrow it down.
-  llvm::SmallVector<std::pair<SemIR::ImplId, SemIR::InstId>> candidate_impl_ids;
+
+  struct CandidateImpl {
+    SemIR::ImplId impl_id;
+    SemIR::InstId loc_inst_id;
+    TypeStructure type_structure;
+  };
+
+  auto query_type_structure = BuildTypeStructure(
+      context, context.constant_values().GetInstId(query_self_const_id),
+      specific_interface);
+
+  llvm::SmallVector<CandidateImpl> candidate_impl_ids;
   for (auto [id, impl] : context.impls().enumerate()) {
     // If the impl's interface_id differs from the query, then this impl can not
     // possibly provide the queried interface.
@@ -338,18 +354,38 @@ static auto FindWitnessInImpls(
     }
     CARBON_CHECK(impl.witness_id.has_value());
 
-    candidate_impl_ids.push_back({id, impl.definition_id});
+    auto type_structure =
+        BuildTypeStructure(context, impl.self_id, impl.interface);
+    if (!query_type_structure.IsCompatibleWith(type_structure)) {
+      continue;
+    }
+
+    candidate_impl_ids.push_back(
+        {id, impl.definition_id, std::move(type_structure)});
   }
 
-  for (auto [impl_id, loc_inst_id] : candidate_impl_ids) {
-    stack.back().impl_loc = loc_inst_id;
+  auto compare = [](auto& lhs, auto& rhs) -> bool {
+    // TODO: Allow Carbon code to provide a priority ordering explicitly. For
+    // now they have all the same priority, so the priority is the order in
+    // which they are found in code.
+
+    // Sort by their type structures. Higher value in type structure comes
+    // first, so we use `>` comparison.
+    return lhs.type_structure > rhs.type_structure;
+  };
+  // Stable sort is used so that impls that are seen first are preferred when
+  // they have an equal priority ordering.
+  std::ranges::stable_sort(candidate_impl_ids, compare);
+
+  for (const auto& candidate : candidate_impl_ids) {
+    stack.back().impl_loc = candidate.loc_inst_id;
     // NOTE: GetWitnessIdForImpl() does deduction, which can cause new impls to
     // be imported, invalidating any pointer into `context.impls()`.
-    auto result_witness_id = GetWitnessIdForImpl(
-        context, loc_id, query_self_const_id, specific_interface, impl_id);
-    if (result_witness_id.has_value()) {
-      // We found a matching impl; don't keep looking for this interface.
-      return result_witness_id;
+    auto witness_id =
+        GetWitnessIdForImpl(context, loc_id, query_self_const_id,
+                            specific_interface, candidate.impl_id);
+    if (witness_id.has_value()) {
+      return witness_id;
     }
   }
   return SemIR::InstId::None;

+ 550 - 0
toolchain/check/testdata/function/generic/deduce.carbon

@@ -191,6 +191,49 @@ fn CallImplicitNotDeducible() {
   ImplicitNotDeducible(42, {.x = 12});
 }
 
+// --- deduce_nested_generic_class.carbon
+library "[[@TEST_NAME]]";
+
+interface Z {}
+
+class EE {}
+impl EE as Z {}
+
+class DD(E:! type) {}
+impl forall [E:! type] DD(E) as Z {}
+
+class CC(D:! Z) {}
+impl forall [E:! type] CC(DD(E)) as Z {}
+
+fn F() {
+  CC(DD(EE)) as Z;
+}
+
+// --- deduce_nested_facet_value.carbon
+library "[[@TEST_NAME]]";
+
+interface Y {}
+interface W {}
+
+// DD implements both Y and W.
+class DD {}
+impl DD as Y {}
+impl DD as W {}
+
+// CC requires D to implement Y.
+class CC(D:! Y) {}
+
+interface Z {}
+
+// The `D` interface provides `Y` but not `W`, so we need to see that the
+// parameter to `CC` is `DD` which provides `Y & W`, not just a FacetValue
+// abstractly providing `Y.
+impl forall [E:! Y & W] CC(E) as Z {}
+
+fn F() {
+  (CC(DD)) as Z;
+}
+
 // CHECK:STDOUT: --- deduce_explicit.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
@@ -1456,3 +1499,510 @@ fn CallImplicitNotDeducible() {
 // CHECK:STDOUT:   %T.patt.loc4_25.2 => constants.%T
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: --- deduce_nested_generic_class.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %Z.type: type = facet_type <@Z> [concrete]
+// CHECK:STDOUT:   %Self: %Z.type = bind_symbolic_name Self, 0 [symbolic]
+// CHECK:STDOUT:   %EE: type = class_type @EE [concrete]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete]
+// CHECK:STDOUT:   %impl_witness.1bc: <witness> = impl_witness () [concrete]
+// CHECK:STDOUT:   %E: type = bind_symbolic_name E, 0 [symbolic]
+// CHECK:STDOUT:   %E.patt: type = symbolic_binding_pattern E, 0 [symbolic]
+// CHECK:STDOUT:   %DD.type: type = generic_class_type @DD [concrete]
+// CHECK:STDOUT:   %DD.generic: %DD.type = struct_value () [concrete]
+// CHECK:STDOUT:   %DD.296: type = class_type @DD, @DD(%E) [symbolic]
+// CHECK:STDOUT:   %impl_witness.d1b: <witness> = impl_witness (), @impl.266(%E) [symbolic]
+// CHECK:STDOUT:   %D: %Z.type = bind_symbolic_name D, 0 [symbolic]
+// CHECK:STDOUT:   %D.patt: %Z.type = symbolic_binding_pattern D, 0 [symbolic]
+// CHECK:STDOUT:   %CC.type: type = generic_class_type @CC [concrete]
+// CHECK:STDOUT:   %CC.generic: %CC.type = struct_value () [concrete]
+// CHECK:STDOUT:   %CC.a2f: type = class_type @CC, @CC(%D) [symbolic]
+// CHECK:STDOUT:   %Z.facet.42e: %Z.type = facet_value %DD.296, (%impl_witness.d1b) [symbolic]
+// CHECK:STDOUT:   %CC.379: type = class_type @CC, @CC(%Z.facet.42e) [symbolic]
+// CHECK:STDOUT:   %impl_witness.b93: <witness> = impl_witness (), @impl.c35(%E) [symbolic]
+// CHECK:STDOUT:   %F.type: type = fn_type @F [concrete]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [concrete]
+// CHECK:STDOUT:   %DD.689: type = class_type @DD, @DD(%EE) [concrete]
+// CHECK:STDOUT:   %impl_witness.f62: <witness> = impl_witness (), @impl.266(%EE) [concrete]
+// CHECK:STDOUT:   %Z.facet.85b: %Z.type = facet_value %DD.689, (%impl_witness.f62) [concrete]
+// CHECK:STDOUT:   %CC.4de: type = class_type @CC, @CC(%Z.facet.85b) [concrete]
+// CHECK:STDOUT:   %impl_witness.baa: <witness> = impl_witness (), @impl.c35(%EE) [concrete]
+// CHECK:STDOUT:   %Z.facet.fb6: %Z.type = facet_value %CC.4de, (%impl_witness.baa) [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [concrete] {
+// CHECK:STDOUT:     import Core//prelude
+// 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:     .Z = %Z.decl
+// CHECK:STDOUT:     .EE = %EE.decl
+// CHECK:STDOUT:     .DD = %DD.decl
+// CHECK:STDOUT:     .CC = %CC.decl
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core.import = import Core
+// CHECK:STDOUT:   %Z.decl: type = interface_decl @Z [concrete = constants.%Z.type] {} {}
+// CHECK:STDOUT:   %EE.decl: type = class_decl @EE [concrete = constants.%EE] {} {}
+// CHECK:STDOUT:   impl_decl @impl.a6b [concrete] {} {
+// CHECK:STDOUT:     %EE.ref: type = name_ref EE, file.%EE.decl [concrete = constants.%EE]
+// CHECK:STDOUT:     %Z.ref: type = name_ref Z, file.%Z.decl [concrete = constants.%Z.type]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %impl_witness.loc6: <witness> = impl_witness () [concrete = constants.%impl_witness.1bc]
+// CHECK:STDOUT:   %DD.decl: %DD.type = class_decl @DD [concrete = constants.%DD.generic] {
+// CHECK:STDOUT:     %E.patt.loc8_10.1: type = symbolic_binding_pattern E, 0 [symbolic = %E.patt.loc8_10.2 (constants.%E.patt)]
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %E.loc8_10.1: type = bind_symbolic_name E, 0 [symbolic = %E.loc8_10.2 (constants.%E)]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   impl_decl @impl.266 [concrete] {
+// CHECK:STDOUT:     %E.patt.loc9_14.1: type = symbolic_binding_pattern E, 0 [symbolic = %E.patt.loc9_14.2 (constants.%E.patt)]
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %DD.ref: %DD.type = name_ref DD, file.%DD.decl [concrete = constants.%DD.generic]
+// CHECK:STDOUT:     %E.ref: type = name_ref E, %E.loc9_14.1 [symbolic = %E.loc9_14.2 (constants.%E)]
+// CHECK:STDOUT:     %DD.loc9_28.1: type = class_type @DD, @DD(constants.%E) [symbolic = %DD.loc9_28.2 (constants.%DD.296)]
+// CHECK:STDOUT:     %Z.ref: type = name_ref Z, file.%Z.decl [concrete = constants.%Z.type]
+// CHECK:STDOUT:     %E.loc9_14.1: type = bind_symbolic_name E, 0 [symbolic = %E.loc9_14.2 (constants.%E)]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %impl_witness.loc9: <witness> = impl_witness (), @impl.266(constants.%E) [symbolic = @impl.266.%impl_witness (constants.%impl_witness.d1b)]
+// CHECK:STDOUT:   %CC.decl: %CC.type = class_decl @CC [concrete = constants.%CC.generic] {
+// CHECK:STDOUT:     %D.patt.loc11_10.1: %Z.type = symbolic_binding_pattern D, 0 [symbolic = %D.patt.loc11_10.2 (constants.%D.patt)]
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %Z.ref: type = name_ref Z, file.%Z.decl [concrete = constants.%Z.type]
+// CHECK:STDOUT:     %D.loc11_10.1: %Z.type = bind_symbolic_name D, 0 [symbolic = %D.loc11_10.2 (constants.%D)]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   impl_decl @impl.c35 [concrete] {
+// CHECK:STDOUT:     %E.patt.loc12_14.1: type = symbolic_binding_pattern E, 0 [symbolic = %E.patt.loc12_14.2 (constants.%E.patt)]
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %CC.ref: %CC.type = name_ref CC, file.%CC.decl [concrete = constants.%CC.generic]
+// CHECK:STDOUT:     %DD.ref: %DD.type = name_ref DD, file.%DD.decl [concrete = constants.%DD.generic]
+// CHECK:STDOUT:     %E.ref: type = name_ref E, %E.loc12_14.1 [symbolic = %E.loc12_14.2 (constants.%E)]
+// CHECK:STDOUT:     %DD.loc12_31.1: type = class_type @DD, @DD(constants.%E) [symbolic = %DD.loc12_31.2 (constants.%DD.296)]
+// CHECK:STDOUT:     %Z.facet.loc12_32.1: %Z.type = facet_value constants.%DD.296, (constants.%impl_witness.d1b) [symbolic = %Z.facet.loc12_32.2 (constants.%Z.facet.42e)]
+// CHECK:STDOUT:     %.loc12: %Z.type = converted %DD.loc12_31.1, %Z.facet.loc12_32.1 [symbolic = %Z.facet.loc12_32.2 (constants.%Z.facet.42e)]
+// CHECK:STDOUT:     %CC.loc12_32.1: type = class_type @CC, @CC(constants.%Z.facet.42e) [symbolic = %CC.loc12_32.2 (constants.%CC.379)]
+// CHECK:STDOUT:     %Z.ref: type = name_ref Z, file.%Z.decl [concrete = constants.%Z.type]
+// CHECK:STDOUT:     %E.loc12_14.1: type = bind_symbolic_name E, 0 [symbolic = %E.loc12_14.2 (constants.%E)]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %impl_witness.loc12: <witness> = impl_witness (), @impl.c35(constants.%E) [symbolic = @impl.c35.%impl_witness.loc12_39 (constants.%impl_witness.b93)]
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Z {
+// CHECK:STDOUT:   %Self: %Z.type = bind_symbolic_name Self, 0 [symbolic = constants.%Self]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   witness = ()
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl.a6b: %EE.ref as %Z.ref {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   witness = file.%impl_witness.loc6
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic impl @impl.266(%E.loc9_14.1: type) {
+// CHECK:STDOUT:   %E.loc9_14.2: type = bind_symbolic_name E, 0 [symbolic = %E.loc9_14.2 (constants.%E)]
+// CHECK:STDOUT:   %E.patt.loc9_14.2: type = symbolic_binding_pattern E, 0 [symbolic = %E.patt.loc9_14.2 (constants.%E.patt)]
+// CHECK:STDOUT:   %DD.loc9_28.2: type = class_type @DD, @DD(%E.loc9_14.2) [symbolic = %DD.loc9_28.2 (constants.%DD.296)]
+// CHECK:STDOUT:   %impl_witness: <witness> = impl_witness (), @impl.266(%E.loc9_14.2) [symbolic = %impl_witness (constants.%impl_witness.d1b)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:
+// CHECK:STDOUT:   impl: %DD.loc9_28.1 as %Z.ref {
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     witness = file.%impl_witness.loc9
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic impl @impl.c35(%E.loc12_14.1: type) {
+// CHECK:STDOUT:   %E.loc12_14.2: type = bind_symbolic_name E, 0 [symbolic = %E.loc12_14.2 (constants.%E)]
+// CHECK:STDOUT:   %E.patt.loc12_14.2: type = symbolic_binding_pattern E, 0 [symbolic = %E.patt.loc12_14.2 (constants.%E.patt)]
+// CHECK:STDOUT:   %DD.loc12_31.2: type = class_type @DD, @DD(%E.loc12_14.2) [symbolic = %DD.loc12_31.2 (constants.%DD.296)]
+// CHECK:STDOUT:   %impl_witness.loc12_32: <witness> = impl_witness (), @impl.266(%E.loc12_14.2) [symbolic = %impl_witness.loc12_32 (constants.%impl_witness.d1b)]
+// CHECK:STDOUT:   %Z.facet.loc12_32.2: %Z.type = facet_value %DD.loc12_31.2, (%impl_witness.loc12_32) [symbolic = %Z.facet.loc12_32.2 (constants.%Z.facet.42e)]
+// CHECK:STDOUT:   %CC.loc12_32.2: type = class_type @CC, @CC(%Z.facet.loc12_32.2) [symbolic = %CC.loc12_32.2 (constants.%CC.379)]
+// CHECK:STDOUT:   %impl_witness.loc12_39: <witness> = impl_witness (), @impl.c35(%E.loc12_14.2) [symbolic = %impl_witness.loc12_39 (constants.%impl_witness.b93)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:
+// CHECK:STDOUT:   impl: %CC.loc12_32.1 as %Z.ref {
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     witness = file.%impl_witness.loc12
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @EE {
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:   complete_type_witness = %complete_type
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%EE
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic class @DD(%E.loc8_10.1: type) {
+// CHECK:STDOUT:   %E.loc8_10.2: type = bind_symbolic_name E, 0 [symbolic = %E.loc8_10.2 (constants.%E)]
+// CHECK:STDOUT:   %E.patt.loc8_10.2: type = symbolic_binding_pattern E, 0 [symbolic = %E.patt.loc8_10.2 (constants.%E.patt)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:
+// CHECK:STDOUT:   class {
+// CHECK:STDOUT:     %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:     complete_type_witness = %complete_type
+// CHECK:STDOUT:
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     .Self = constants.%DD.296
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic class @CC(%D.loc11_10.1: %Z.type) {
+// CHECK:STDOUT:   %D.loc11_10.2: %Z.type = bind_symbolic_name D, 0 [symbolic = %D.loc11_10.2 (constants.%D)]
+// CHECK:STDOUT:   %D.patt.loc11_10.2: %Z.type = symbolic_binding_pattern D, 0 [symbolic = %D.patt.loc11_10.2 (constants.%D.patt)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:
+// CHECK:STDOUT:   class {
+// CHECK:STDOUT:     %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:     complete_type_witness = %complete_type
+// CHECK:STDOUT:
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     .Self = constants.%CC.a2f
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %CC.ref: %CC.type = name_ref CC, file.%CC.decl [concrete = constants.%CC.generic]
+// CHECK:STDOUT:   %DD.ref: %DD.type = name_ref DD, file.%DD.decl [concrete = constants.%DD.generic]
+// CHECK:STDOUT:   %EE.ref: type = name_ref EE, file.%EE.decl [concrete = constants.%EE]
+// CHECK:STDOUT:   %DD: type = class_type @DD, @DD(constants.%EE) [concrete = constants.%DD.689]
+// CHECK:STDOUT:   %Z.facet.loc15_12: %Z.type = facet_value constants.%DD.689, (constants.%impl_witness.f62) [concrete = constants.%Z.facet.85b]
+// CHECK:STDOUT:   %.loc15_12: %Z.type = converted %DD, %Z.facet.loc15_12 [concrete = constants.%Z.facet.85b]
+// CHECK:STDOUT:   %CC: type = class_type @CC, @CC(constants.%Z.facet.85b) [concrete = constants.%CC.4de]
+// CHECK:STDOUT:   %Z.ref: type = name_ref Z, file.%Z.decl [concrete = constants.%Z.type]
+// CHECK:STDOUT:   %Z.facet.loc15_14: %Z.type = facet_value constants.%CC.4de, (constants.%impl_witness.baa) [concrete = constants.%Z.facet.fb6]
+// CHECK:STDOUT:   %.loc15_14: %Z.type = converted %CC, %Z.facet.loc15_14 [concrete = constants.%Z.facet.fb6]
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @DD(constants.%E) {
+// CHECK:STDOUT:   %E.loc8_10.2 => constants.%E
+// CHECK:STDOUT:   %E.patt.loc8_10.2 => constants.%E
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @impl.266(constants.%E) {
+// CHECK:STDOUT:   %E.loc9_14.2 => constants.%E
+// CHECK:STDOUT:   %E.patt.loc9_14.2 => constants.%E
+// CHECK:STDOUT:   %DD.loc9_28.2 => constants.%DD.296
+// CHECK:STDOUT:   %impl_witness => constants.%impl_witness.d1b
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @DD(@impl.266.%E.loc9_14.2) {}
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @impl.266(%E.loc9_14.2) {}
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @CC(constants.%D) {
+// CHECK:STDOUT:   %D.loc11_10.2 => constants.%D
+// CHECK:STDOUT:   %D.patt.loc11_10.2 => constants.%D
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @CC(constants.%Z.facet.42e) {
+// CHECK:STDOUT:   %D.loc11_10.2 => constants.%Z.facet.42e
+// CHECK:STDOUT:   %D.patt.loc11_10.2 => constants.%Z.facet.42e
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @impl.c35(constants.%E) {
+// CHECK:STDOUT:   %E.loc12_14.2 => constants.%E
+// CHECK:STDOUT:   %E.patt.loc12_14.2 => constants.%E
+// CHECK:STDOUT:   %DD.loc12_31.2 => constants.%DD.296
+// CHECK:STDOUT:   %impl_witness.loc12_32 => constants.%impl_witness.d1b
+// CHECK:STDOUT:   %Z.facet.loc12_32.2 => constants.%Z.facet.42e
+// CHECK:STDOUT:   %CC.loc12_32.2 => constants.%CC.379
+// CHECK:STDOUT:   %impl_witness.loc12_39 => constants.%impl_witness.b93
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @DD(@impl.c35.%E.loc12_14.2) {}
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @impl.266(@impl.c35.%E.loc12_14.2) {}
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @CC(@impl.c35.%Z.facet.loc12_32.2) {}
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @impl.c35(%E.loc12_14.2) {}
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @DD(constants.%EE) {
+// CHECK:STDOUT:   %E.loc8_10.2 => constants.%EE
+// CHECK:STDOUT:   %E.patt.loc8_10.2 => constants.%EE
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @impl.266(constants.%EE) {
+// CHECK:STDOUT:   %E.loc9_14.2 => constants.%EE
+// CHECK:STDOUT:   %E.patt.loc9_14.2 => constants.%EE
+// CHECK:STDOUT:   %DD.loc9_28.2 => constants.%DD.689
+// CHECK:STDOUT:   %impl_witness => constants.%impl_witness.f62
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @CC(constants.%Z.facet.85b) {
+// CHECK:STDOUT:   %D.loc11_10.2 => constants.%Z.facet.85b
+// CHECK:STDOUT:   %D.patt.loc11_10.2 => constants.%Z.facet.85b
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @impl.c35(constants.%EE) {
+// CHECK:STDOUT:   %E.loc12_14.2 => constants.%EE
+// CHECK:STDOUT:   %E.patt.loc12_14.2 => constants.%EE
+// CHECK:STDOUT:   %DD.loc12_31.2 => constants.%DD.689
+// CHECK:STDOUT:   %impl_witness.loc12_32 => constants.%impl_witness.f62
+// CHECK:STDOUT:   %Z.facet.loc12_32.2 => constants.%Z.facet.85b
+// CHECK:STDOUT:   %CC.loc12_32.2 => constants.%CC.4de
+// CHECK:STDOUT:   %impl_witness.loc12_39 => constants.%impl_witness.baa
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- deduce_nested_facet_value.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %Y.type: type = facet_type <@Y> [concrete]
+// CHECK:STDOUT:   %Self.b29: %Y.type = bind_symbolic_name Self, 0 [symbolic]
+// CHECK:STDOUT:   %W.type: type = facet_type <@W> [concrete]
+// CHECK:STDOUT:   %Self.f12: %W.type = bind_symbolic_name Self, 0 [symbolic]
+// CHECK:STDOUT:   %DD: type = class_type @DD [concrete]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete]
+// CHECK:STDOUT:   %impl_witness.1bc: <witness> = impl_witness () [concrete]
+// CHECK:STDOUT:   %D: %Y.type = bind_symbolic_name D, 0 [symbolic]
+// CHECK:STDOUT:   %D.patt: %Y.type = symbolic_binding_pattern D, 0 [symbolic]
+// CHECK:STDOUT:   %CC.type: type = generic_class_type @CC [concrete]
+// CHECK:STDOUT:   %CC.generic: %CC.type = struct_value () [concrete]
+// CHECK:STDOUT:   %CC.3ba: type = class_type @CC, @CC(%D) [symbolic]
+// CHECK:STDOUT:   %Z.type: type = facet_type <@Z> [concrete]
+// CHECK:STDOUT:   %Self.6e6: %Z.type = bind_symbolic_name Self, 0 [symbolic]
+// CHECK:STDOUT:   %BitAnd.type: type = facet_type <@BitAnd> [concrete]
+// CHECK:STDOUT:   %Op.type.27a: type = fn_type @Op.1 [concrete]
+// CHECK:STDOUT:   %impl_witness.ec2: <witness> = impl_witness (imports.%Core.import_ref.d90) [concrete]
+// CHECK:STDOUT:   %BitAnd.facet: %BitAnd.type = facet_value type, (%impl_witness.ec2) [concrete]
+// CHECK:STDOUT:   %.3a2: type = fn_type_with_self_type %Op.type.27a, %BitAnd.facet [concrete]
+// CHECK:STDOUT:   %Op.type.c1b: type = fn_type @Op.2 [concrete]
+// CHECK:STDOUT:   %Op.0a6: %Op.type.c1b = struct_value () [concrete]
+// CHECK:STDOUT:   %Op.bound: <bound method> = bound_method %Y.type, %Op.0a6 [concrete]
+// CHECK:STDOUT:   %facet_type: type = facet_type <@Y & @W> [concrete]
+// CHECK:STDOUT:   %E: %facet_type = bind_symbolic_name E, 0 [symbolic]
+// CHECK:STDOUT:   %E.patt: %facet_type = symbolic_binding_pattern E, 0 [symbolic]
+// CHECK:STDOUT:   %E.as_wit.iface0: <witness> = facet_access_witness %E, element0 [symbolic]
+// CHECK:STDOUT:   %E.as_type: type = facet_access_type %E [symbolic]
+// CHECK:STDOUT:   %Y.facet.15a: %Y.type = facet_value %E.as_type, (%E.as_wit.iface0) [symbolic]
+// CHECK:STDOUT:   %CC.ed1: type = class_type @CC, @CC(%Y.facet.15a) [symbolic]
+// CHECK:STDOUT:   %impl_witness.b63: <witness> = impl_witness (), @impl.0ce(%E) [symbolic]
+// CHECK:STDOUT:   %F.type: type = fn_type @F [concrete]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [concrete]
+// CHECK:STDOUT:   %Y.facet.885: %Y.type = facet_value %DD, (%impl_witness.1bc) [concrete]
+// CHECK:STDOUT:   %CC.49f: type = class_type @CC, @CC(%Y.facet.885) [concrete]
+// CHECK:STDOUT:   %facet_value: %facet_type = facet_value %DD, (%impl_witness.1bc, %impl_witness.1bc) [concrete]
+// CHECK:STDOUT:   %impl_witness.454: <witness> = impl_witness (), @impl.0ce(%facet_value) [concrete]
+// CHECK:STDOUT:   %Z.facet: %Z.type = facet_value %CC.49f, (%impl_witness.454) [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [concrete] {
+// CHECK:STDOUT:     .BitAnd = %Core.BitAnd
+// CHECK:STDOUT:     import Core//prelude
+// 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:     .Y = %Y.decl
+// CHECK:STDOUT:     .W = %W.decl
+// CHECK:STDOUT:     .DD = %DD.decl
+// CHECK:STDOUT:     .CC = %CC.decl
+// CHECK:STDOUT:     .Z = %Z.decl
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core.import = import Core
+// CHECK:STDOUT:   %Y.decl: type = interface_decl @Y [concrete = constants.%Y.type] {} {}
+// CHECK:STDOUT:   %W.decl: type = interface_decl @W [concrete = constants.%W.type] {} {}
+// CHECK:STDOUT:   %DD.decl: type = class_decl @DD [concrete = constants.%DD] {} {}
+// CHECK:STDOUT:   impl_decl @impl.3d7 [concrete] {} {
+// CHECK:STDOUT:     %DD.ref: type = name_ref DD, file.%DD.decl [concrete = constants.%DD]
+// CHECK:STDOUT:     %Y.ref: type = name_ref Y, file.%Y.decl [concrete = constants.%Y.type]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %impl_witness.loc8: <witness> = impl_witness () [concrete = constants.%impl_witness.1bc]
+// CHECK:STDOUT:   impl_decl @impl.e11 [concrete] {} {
+// CHECK:STDOUT:     %DD.ref: type = name_ref DD, file.%DD.decl [concrete = constants.%DD]
+// CHECK:STDOUT:     %W.ref: type = name_ref W, file.%W.decl [concrete = constants.%W.type]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %impl_witness.loc9: <witness> = impl_witness () [concrete = constants.%impl_witness.1bc]
+// CHECK:STDOUT:   %CC.decl: %CC.type = class_decl @CC [concrete = constants.%CC.generic] {
+// CHECK:STDOUT:     %D.patt.loc12_10.1: %Y.type = symbolic_binding_pattern D, 0 [symbolic = %D.patt.loc12_10.2 (constants.%D.patt)]
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %Y.ref: type = name_ref Y, file.%Y.decl [concrete = constants.%Y.type]
+// CHECK:STDOUT:     %D.loc12_10.1: %Y.type = bind_symbolic_name D, 0 [symbolic = %D.loc12_10.2 (constants.%D)]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Z.decl: type = interface_decl @Z [concrete = constants.%Z.type] {} {}
+// CHECK:STDOUT:   impl_decl @impl.0ce [concrete] {
+// CHECK:STDOUT:     %E.patt.loc19_14.1: %facet_type = symbolic_binding_pattern E, 0 [symbolic = %E.patt.loc19_14.2 (constants.%E.patt)]
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %CC.ref: %CC.type = name_ref CC, file.%CC.decl [concrete = constants.%CC.generic]
+// CHECK:STDOUT:     %E.ref: %facet_type = name_ref E, %E.loc19_14.1 [symbolic = %E.loc19_14.2 (constants.%E)]
+// CHECK:STDOUT:     %E.as_wit.iface0.loc19_29.1: <witness> = facet_access_witness constants.%E, element0 [symbolic = %E.as_wit.iface0.loc19_29.2 (constants.%E.as_wit.iface0)]
+// CHECK:STDOUT:     %E.as_type.loc19_29.1: type = facet_access_type constants.%E [symbolic = %E.as_type.loc19_29.2 (constants.%E.as_type)]
+// CHECK:STDOUT:     %Y.facet.loc19_29.1: %Y.type = facet_value %E.as_type.loc19_29.1, (%E.as_wit.iface0.loc19_29.1) [symbolic = %Y.facet.loc19_29.2 (constants.%Y.facet.15a)]
+// CHECK:STDOUT:     %.loc19_29: %Y.type = converted %E.ref, %Y.facet.loc19_29.1 [symbolic = %Y.facet.loc19_29.2 (constants.%Y.facet.15a)]
+// CHECK:STDOUT:     %CC.loc19_29.1: type = class_type @CC, @CC(constants.%Y.facet.15a) [symbolic = %CC.loc19_29.2 (constants.%CC.ed1)]
+// CHECK:STDOUT:     %Z.ref: type = name_ref Z, file.%Z.decl [concrete = constants.%Z.type]
+// CHECK:STDOUT:     %.loc19_20.1: type = splice_block %.loc19_20.3 [concrete = constants.%facet_type] {
+// CHECK:STDOUT:       %Y.ref: type = name_ref Y, file.%Y.decl [concrete = constants.%Y.type]
+// CHECK:STDOUT:       %W.ref: type = name_ref W, file.%W.decl [concrete = constants.%W.type]
+// CHECK:STDOUT:       %impl.elem0: %.3a2 = impl_witness_access constants.%impl_witness.ec2, element0 [concrete = constants.%Op.0a6]
+// CHECK:STDOUT:       %bound_method: <bound method> = bound_method %Y.ref, %impl.elem0 [concrete = constants.%Op.bound]
+// CHECK:STDOUT:       %type.and: init type = call %bound_method(%Y.ref, %W.ref) [concrete = constants.%facet_type]
+// CHECK:STDOUT:       %.loc19_20.2: type = value_of_initializer %type.and [concrete = constants.%facet_type]
+// CHECK:STDOUT:       %.loc19_20.3: type = converted %type.and, %.loc19_20.2 [concrete = constants.%facet_type]
+// CHECK:STDOUT:     }
+// CHECK:STDOUT:     %E.loc19_14.1: %facet_type = bind_symbolic_name E, 0 [symbolic = %E.loc19_14.2 (constants.%E)]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %impl_witness.loc19: <witness> = impl_witness (), @impl.0ce(constants.%E) [symbolic = @impl.0ce.%impl_witness (constants.%impl_witness.b63)]
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Y {
+// CHECK:STDOUT:   %Self: %Y.type = bind_symbolic_name Self, 0 [symbolic = constants.%Self.b29]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   witness = ()
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @W {
+// CHECK:STDOUT:   %Self: %W.type = bind_symbolic_name Self, 0 [symbolic = constants.%Self.f12]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   witness = ()
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Z {
+// CHECK:STDOUT:   %Self: %Z.type = bind_symbolic_name Self, 0 [symbolic = constants.%Self.6e6]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   witness = ()
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl.3d7: %DD.ref as %Y.ref {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   witness = file.%impl_witness.loc8
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl.e11: %DD.ref as %W.ref {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   witness = file.%impl_witness.loc9
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic impl @impl.0ce(%E.loc19_14.1: %facet_type) {
+// CHECK:STDOUT:   %E.loc19_14.2: %facet_type = bind_symbolic_name E, 0 [symbolic = %E.loc19_14.2 (constants.%E)]
+// CHECK:STDOUT:   %E.patt.loc19_14.2: %facet_type = symbolic_binding_pattern E, 0 [symbolic = %E.patt.loc19_14.2 (constants.%E.patt)]
+// CHECK:STDOUT:   %E.as_wit.iface0.loc19_29.2: <witness> = facet_access_witness %E.loc19_14.2, element0 [symbolic = %E.as_wit.iface0.loc19_29.2 (constants.%E.as_wit.iface0)]
+// CHECK:STDOUT:   %E.as_type.loc19_29.2: type = facet_access_type %E.loc19_14.2 [symbolic = %E.as_type.loc19_29.2 (constants.%E.as_type)]
+// CHECK:STDOUT:   %Y.facet.loc19_29.2: %Y.type = facet_value %E.as_type.loc19_29.2, (%E.as_wit.iface0.loc19_29.2) [symbolic = %Y.facet.loc19_29.2 (constants.%Y.facet.15a)]
+// CHECK:STDOUT:   %CC.loc19_29.2: type = class_type @CC, @CC(%Y.facet.loc19_29.2) [symbolic = %CC.loc19_29.2 (constants.%CC.ed1)]
+// CHECK:STDOUT:   %impl_witness: <witness> = impl_witness (), @impl.0ce(%E.loc19_14.2) [symbolic = %impl_witness (constants.%impl_witness.b63)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:
+// CHECK:STDOUT:   impl: %CC.loc19_29.1 as %Z.ref {
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     witness = file.%impl_witness.loc19
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @DD {
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:   complete_type_witness = %complete_type
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%DD
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic class @CC(%D.loc12_10.1: %Y.type) {
+// CHECK:STDOUT:   %D.loc12_10.2: %Y.type = bind_symbolic_name D, 0 [symbolic = %D.loc12_10.2 (constants.%D)]
+// CHECK:STDOUT:   %D.patt.loc12_10.2: %Y.type = symbolic_binding_pattern D, 0 [symbolic = %D.patt.loc12_10.2 (constants.%D.patt)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:
+// CHECK:STDOUT:   class {
+// CHECK:STDOUT:     %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:     complete_type_witness = %complete_type
+// CHECK:STDOUT:
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     .Self = constants.%CC.3ba
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %CC.ref: %CC.type = name_ref CC, file.%CC.decl [concrete = constants.%CC.generic]
+// CHECK:STDOUT:   %DD.ref: type = name_ref DD, file.%DD.decl [concrete = constants.%DD]
+// CHECK:STDOUT:   %Y.facet: %Y.type = facet_value constants.%DD, (constants.%impl_witness.1bc) [concrete = constants.%Y.facet.885]
+// CHECK:STDOUT:   %.loc22_9: %Y.type = converted %DD.ref, %Y.facet [concrete = constants.%Y.facet.885]
+// CHECK:STDOUT:   %CC: type = class_type @CC, @CC(constants.%Y.facet.885) [concrete = constants.%CC.49f]
+// CHECK:STDOUT:   %Z.ref: type = name_ref Z, file.%Z.decl [concrete = constants.%Z.type]
+// CHECK:STDOUT:   %facet_value: %facet_type = facet_value constants.%DD, (constants.%impl_witness.1bc, constants.%impl_witness.1bc) [concrete = constants.%facet_value]
+// CHECK:STDOUT:   %.loc22_12.1: %facet_type = converted constants.%DD, %facet_value [concrete = constants.%facet_value]
+// CHECK:STDOUT:   %Z.facet: %Z.type = facet_value constants.%CC.49f, (constants.%impl_witness.454) [concrete = constants.%Z.facet]
+// CHECK:STDOUT:   %.loc22_12.2: %Z.type = converted %CC, %Z.facet [concrete = constants.%Z.facet]
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @CC(constants.%D) {
+// CHECK:STDOUT:   %D.loc12_10.2 => constants.%D
+// CHECK:STDOUT:   %D.patt.loc12_10.2 => constants.%D
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @CC(constants.%Y.facet.15a) {
+// CHECK:STDOUT:   %D.loc12_10.2 => constants.%Y.facet.15a
+// CHECK:STDOUT:   %D.patt.loc12_10.2 => constants.%Y.facet.15a
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @impl.0ce(constants.%E) {
+// CHECK:STDOUT:   %E.loc19_14.2 => constants.%E
+// CHECK:STDOUT:   %E.patt.loc19_14.2 => constants.%E
+// CHECK:STDOUT:   %E.as_wit.iface0.loc19_29.2 => constants.%E.as_wit.iface0
+// CHECK:STDOUT:   %E.as_type.loc19_29.2 => constants.%E.as_type
+// CHECK:STDOUT:   %Y.facet.loc19_29.2 => constants.%Y.facet.15a
+// CHECK:STDOUT:   %CC.loc19_29.2 => constants.%CC.ed1
+// CHECK:STDOUT:   %impl_witness => constants.%impl_witness.b63
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @CC(@impl.0ce.%Y.facet.loc19_29.2) {}
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @impl.0ce(%E.loc19_14.2) {}
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @CC(constants.%Y.facet.885) {
+// CHECK:STDOUT:   %D.loc12_10.2 => constants.%Y.facet.885
+// CHECK:STDOUT:   %D.patt.loc12_10.2 => constants.%Y.facet.885
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @impl.0ce(constants.%facet_value) {
+// CHECK:STDOUT:   %E.loc19_14.2 => constants.%facet_value
+// CHECK:STDOUT:   %E.patt.loc19_14.2 => constants.%facet_value
+// CHECK:STDOUT:   %E.as_wit.iface0.loc19_29.2 => constants.%impl_witness.1bc
+// CHECK:STDOUT:   %E.as_type.loc19_29.2 => constants.%DD
+// CHECK:STDOUT:   %Y.facet.loc19_29.2 => constants.%Y.facet.885
+// CHECK:STDOUT:   %CC.loc19_29.2 => constants.%CC.49f
+// CHECK:STDOUT:   %impl_witness => constants.%impl_witness.454
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 132 - 21
toolchain/check/testdata/generic/template/unimplemented.carbon

@@ -12,8 +12,13 @@
 
 library "[[@TEST_NAME]]";
 
-// Check that we get a reasonable diagnostic for an unimplemented operation on a template dependent expression.
+// Check that we get a reasonable diagnostic for an unimplemented operation on a
+// template dependent expression.
 fn F[template T:! type](x: T) -> i32 {
+  // CHECK:STDERR: fail_todo_unimplemented_operator.carbon:[[@LINE+8]]:10: error: semantics TODO: `Impl lookup on template-dependent type value` [SemanticsTodo]
+  // CHECK:STDERR:   return x.n * 3;
+  // CHECK:STDERR:          ^~~
+  // CHECK:STDERR:
   // CHECK:STDERR: fail_todo_unimplemented_operator.carbon:[[@LINE+4]]:10: error: cannot access member of interface `Core.Mul` in type `<dependent type>` that does not implement that interface [MissingImplInMemberAccess]
   // CHECK:STDERR:   return x.n * 3;
   // CHECK:STDERR:          ^~~~~~~
@@ -21,6 +26,29 @@ fn F[template T:! type](x: T) -> i32 {
   return x.n * 3;
 }
 
+// --- fail_todo_unimplemented_value.carbon
+
+library "[[@TEST_NAME]]";
+
+class C {
+  var n: i32;
+}
+
+// Check that we get a reasonable diagnostic for an unimplemented operation on a
+// template dependent value where the type is concrete but determined through
+// the template dependent value.
+fn F(template c:! C) -> i32 {
+  // CHECK:STDERR: fail_todo_unimplemented_value.carbon:[[@LINE+8]]:10: error: semantics TODO: `Impl lookup on template-dependent type value` [SemanticsTodo]
+  // CHECK:STDERR:   return c.n * 3;
+  // CHECK:STDERR:          ^~~
+  // CHECK:STDERR:
+  // CHECK:STDERR: fail_todo_unimplemented_value.carbon:[[@LINE+4]]:10: error: cannot access member of interface `Core.Mul` in type `<dependent type>` that does not implement that interface [MissingImplInMemberAccess]
+  // CHECK:STDERR:   return c.n * 3;
+  // CHECK:STDERR:          ^~~~~~~
+  // CHECK:STDERR:
+  return c.n * 3;
+}
+
 // --- fail_todo_unimplemented_convert.carbon
 
 library "[[@TEST_NAME]]";
@@ -75,46 +103,129 @@ fn F[template T:! type](x: T) {
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Core.import = import Core
 // CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {
-// CHECK:STDOUT:     %T.patt.loc5_15.1: type = symbolic_binding_pattern T, 0, template [template = %T.patt.loc5_15.2 (constants.%T.patt)]
-// CHECK:STDOUT:     %x.patt: @F.%T.loc5_15.2 (%T) = binding_pattern x
-// CHECK:STDOUT:     %x.param_patt: @F.%T.loc5_15.2 (%T) = value_param_pattern %x.patt, call_param0
+// CHECK:STDOUT:     %T.patt.loc6_15.1: type = symbolic_binding_pattern T, 0, template [template = %T.patt.loc6_15.2 (constants.%T.patt)]
+// CHECK:STDOUT:     %x.patt: @F.%T.loc6_15.2 (%T) = binding_pattern x
+// CHECK:STDOUT:     %x.param_patt: @F.%T.loc6_15.2 (%T) = value_param_pattern %x.patt, call_param0
 // CHECK:STDOUT:     %return.patt: %i32 = return_slot_pattern
 // CHECK:STDOUT:     %return.param_patt: %i32 = out_param_pattern %return.patt, call_param1
 // CHECK:STDOUT:   } {
 // CHECK:STDOUT:     %int_32: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
 // CHECK:STDOUT:     %i32: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
-// CHECK:STDOUT:     %T.loc5_15.1: type = bind_symbolic_name T, 0, template [template = %T.loc5_15.2 (constants.%T)]
-// CHECK:STDOUT:     %x.param: @F.%T.loc5_15.2 (%T) = value_param call_param0
-// CHECK:STDOUT:     %T.ref: type = name_ref T, %T.loc5_15.1 [template = %T.loc5_15.2 (constants.%T)]
-// CHECK:STDOUT:     %x: @F.%T.loc5_15.2 (%T) = bind_name x, %x.param
+// CHECK:STDOUT:     %T.loc6_15.1: type = bind_symbolic_name T, 0, template [template = %T.loc6_15.2 (constants.%T)]
+// CHECK:STDOUT:     %x.param: @F.%T.loc6_15.2 (%T) = value_param call_param0
+// CHECK:STDOUT:     %T.ref: type = name_ref T, %T.loc6_15.1 [template = %T.loc6_15.2 (constants.%T)]
+// CHECK:STDOUT:     %x: @F.%T.loc6_15.2 (%T) = bind_name x, %x.param
 // CHECK:STDOUT:     %return.param: ref %i32 = out_param call_param1
 // CHECK:STDOUT:     %return: ref %i32 = return_slot %return.param
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: generic fn @F(%T.loc5_15.1: type) {
-// CHECK:STDOUT:   %T.loc5_15.2: type = bind_symbolic_name T, 0, template [template = %T.loc5_15.2 (constants.%T)]
-// CHECK:STDOUT:   %T.patt.loc5_15.2: type = symbolic_binding_pattern T, 0, template [template = %T.patt.loc5_15.2 (constants.%T.patt)]
+// CHECK:STDOUT: generic fn @F(%T.loc6_15.1: type) {
+// CHECK:STDOUT:   %T.loc6_15.2: type = bind_symbolic_name T, 0, template [template = %T.loc6_15.2 (constants.%T)]
+// CHECK:STDOUT:   %T.patt.loc6_15.2: type = symbolic_binding_pattern T, 0, template [template = %T.patt.loc6_15.2 (constants.%T.patt)]
 // CHECK:STDOUT:
 // CHECK:STDOUT: !definition:
-// CHECK:STDOUT:   %require_complete: <witness> = require_complete_type @F.%T.loc5_15.2 (%T) [template = %require_complete (constants.%require_complete.4ae)]
-// CHECK:STDOUT:   %.loc10_11.3: <instruction> = refine_type_action %x.ref, @F.%T.loc5_15.2 (%T) [template]
-// CHECK:STDOUT:   %.loc10_11.4: <instruction> = access_member_action %.loc10_11.1, n [template]
-// CHECK:STDOUT:   %.loc10_11.5: type = type_of_inst %.loc10_11.4 [template]
+// CHECK:STDOUT:   %require_complete: <witness> = require_complete_type @F.%T.loc6_15.2 (%T) [template = %require_complete (constants.%require_complete.4ae)]
+// CHECK:STDOUT:   %.loc15_11.3: <instruction> = refine_type_action %x.ref, @F.%T.loc6_15.2 (%T) [template]
+// CHECK:STDOUT:   %.loc15_11.4: <instruction> = access_member_action %.loc15_11.1, n [template]
+// CHECK:STDOUT:   %.loc15_11.5: type = type_of_inst %.loc15_11.4 [template]
 // CHECK:STDOUT:
-// CHECK:STDOUT:   fn[%T.patt.loc5_15.1: type](%x.param_patt: @F.%T.loc5_15.2 (%T)) -> %i32 {
+// CHECK:STDOUT:   fn[%T.patt.loc6_15.1: type](%x.param_patt: @F.%T.loc6_15.2 (%T)) -> %i32 {
 // CHECK:STDOUT:   !entry:
-// CHECK:STDOUT:     %x.ref: @F.%T.loc5_15.2 (%T) = name_ref x, %x
-// CHECK:STDOUT:     %.loc10_11.1: @F.%T.loc5_15.2 (%T) = splice_inst %.loc10_11.3
-// CHECK:STDOUT:     %.loc10_11.2: @F.%.loc10_11.5 (@F.%.loc10_11.5) = splice_inst %.loc10_11.4
+// CHECK:STDOUT:     %x.ref: @F.%T.loc6_15.2 (%T) = name_ref x, %x
+// CHECK:STDOUT:     %.loc15_11.1: @F.%T.loc6_15.2 (%T) = splice_inst %.loc15_11.3
+// CHECK:STDOUT:     %.loc15_11.2: @F.%.loc15_11.5 (@F.%.loc15_11.5) = splice_inst %.loc15_11.4
 // CHECK:STDOUT:     %int_3: Core.IntLiteral = int_value 3 [concrete = constants.%int_3]
 // CHECK:STDOUT:     return <error>
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @F(constants.%T) {
-// CHECK:STDOUT:   %T.loc5_15.2 => constants.%T
-// CHECK:STDOUT:   %T.patt.loc5_15.2 => constants.%T
+// CHECK:STDOUT:   %T.loc6_15.2 => constants.%T
+// CHECK:STDOUT:   %T.patt.loc6_15.2 => constants.%T
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_todo_unimplemented_value.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [concrete]
+// CHECK:STDOUT:   %int_32: Core.IntLiteral = int_value 32 [concrete]
+// CHECK:STDOUT:   %i32: type = class_type @Int, @Int(%int_32) [concrete]
+// CHECK:STDOUT:   %C.elem: type = unbound_element_type %C, %i32 [concrete]
+// CHECK:STDOUT:   %struct_type.n: type = struct_type {.n: %i32} [concrete]
+// CHECK:STDOUT:   %complete_type.54b: <witness> = complete_type_witness %struct_type.n [concrete]
+// CHECK:STDOUT:   %c: %C = bind_symbolic_name c, 0, template [template]
+// CHECK:STDOUT:   %c.patt: %C = symbolic_binding_pattern c, 0, template [template]
+// CHECK:STDOUT:   %F.type: type = fn_type @F [concrete]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [concrete]
+// CHECK:STDOUT:   %int_3: Core.IntLiteral = int_value 3 [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [concrete] {
+// CHECK:STDOUT:     .Int = %Core.Int
+// CHECK:STDOUT:     .Mul = %Core.Mul
+// CHECK:STDOUT:     import Core//prelude
+// 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:     .C = %C.decl
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core.import = import Core
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [concrete = constants.%C] {} {}
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {
+// CHECK:STDOUT:     %c.patt.loc11_15.1: %C = symbolic_binding_pattern c, 0, template [template = %c.patt.loc11_15.2 (constants.%c.patt)]
+// CHECK:STDOUT:     %return.patt: %i32 = return_slot_pattern
+// CHECK:STDOUT:     %return.param_patt: %i32 = out_param_pattern %return.patt, call_param0
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %int_32: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
+// CHECK:STDOUT:     %i32: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
+// CHECK:STDOUT:     %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C]
+// CHECK:STDOUT:     %c.loc11_15.1: %C = bind_symbolic_name c, 0, template [template = %c.loc11_15.2 (constants.%c)]
+// CHECK:STDOUT:     %return.param: ref %i32 = out_param call_param0
+// CHECK:STDOUT:     %return: ref %i32 = return_slot %return.param
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT:   %.loc5_8: %C.elem = field_decl n, element0 [concrete]
+// CHECK:STDOUT:   name_binding_decl {
+// CHECK:STDOUT:     %.loc5_3: %C.elem = var_pattern %.loc5_8
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.var: ref %C.elem = var <none>
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.n [concrete = constants.%complete_type.54b]
+// CHECK:STDOUT:   complete_type_witness = %complete_type
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%C
+// CHECK:STDOUT:   .n = %.loc5_8
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic fn @F(%c.loc11_15.1: %C) {
+// CHECK:STDOUT:   %c.loc11_15.2: %C = bind_symbolic_name c, 0, template [template = %c.loc11_15.2 (constants.%c)]
+// CHECK:STDOUT:   %c.patt.loc11_15.2: %C = symbolic_binding_pattern c, 0, template [template = %c.patt.loc11_15.2 (constants.%c.patt)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %.loc20_11.2: <instruction> = access_member_action %c.ref, n [template]
+// CHECK:STDOUT:   %.loc20_11.3: type = type_of_inst %.loc20_11.2 [template]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   fn(%c.patt.loc11_15.1: %C) -> %i32 {
+// CHECK:STDOUT:   !entry:
+// CHECK:STDOUT:     %c.ref: %C = name_ref c, %c.loc11_15.1 [template = %c.loc11_15.2 (constants.%c)]
+// CHECK:STDOUT:     %.loc20_11.1: @F.%.loc20_11.3 (@F.%.loc20_11.3) = splice_inst %.loc20_11.2
+// CHECK:STDOUT:     %int_3: Core.IntLiteral = int_value 3 [concrete = constants.%int_3]
+// CHECK:STDOUT:     return <error>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @F(constants.%c) {
+// CHECK:STDOUT:   %c.loc11_15.2 => constants.%c
+// CHECK:STDOUT:   %c.patt.loc11_15.2 => constants.%c
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- fail_todo_unimplemented_convert.carbon

+ 79 - 38
toolchain/check/testdata/impl/lookup/min_prelude/impl_cycle.carbon

@@ -19,36 +19,48 @@ interface Z {}
 // This creates a dependency cycle with itself.
 impl forall [T:! Z] T as Z {}
 
-class Point {
-  impl as Z {}
-}
+class Point {}
 
 fn F() {
-  // CHECK:STDERR: fail_impl_simple_cycle.carbon:[[@LINE+7]]:21: error: cycle found in search for impl of `Z` for type `Point` [ImplLookupCycle]
-  // CHECK:STDERR:   ({} as Point) as (Point as Z);
-  // CHECK:STDERR:                     ^~~~~~~~~~
-  // CHECK:STDERR: fail_impl_simple_cycle.carbon:[[@LINE-10]]:1: note: determining if this impl clause matches [ImplLookupCycleNote]
+  // TODO: We should just get one diagnostic here, either the conversion fails
+  // with a note about the cycle as the reason for impl lookup failing, or the
+  // cycle causes an error so the conversion is not diagnosed.
+
+  // CHECK:STDERR: fail_impl_simple_cycle.carbon:[[@LINE+14]]:3: error: cycle found in search for impl of `Z` for type `Point` [ImplLookupCycle]
+  // CHECK:STDERR:   Point as Z;
+  // CHECK:STDERR:   ^~~~~~~~~~
+  // CHECK:STDERR: fail_impl_simple_cycle.carbon:[[@LINE-12]]:1: note: determining if this impl clause matches [ImplLookupCycleNote]
   // CHECK:STDERR: impl forall [T:! Z] T as Z {}
   // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   // CHECK:STDERR:
-  ({} as Point) as (Point as Z);
+  // CHECK:STDERR: fail_impl_simple_cycle.carbon:[[@LINE+7]]:3: error: cannot convert type `Point` into type implementing `Z` with `as` [ConversionFailureTypeToFacet]
+  // CHECK:STDERR:   Point as Z;
+  // CHECK:STDERR:   ^~~~~~~~~~
+  // CHECK:STDERR: fail_impl_simple_cycle.carbon:[[@LINE+4]]:3: note: type `type` does not implement interface `Core.As(Z)` [MissingImplInMemberAccessNote]
+  // CHECK:STDERR:   Point as Z;
+  // CHECK:STDERR:   ^~~~~~~~~~
+  // CHECK:STDERR:
+  Point as Z;
 }
 
 // --- todo_fail_impl_simple_where_cycle.carbon
 library "[[@TEST_NAME]]";
 
+interface Y {}
 interface Z {}
 
 // This creates a dependency cycle with itself.
 impl forall [T:! type where .Self impls Z] T as Z {}
+// TODO: Remove these two when the above impl can match Point as the self type
+// (and makes a cycle).
+impl forall [T:! Y] T as Z {}
+impl forall [T:! type] T as Y {}
 
-class Point {
-  impl as Z {}
-}
+class Point {}
 
 fn F() {
   // TODO: A cycle should be found in the `impl forall T as Z` above.
-  ({} as Point) as (Point as Z);
+  Point as Z;
 }
 
 // --- fail_impl_simple_two_interfaces.carbon
@@ -60,20 +72,24 @@ interface Y {}
 // This creates a dependency cycle with itself.
 impl forall [T:! Z & Y] T as Z {}
 
-class Point {
-  impl as Z {}
-}
+class Point {}
 
 fn F() {
-  // TODO: A cycle should be found in the `impl forall T as Z` above.
-  // CHECK:STDERR: fail_impl_simple_two_interfaces.carbon:[[@LINE+7]]:21: error: cycle found in search for impl of `Z & Y` for type `Point` [ImplLookupCycle]
-  // CHECK:STDERR:   ({} as Point) as (Point as Z);
-  // CHECK:STDERR:                     ^~~~~~~~~~
-  // CHECK:STDERR: fail_impl_simple_two_interfaces.carbon:[[@LINE-11]]:1: note: determining if this impl clause matches [ImplLookupCycleNote]
+  // CHECK:STDERR: fail_impl_simple_two_interfaces.carbon:[[@LINE+14]]:3: error: cycle found in search for impl of `Z & Y` for type `Point` [ImplLookupCycle]
+  // CHECK:STDERR:   Point as Z;
+  // CHECK:STDERR:   ^~~~~~~~~~
+  // CHECK:STDERR: fail_impl_simple_two_interfaces.carbon:[[@LINE-8]]:1: note: determining if this impl clause matches [ImplLookupCycleNote]
   // CHECK:STDERR: impl forall [T:! Z & Y] T as Z {}
   // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   // CHECK:STDERR:
-  ({} as Point) as (Point as Z);
+  // CHECK:STDERR: fail_impl_simple_two_interfaces.carbon:[[@LINE+7]]:3: error: cannot convert type `Point` into type implementing `Z` with `as` [ConversionFailureTypeToFacet]
+  // CHECK:STDERR:   Point as Z;
+  // CHECK:STDERR:   ^~~~~~~~~~
+  // CHECK:STDERR: fail_impl_simple_two_interfaces.carbon:[[@LINE+4]]:3: note: type `type` does not implement interface `Core.As(Z)` [MissingImplInMemberAccessNote]
+  // CHECK:STDERR:   Point as Z;
+  // CHECK:STDERR:   ^~~~~~~~~~
+  // CHECK:STDERR:
+  Point as Z;
 }
 
 // --- fail_impl_long_cycle.carbon
@@ -89,23 +105,29 @@ impl forall [T:! Y] T as Z {}
 impl forall [T:! Z] T as X {}
 
 class C {}
-impl C as Z {}
 
 fn F() {
-  // CHECK:STDERR: fail_impl_long_cycle.carbon:[[@LINE+13]]:17: error: cycle found in search for impl of `Z` for type `C` [ImplLookupCycle]
-  // CHECK:STDERR:   ({} as C) as (C as Z);
-  // CHECK:STDERR:                 ^~~~~~
-  // CHECK:STDERR: fail_impl_long_cycle.carbon:[[@LINE-10]]:1: note: determining if this impl clause matches [ImplLookupCycleNote]
+  // CHECK:STDERR: fail_impl_long_cycle.carbon:[[@LINE+20]]:3: error: cycle found in search for impl of `Z` for type `C` [ImplLookupCycle]
+  // CHECK:STDERR:   C as Z;
+  // CHECK:STDERR:   ^~~~~~
+  // CHECK:STDERR: fail_impl_long_cycle.carbon:[[@LINE-9]]:1: note: determining if this impl clause matches [ImplLookupCycleNote]
   // CHECK:STDERR: impl forall [T:! Y] T as Z {}
   // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
-  // CHECK:STDERR: fail_impl_long_cycle.carbon:[[@LINE-14]]:1: note: determining if this impl clause matches [ImplLookupCycleNote]
+  // CHECK:STDERR: fail_impl_long_cycle.carbon:[[@LINE-13]]:1: note: determining if this impl clause matches [ImplLookupCycleNote]
   // CHECK:STDERR: impl forall [T:! X] T as Y {}
   // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
-  // CHECK:STDERR: fail_impl_long_cycle.carbon:[[@LINE-15]]:1: note: determining if this impl clause matches [ImplLookupCycleNote]
+  // CHECK:STDERR: fail_impl_long_cycle.carbon:[[@LINE-14]]:1: note: determining if this impl clause matches [ImplLookupCycleNote]
   // CHECK:STDERR: impl forall [T:! Z] T as X {}
   // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   // CHECK:STDERR:
-  ({} as C) as (C as Z);
+  // CHECK:STDERR: fail_impl_long_cycle.carbon:[[@LINE+7]]:3: error: cannot convert type `C` into type implementing `Z` with `as` [ConversionFailureTypeToFacet]
+  // CHECK:STDERR:   C as Z;
+  // CHECK:STDERR:   ^~~~~~
+  // CHECK:STDERR: fail_impl_long_cycle.carbon:[[@LINE+4]]:3: note: type `type` does not implement interface `Core.As(Z)` [MissingImplInMemberAccessNote]
+  // CHECK:STDERR:   C as Z;
+  // CHECK:STDERR:   ^~~~~~
+  // CHECK:STDERR:
+  C as Z;
 }
 
 // --- fail_impl_cycle_one_generic_param.carbon
@@ -119,7 +141,6 @@ impl forall [U:! type, T:! ComparableWith(U)]
 
 class C {}
 class D {}
-impl C as ComparableWith(D) {}
 
 fn Compare[T:! type, U:! ComparableWith(T)](t: T, u: U) {}
 
@@ -127,7 +148,7 @@ fn F() {
   // CHECK:STDERR: fail_impl_cycle_one_generic_param.carbon:[[@LINE+20]]:3: error: cycle found in search for impl of `ComparableWith(C)` for type `C` [ImplLookupCycle]
   // CHECK:STDERR:   Compare({} as C, {} as C);
   // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~~~~~
-  // CHECK:STDERR: fail_impl_cycle_one_generic_param.carbon:[[@LINE-13]]:1: note: determining if this impl clause matches [ImplLookupCycleNote]
+  // CHECK:STDERR: fail_impl_cycle_one_generic_param.carbon:[[@LINE-12]]:1: note: determining if this impl clause matches [ImplLookupCycleNote]
   // CHECK:STDERR: impl forall [U:! type, T:! ComparableWith(U)]
   // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   // CHECK:STDERR: fail_impl_cycle_one_generic_param.carbon:[[@LINE-9]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere]
@@ -146,31 +167,51 @@ fn F() {
   // CHECK:STDERR:
   Compare({} as C, {} as C);
 
-  // CHECK:STDERR: fail_impl_cycle_one_generic_param.carbon:[[@LINE+13]]:3: error: cycle found in search for impl of `ComparableWith(C)` for type `D` [ImplLookupCycle]
+  // CHECK:STDERR: fail_impl_cycle_one_generic_param.carbon:[[@LINE+23]]:3: error: cycle found in search for impl of `ComparableWith(C)` for type `D` [ImplLookupCycle]
   // CHECK:STDERR:   Compare({} as C, {} as D);
   // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~~~~~
-  // CHECK:STDERR: fail_impl_cycle_one_generic_param.carbon:[[@LINE-35]]:1: note: determining if this impl clause matches [ImplLookupCycleNote]
+  // CHECK:STDERR: fail_impl_cycle_one_generic_param.carbon:[[@LINE-34]]:1: note: determining if this impl clause matches [ImplLookupCycleNote]
   // CHECK:STDERR: impl forall [U:! type, T:! ComparableWith(U)]
   // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-  // CHECK:STDERR: fail_impl_cycle_one_generic_param.carbon:[[@LINE-38]]:1: note: determining if this impl clause matches [ImplLookupCycleNote]
+  // CHECK:STDERR: fail_impl_cycle_one_generic_param.carbon:[[@LINE-37]]:1: note: determining if this impl clause matches [ImplLookupCycleNote]
   // CHECK:STDERR: impl forall [U:! type, T:! ComparableWith(U)]
   // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   // CHECK:STDERR: fail_impl_cycle_one_generic_param.carbon:[[@LINE-34]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere]
   // CHECK:STDERR: fn Compare[T:! type, U:! ComparableWith(T)](t: T, u: U) {}
   // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   // CHECK:STDERR:
+  // CHECK:STDERR: fail_impl_cycle_one_generic_param.carbon:[[@LINE+10]]:3: error: cannot implicitly convert type `D` into type implementing `ComparableWith(C)` [ConversionFailureTypeToFacet]
+  // CHECK:STDERR:   Compare({} as C, {} as D);
+  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~~~~~
+  // CHECK:STDERR: fail_impl_cycle_one_generic_param.carbon:[[@LINE+7]]:3: note: type `type` does not implement interface `Core.ImplicitAs(ComparableWith(C))` [MissingImplInMemberAccessNote]
+  // CHECK:STDERR:   Compare({} as C, {} as D);
+  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~~~~~
+  // CHECK:STDERR: fail_impl_cycle_one_generic_param.carbon:[[@LINE-44]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere]
+  // CHECK:STDERR: fn Compare[T:! type, U:! ComparableWith(T)](t: T, u: U) {}
+  // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  // CHECK:STDERR:
   Compare({} as C, {} as D);
 
-  // CHECK:STDERR: fail_impl_cycle_one_generic_param.carbon:[[@LINE+13]]:3: error: cycle found in search for impl of `ComparableWith(D)` for type `C` [ImplLookupCycle]
+  // CHECK:STDERR: fail_impl_cycle_one_generic_param.carbon:[[@LINE+23]]:3: error: cycle found in search for impl of `ComparableWith(D)` for type `C` [ImplLookupCycle]
   // CHECK:STDERR:   Compare({} as D, {} as C);
   // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~~~~~
-  // CHECK:STDERR: fail_impl_cycle_one_generic_param.carbon:[[@LINE-50]]:1: note: determining if this impl clause matches [ImplLookupCycleNote]
+  // CHECK:STDERR: fail_impl_cycle_one_generic_param.carbon:[[@LINE-59]]:1: note: determining if this impl clause matches [ImplLookupCycleNote]
   // CHECK:STDERR: impl forall [U:! type, T:! ComparableWith(U)]
   // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-  // CHECK:STDERR: fail_impl_cycle_one_generic_param.carbon:[[@LINE-53]]:1: note: determining if this impl clause matches [ImplLookupCycleNote]
+  // CHECK:STDERR: fail_impl_cycle_one_generic_param.carbon:[[@LINE-62]]:1: note: determining if this impl clause matches [ImplLookupCycleNote]
   // CHECK:STDERR: impl forall [U:! type, T:! ComparableWith(U)]
   // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-  // CHECK:STDERR: fail_impl_cycle_one_generic_param.carbon:[[@LINE-49]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere]
+  // CHECK:STDERR: fail_impl_cycle_one_generic_param.carbon:[[@LINE-59]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere]
+  // CHECK:STDERR: fn Compare[T:! type, U:! ComparableWith(T)](t: T, u: U) {}
+  // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  // CHECK:STDERR:
+  // CHECK:STDERR: fail_impl_cycle_one_generic_param.carbon:[[@LINE+10]]:3: error: cannot implicitly convert type `C` into type implementing `ComparableWith(D)` [ConversionFailureTypeToFacet]
+  // CHECK:STDERR:   Compare({} as D, {} as C);
+  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~~~~~
+  // CHECK:STDERR: fail_impl_cycle_one_generic_param.carbon:[[@LINE+7]]:3: note: type `type` does not implement interface `Core.ImplicitAs(ComparableWith(D))` [MissingImplInMemberAccessNote]
+  // CHECK:STDERR:   Compare({} as D, {} as C);
+  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~~~~~
+  // CHECK:STDERR: fail_impl_cycle_one_generic_param.carbon:[[@LINE-69]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere]
   // CHECK:STDERR: fn Compare[T:! type, U:! ComparableWith(T)](t: T, u: U) {}
   // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   // CHECK:STDERR:

+ 307 - 0
toolchain/check/testdata/impl/lookup/min_prelude/specialization.carbon

@@ -0,0 +1,307 @@
+// 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/min_prelude/facet_types.carbon
+// EXTRA-ARGS: --no-dump-sem-ir --custom-core
+//
+// AUTOUPDATE
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/impl/lookup/min_prelude/specialization.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/lookup/min_prelude/specialization.carbon
+
+// --- specialized_self_first.carbon
+library "[[@TEST_NAME]]";
+
+interface Z(T:! type) {
+  let X:! type;
+}
+
+class C {}
+impl C as Z(C) where .X = C {}
+
+impl forall [T:! type] T as Z(T) where .X = () {}
+
+fn F() {
+  // The specialization of `Z(C)` should match in preference to the blanket impl
+  // of `Z(T)`. If the blanket impl is chosen, then `a` will have type `()`
+  // which will fail to typecheck here.
+  let a: C.(Z(C).X) = {} as C;
+}
+
+// --- specialized_self_second.carbon
+library "[[@TEST_NAME]]";
+
+interface Z(T:! type) {
+  let X:! type;
+}
+
+impl forall [T:! type] T as Z(T) where .X = () {}
+
+class C {}
+impl C as Z(C) where .X = C {}
+
+fn F() {
+  // The specialization of `Z(C)` should match in preference to the blanket impl
+  // of `Z(T)`. If the blanket impl is chosen, then `a` will have type `()`
+  // which will fail to typecheck here.
+  let a: C.(Z(C).X) = {} as C;
+}
+
+// --- specialized_constraint_first.carbon
+library "[[@TEST_NAME]]";
+
+interface Z(T:! type) {
+  let X:! type;
+}
+
+class C {}
+
+impl C as Z(C) where .X = C {}
+impl forall [T:! type] C as Z(T) where .X = () {}
+
+fn F() {
+  // The specialization of `Z(C)` should match in preference to the blanket impl
+  // of `Z(T)`. If the blanket impl is chosen, then `a` will have type `()`
+  // which will fail to typecheck here.
+  let a: C.(Z(C).X) = {} as C;
+}
+
+// --- specialized_constraint_second.carbon
+library "[[@TEST_NAME]]";
+
+interface Z(T:! type) {
+  let X:! type;
+}
+
+class C {}
+
+impl forall [T:! type] C as Z(T) where .X = () {}
+impl C as Z(C) where .X = C {}
+
+fn F() {
+  // The specialization of `Z(C)` should match in preference to the blanket impl
+  // of `Z(T)`. If the blanket impl is chosen, then `a` will have type `()`
+  // which will fail to typecheck here.
+  let a: C.(Z(C).X) = {} as C;
+}
+
+// --- specialized_self_vs_constraint_self_first.carbon
+library "[[@TEST_NAME]]";
+
+interface Z(T:! type) {
+  let X:! type;
+}
+
+class C(T:! type) {}
+
+impl forall [T:! type] C(()) as Z(T) where .X = C(()) {}
+impl forall [T:! type] C(T) as Z(C(())) where .X = () {}
+
+fn F() {
+  // The specialization of `C(())` should match in preference to the blanket impl
+  // of `C(T)`. If the blanket impl is chosen, then `a` will have type `()`
+  // which will fail to typecheck here.
+  let a: C(()).(Z(C(())).X) = {} as C(());
+}
+
+// --- specialized_self_vs_constraint_self_second.carbon
+library "[[@TEST_NAME]]";
+
+interface Z(T:! type) {
+  let X:! type;
+}
+
+class C(T:! type) {}
+
+impl forall [T:! type] C(T) as Z(C(())) where .X = () {}
+impl forall [T:! type] C(()) as Z(T) where .X = C(()) {}
+
+fn F() {
+  // The specialization of `Z(C)` should match in preference to the blanket impl
+  // of `Z(T)`. If the blanket impl is chosen, then `a` will have type `()`
+  // which will fail to typecheck here.
+  let a: C(()).(Z(C(())).X) = {} as C(());
+}
+
+// --- generic_class_with_fully_specified_impl.carbon
+library "[[@TEST_NAME]]";
+
+interface Z(T:! type) {
+  let X:! type;
+}
+
+impl forall [T:! type] T as Z(T) where .X = {.a: ()} {}
+
+class C(T:! type) {}
+impl C(()) as Z(()) where .X = C(()) {}
+
+impl forall [T:! type] C(T) as Z(T) where .X = {.b: ()} {}
+
+fn F() {
+  // The specialization of `Z(C)` should match in preference to the blanket
+  // impls of `Z(T)`. If a blanket impl is chosen, then `a` will have a struct
+  // type which will fail to typecheck here when constructed from a `C` value.
+  let a: C(()).(Z(()).X) = {} as C(());
+}
+
+// --- generic_class_with_blanket_impl_first.carbon
+library "[[@TEST_NAME]]";
+
+interface Z(T:! type) {
+  let X:! type;
+}
+
+class C(T:! type) {}
+
+impl forall [T:! type] C(T) as Z(T) where .X = C(()) {}
+
+impl forall [T:! type] T as Z(T) where .X = () {}
+
+fn F() {
+  // The specialization of `Z(C)` should match in preference to the blanket
+  // impls of `Z(T)`. If a blanket impl is chosen, then `a` will have type `()`
+  // which will fail to typecheck here when constructed from a `C` value.
+  let a: C(()).(Z(()).X) = {} as C(());
+}
+
+// --- generic_class_with_blanket_impl_second.carbon
+library "[[@TEST_NAME]]";
+
+interface Z(T:! type) {
+  let X:! type;
+}
+
+impl forall [T:! type] T as Z(T) where .X = () {}
+
+class C(T:! type) {}
+
+impl forall [T:! type] C(T) as Z(T) where .X = C(()) {}
+
+fn F() {
+  // The specialization of `Z(C)` should match in preference to the blanket
+  // impls of `Z(T)`. If a blanket impl is chosen, then `a` will have type `()`
+  // which will fail to typecheck here when constructed from a `C` value.
+  //
+  // TODO: C(T) is C(()) so this should work?
+  let a: C(()).(Z(()).X) = {} as C(());
+}
+
+// --- specialized_class_with_facet_value_param.carbon
+library "[[@TEST_NAME]]";
+
+interface Z {
+  let X:! type;
+}
+
+class D(T:! type) {}
+impl forall [T:! type] D(T) as Z where .X = D(()) {}
+
+class E {}
+impl E as Z where .X = () {}
+
+class C(T:! Z, U:! Z) {}
+
+// This places a FacetValue of type FacetType(Z) at the position of `D` in the
+// self type, because the class C requires a facet value satisfying Z. It tests
+// that we correctly determine that this FacetType is not symbolic, and look at
+// the parametes of D for symbolic references.
+impl forall [T:! Z] C(T, D(E)) as Z where .X = () {}
+impl forall [T:! Z] C(D(T), T) as Z where .X = () {}
+// This is the best match, `T` is in the last position compared to the others.
+impl forall [T:! Z] C(D(E), T) as Z where .X = C(E, E) {}
+impl forall [T:! Z] C(T, T) as Z where .X = () {}
+
+fn F() {
+  let a: C(D(E), D(E)).(Z.X) = {} as C(E, E);
+}
+
+// --- specialized_class_with_symbolic_facet_value_param.carbon
+
+interface Z {
+  let X:! type;
+}
+
+impl forall [T:! type] T as Z where .X = T {}
+
+interface Y {}
+class C(T:! Y) {}
+
+class D {}
+impl D as Y {}
+
+// D can be either a concrete or symbolic FacetValue, depending on what the
+// caller has.
+impl forall [D:! Y] C(D) as Z where .X = () {}
+
+fn F[D:! Y](d: D) {
+  // The FacetValue deduced for the param of `C` will be a symbolic FacetValue
+  // because we are in a generic where `D` is an unknown type, which will cause
+  // the query and impl self type to be C(FacetValue) for a symbolic FacetValue.
+  //
+  // TODO: The type of `.X` should actually be symbolic here, unless the impl is
+  // effectively final (since the lookup query uses a symbolic `D` in its
+  // input). But we don't have support for either of those yet.
+  let a: C(D).(Z.X) = ();
+}
+
+// --- pointer_specialization_first.carbon
+library "[[@TEST_NAME]]";
+
+interface Z {
+  let X:! type;
+}
+
+class C {}
+impl C* as Z where .X = C {}
+
+impl forall [T:! type] T* as Z where .X = () {}
+
+fn F() {
+  // The specialization of `Z(C)` should match in preference to the blanket impl
+  // of `Z(T)`. If the blanket impl is chosen, then `a` will have type `()`
+  // which will fail to typecheck here.
+  let a: (C*).(Z.X) = {} as C;
+}
+
+// --- pointer_specialization_second.carbon
+library "[[@TEST_NAME]]";
+
+interface Z {
+  let X:! type;
+}
+
+impl forall [T:! type] T* as Z where .X = () {}
+
+class C {}
+impl C* as Z where .X = C {}
+
+fn F() {
+  // The specialization of `Z(C)` should match in preference to the blanket impl
+  // of `Z(T)`. If the blanket impl is chosen, then `a` will have type `()`
+  // which will fail to typecheck here.
+  let a: (C*).(Z.X) = {} as C;
+}
+
+// --- cycle_in_deduce_avoided_by_specialization.carbon
+library "[[@TEST_NAME]]";
+
+interface Z {
+  let X:! type;
+}
+
+class C(T:! type) {}
+
+// This impl makes a cycle, but it's not considered at all for `C(())` since
+// there is another impl with a better type structure, so no diagnostic is
+// emitted.
+impl forall [T:! Z] T as Z where .X = () {}
+// Also a cycle, and also a worse match for `C(())`.
+impl forall [T:! Z] C(T) as Z where .X = () {}
+
+impl C(()) as Z where .X = C(()) {}
+
+fn F() {
+  let a: C(()).(Z.X) = {} as C(());
+}

+ 419 - 0
toolchain/check/testdata/impl/lookup/no_prelude/import.carbon

@@ -155,6 +155,35 @@ fn M() {
   obj.(PackageHasParam.Y.K)();
 }
 
+// --- has_generic_class.carbon
+
+package PackageGenericClass;
+
+import PackageHasParam;
+
+class GenericClass(U:! type) {}
+
+impl PackageHasParam.AnyParam(GenericClass) as PackageHasParam.Y {
+  fn K[self: Self]() {}
+}
+
+fn L() {
+  var obj: PackageHasParam.AnyParam(GenericClass) = {};
+  obj.(PackageHasParam.Y.K)();
+}
+
+// --- use_generic_class_as_param.carbon
+
+library "[[@TEST_NAME]]";
+
+import PackageHasParam;
+import PackageGenericClass;
+
+fn M() {
+  var obj: PackageHasParam.AnyParam(PackageGenericClass.GenericClass) = {};
+  obj.(PackageHasParam.Y.K)();
+}
+
 // --- has_extra_interfaces.carbon
 
 package HasExtraInterfaces;
@@ -1741,6 +1770,396 @@ fn Test(c: HasExtraInterfaces.C(type)) {
 // CHECK:STDOUT:   %Self.as_type => constants.%Self.as_type
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: --- has_generic_class.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %U: type = bind_symbolic_name U, 0 [symbolic]
+// CHECK:STDOUT:   %U.patt: type = symbolic_binding_pattern U, 0 [symbolic]
+// CHECK:STDOUT:   %GenericClass.type: type = generic_class_type @GenericClass [concrete]
+// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
+// CHECK:STDOUT:   %GenericClass.generic: %GenericClass.type = struct_value () [concrete]
+// CHECK:STDOUT:   %GenericClass: type = class_type @GenericClass, @GenericClass(%U) [symbolic]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete]
+// CHECK:STDOUT:   %AnyParam.type: type = generic_class_type @AnyParam [concrete]
+// CHECK:STDOUT:   %AnyParam.generic: %AnyParam.type = struct_value () [concrete]
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T, 0 [symbolic]
+// CHECK:STDOUT:   %X: %T = bind_symbolic_name X, 1 [symbolic]
+// CHECK:STDOUT:   %T.patt: type = symbolic_binding_pattern T, 0 [symbolic]
+// CHECK:STDOUT:   %X.patt.51ccc0.2: %T = symbolic_binding_pattern X, 1 [symbolic]
+// CHECK:STDOUT:   %AnyParam.0dd: type = class_type @AnyParam, @AnyParam(%GenericClass.type, %GenericClass.generic) [concrete]
+// CHECK:STDOUT:   %Y.type: type = facet_type <@Y> [concrete]
+// CHECK:STDOUT:   %Self: %Y.type = bind_symbolic_name Self, 0 [symbolic]
+// CHECK:STDOUT:   %K.type.311: type = fn_type @K.1 [concrete]
+// CHECK:STDOUT:   %K.7a1: %K.type.311 = struct_value () [concrete]
+// CHECK:STDOUT:   %Self.as_type: type = facet_access_type %Self [symbolic]
+// CHECK:STDOUT:   %require_complete: <witness> = require_complete_type %Self.as_type [symbolic]
+// CHECK:STDOUT:   %impl_witness: <witness> = impl_witness (@impl.%K.decl) [concrete]
+// CHECK:STDOUT:   %K.type.965: type = fn_type @K.2 [concrete]
+// CHECK:STDOUT:   %K.5a9: %K.type.965 = struct_value () [concrete]
+// CHECK:STDOUT:   %Y.facet: %Y.type = facet_value %AnyParam.0dd, (%impl_witness) [concrete]
+// CHECK:STDOUT:   %L.type: type = fn_type @L [concrete]
+// CHECK:STDOUT:   %L: %L.type = struct_value () [concrete]
+// CHECK:STDOUT:   %AnyParam.val: %AnyParam.0dd = struct_value () [concrete]
+// CHECK:STDOUT:   %Y.assoc_type: type = assoc_entity_type %Y.type [concrete]
+// CHECK:STDOUT:   %assoc0: %Y.assoc_type = assoc_entity element0, imports.%PackageHasParam.import_ref.ce2 [concrete]
+// CHECK:STDOUT:   %.e27: type = fn_type_with_self_type %K.type.311, %Y.facet [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %PackageHasParam: <namespace> = namespace file.%PackageHasParam.import, [concrete] {
+// CHECK:STDOUT:     .AnyParam = %PackageHasParam.AnyParam
+// CHECK:STDOUT:     .Y = %PackageHasParam.Y
+// CHECK:STDOUT:     import PackageHasParam//default
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %PackageHasParam.AnyParam: %AnyParam.type = import_ref PackageHasParam//default, AnyParam, loaded [concrete = constants.%AnyParam.generic]
+// CHECK:STDOUT:   %PackageHasParam.import_ref.5ab: type = import_ref PackageHasParam//default, loc4_16, loaded [symbolic = @AnyParam.%T (constants.%T)]
+// CHECK:STDOUT:   %PackageHasParam.import_ref.34c: @AnyParam.%T (%T) = import_ref PackageHasParam//default, loc4_26, loaded [symbolic = @AnyParam.%X (constants.%X)]
+// CHECK:STDOUT:   %PackageHasParam.import_ref.8f2: <witness> = import_ref PackageHasParam//default, loc4_34, loaded [concrete = constants.%complete_type]
+// CHECK:STDOUT:   %PackageHasParam.import_ref.601 = import_ref PackageHasParam//default, inst31 [no loc], unloaded
+// CHECK:STDOUT:   %PackageHasParam.Y: type = import_ref PackageHasParam//default, Y, loaded [concrete = constants.%Y.type]
+// CHECK:STDOUT:   %PackageHasParam.import_ref.dc1 = import_ref PackageHasParam//default, inst37 [no loc], unloaded
+// CHECK:STDOUT:   %PackageHasParam.import_ref.5e7: %Y.assoc_type = import_ref PackageHasParam//default, loc7_22, loaded [concrete = constants.%assoc0]
+// CHECK:STDOUT:   %PackageHasParam.K: %K.type.311 = import_ref PackageHasParam//default, K, loaded [concrete = constants.%K.7a1]
+// CHECK:STDOUT:   %PackageHasParam.import_ref.292: %Y.type = import_ref PackageHasParam//default, inst37 [no loc], loaded [symbolic = constants.%Self]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .PackageHasParam = imports.%PackageHasParam
+// CHECK:STDOUT:     .GenericClass = %GenericClass.decl
+// CHECK:STDOUT:     .L = %L.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %PackageHasParam.import = import PackageHasParam
+// CHECK:STDOUT:   %GenericClass.decl: %GenericClass.type = class_decl @GenericClass [concrete = constants.%GenericClass.generic] {
+// CHECK:STDOUT:     %U.patt.loc6_20.1: type = symbolic_binding_pattern U, 0 [symbolic = %U.patt.loc6_20.2 (constants.%U.patt)]
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %U.loc6_20.1: type = bind_symbolic_name U, 0 [symbolic = %U.loc6_20.2 (constants.%U)]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   impl_decl @impl [concrete] {} {
+// CHECK:STDOUT:     %PackageHasParam.ref.loc8_6: <namespace> = name_ref PackageHasParam, imports.%PackageHasParam [concrete = imports.%PackageHasParam]
+// CHECK:STDOUT:     %AnyParam.ref: %AnyParam.type = name_ref AnyParam, imports.%PackageHasParam.AnyParam [concrete = constants.%AnyParam.generic]
+// CHECK:STDOUT:     %GenericClass.ref: %GenericClass.type = name_ref GenericClass, file.%GenericClass.decl [concrete = constants.%GenericClass.generic]
+// CHECK:STDOUT:     %AnyParam: type = class_type @AnyParam, @AnyParam(constants.%GenericClass.type, constants.%GenericClass.generic) [concrete = constants.%AnyParam.0dd]
+// CHECK:STDOUT:     %PackageHasParam.ref.loc8_48: <namespace> = name_ref PackageHasParam, imports.%PackageHasParam [concrete = imports.%PackageHasParam]
+// CHECK:STDOUT:     %Y.ref: type = name_ref Y, imports.%PackageHasParam.Y [concrete = constants.%Y.type]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %impl_witness: <witness> = impl_witness (@impl.%K.decl) [concrete = constants.%impl_witness]
+// CHECK:STDOUT:   %L.decl: %L.type = fn_decl @L [concrete = constants.%L] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Y [from "has_param.carbon"] {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = imports.%PackageHasParam.import_ref.dc1
+// CHECK:STDOUT:   .K = imports.%PackageHasParam.import_ref.5e7
+// CHECK:STDOUT:   witness = (imports.%PackageHasParam.K)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl: %AnyParam as %Y.ref {
+// CHECK:STDOUT:   %K.decl: %K.type.965 = fn_decl @K.2 [concrete = constants.%K.5a9] {
+// CHECK:STDOUT:     %self.patt: %AnyParam.0dd = binding_pattern self
+// CHECK:STDOUT:     %self.param_patt: %AnyParam.0dd = value_param_pattern %self.patt, call_param0
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %self.param: %AnyParam.0dd = value_param call_param0
+// CHECK:STDOUT:     %Self.ref: type = name_ref Self, @impl.%AnyParam [concrete = constants.%AnyParam.0dd]
+// CHECK:STDOUT:     %self: %AnyParam.0dd = bind_name self, %self.param
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .K = %K.decl
+// CHECK:STDOUT:   witness = file.%impl_witness
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic class @GenericClass(%U.loc6_20.1: type) {
+// CHECK:STDOUT:   %U.loc6_20.2: type = bind_symbolic_name U, 0 [symbolic = %U.loc6_20.2 (constants.%U)]
+// CHECK:STDOUT:   %U.patt.loc6_20.2: type = symbolic_binding_pattern U, 0 [symbolic = %U.patt.loc6_20.2 (constants.%U.patt)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:
+// CHECK:STDOUT:   class {
+// CHECK:STDOUT:     %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:     complete_type_witness = %complete_type
+// CHECK:STDOUT:
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     .Self = constants.%GenericClass
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic class @AnyParam(imports.%PackageHasParam.import_ref.5ab: type, imports.%PackageHasParam.import_ref.34c: @AnyParam.%T (%T)) [from "has_param.carbon"] {
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T, 0 [symbolic = %T (constants.%T)]
+// CHECK:STDOUT:   %T.patt: type = symbolic_binding_pattern T, 0 [symbolic = %T.patt (constants.%T.patt)]
+// CHECK:STDOUT:   %X: %T = bind_symbolic_name X, 1 [symbolic = %X (constants.%X)]
+// CHECK:STDOUT:   %X.patt: %T = symbolic_binding_pattern X, 1 [symbolic = %X.patt (constants.%X.patt.51ccc0.2)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:
+// CHECK:STDOUT:   class {
+// CHECK:STDOUT:     complete_type_witness = imports.%PackageHasParam.import_ref.8f2
+// CHECK:STDOUT:
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     .Self = imports.%PackageHasParam.import_ref.601
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic fn @K.1(imports.%PackageHasParam.import_ref.292: %Y.type) [from "has_param.carbon"] {
+// CHECK:STDOUT:   %Self: %Y.type = bind_symbolic_name Self, 0 [symbolic = %Self (constants.%Self)]
+// CHECK:STDOUT:   %Self.as_type: type = facet_access_type %Self [symbolic = %Self.as_type (constants.%Self.as_type)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %require_complete: <witness> = require_complete_type @K.1.%Self.as_type (%Self.as_type) [symbolic = %require_complete (constants.%require_complete)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   fn[%self.param_patt: @K.1.%Self.as_type (%Self.as_type)]();
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @K.2[%self.param_patt: %AnyParam.0dd]() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @L() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   name_binding_decl {
+// CHECK:STDOUT:     %obj.patt: %AnyParam.0dd = binding_pattern obj
+// CHECK:STDOUT:     %.loc13_3.1: %AnyParam.0dd = var_pattern %obj.patt
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %obj.var: ref %AnyParam.0dd = var obj
+// CHECK:STDOUT:   %.loc13_54.1: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:   %.loc13_54.2: init %AnyParam.0dd = class_init (), %obj.var [concrete = constants.%AnyParam.val]
+// CHECK:STDOUT:   %.loc13_3.2: init %AnyParam.0dd = converted %.loc13_54.1, %.loc13_54.2 [concrete = constants.%AnyParam.val]
+// CHECK:STDOUT:   assign %obj.var, %.loc13_3.2
+// CHECK:STDOUT:   %.loc13_49: type = splice_block %AnyParam [concrete = constants.%AnyParam.0dd] {
+// CHECK:STDOUT:     %PackageHasParam.ref.loc13: <namespace> = name_ref PackageHasParam, imports.%PackageHasParam [concrete = imports.%PackageHasParam]
+// CHECK:STDOUT:     %AnyParam.ref: %AnyParam.type = name_ref AnyParam, imports.%PackageHasParam.AnyParam [concrete = constants.%AnyParam.generic]
+// CHECK:STDOUT:     %GenericClass.ref: %GenericClass.type = name_ref GenericClass, file.%GenericClass.decl [concrete = constants.%GenericClass.generic]
+// CHECK:STDOUT:     %AnyParam: type = class_type @AnyParam, @AnyParam(constants.%GenericClass.type, constants.%GenericClass.generic) [concrete = constants.%AnyParam.0dd]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %obj: ref %AnyParam.0dd = bind_name obj, %obj.var
+// CHECK:STDOUT:   %obj.ref: ref %AnyParam.0dd = name_ref obj, %obj
+// CHECK:STDOUT:   %PackageHasParam.ref.loc14: <namespace> = name_ref PackageHasParam, imports.%PackageHasParam [concrete = imports.%PackageHasParam]
+// CHECK:STDOUT:   %Y.ref: type = name_ref Y, imports.%PackageHasParam.Y [concrete = constants.%Y.type]
+// CHECK:STDOUT:   %K.ref: %Y.assoc_type = name_ref K, imports.%PackageHasParam.import_ref.5e7 [concrete = constants.%assoc0]
+// CHECK:STDOUT:   %impl.elem0: %.e27 = impl_witness_access constants.%impl_witness, element0 [concrete = constants.%K.5a9]
+// CHECK:STDOUT:   %bound_method: <bound method> = bound_method %obj.ref, %impl.elem0
+// CHECK:STDOUT:   %.loc14: %AnyParam.0dd = bind_value %obj.ref
+// CHECK:STDOUT:   %K.call: init %empty_tuple.type = call %bound_method(%.loc14)
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @GenericClass(constants.%U) {
+// CHECK:STDOUT:   %U.loc6_20.2 => constants.%U
+// CHECK:STDOUT:   %U.patt.loc6_20.2 => constants.%U
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @AnyParam(constants.%T, constants.%X) {
+// CHECK:STDOUT:   %T => constants.%T
+// CHECK:STDOUT:   %T.patt => constants.%T
+// CHECK:STDOUT:   %X => constants.%X
+// CHECK:STDOUT:   %X.patt => constants.%X
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @AnyParam(constants.%GenericClass.type, constants.%GenericClass.generic) {
+// CHECK:STDOUT:   %T => constants.%GenericClass.type
+// CHECK:STDOUT:   %T.patt => constants.%GenericClass.type
+// CHECK:STDOUT:   %X => constants.%GenericClass.generic
+// CHECK:STDOUT:   %X.patt => constants.%GenericClass.generic
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @K.1(constants.%Self) {
+// CHECK:STDOUT:   %Self => constants.%Self
+// CHECK:STDOUT:   %Self.as_type => constants.%Self.as_type
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @K.1(constants.%Y.facet) {
+// CHECK:STDOUT:   %Self => constants.%Y.facet
+// CHECK:STDOUT:   %Self.as_type => constants.%AnyParam.0dd
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- use_generic_class_as_param.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %M.type: type = fn_type @M [concrete]
+// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
+// CHECK:STDOUT:   %M: %M.type = struct_value () [concrete]
+// CHECK:STDOUT:   %AnyParam.type: type = generic_class_type @AnyParam [concrete]
+// CHECK:STDOUT:   %AnyParam.generic: %AnyParam.type = struct_value () [concrete]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete]
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T, 0 [symbolic]
+// CHECK:STDOUT:   %X: %T = bind_symbolic_name X, 1 [symbolic]
+// CHECK:STDOUT:   %T.patt: type = symbolic_binding_pattern T, 0 [symbolic]
+// CHECK:STDOUT:   %X.patt.51ccc0.2: %T = symbolic_binding_pattern X, 1 [symbolic]
+// CHECK:STDOUT:   %GenericClass.type: type = generic_class_type @GenericClass [concrete]
+// CHECK:STDOUT:   %GenericClass.generic: %GenericClass.type = struct_value () [concrete]
+// CHECK:STDOUT:   %U: type = bind_symbolic_name U, 0 [symbolic]
+// CHECK:STDOUT:   %U.patt: type = symbolic_binding_pattern U, 0 [symbolic]
+// CHECK:STDOUT:   %AnyParam.d71: type = class_type @AnyParam, @AnyParam(%GenericClass.type, %GenericClass.generic) [concrete]
+// CHECK:STDOUT:   %AnyParam.val: %AnyParam.d71 = struct_value () [concrete]
+// CHECK:STDOUT:   %Y.type: type = facet_type <@Y> [concrete]
+// CHECK:STDOUT:   %Self: %Y.type = bind_symbolic_name Self, 0 [symbolic]
+// CHECK:STDOUT:   %Y.assoc_type: type = assoc_entity_type %Y.type [concrete]
+// CHECK:STDOUT:   %assoc0: %Y.assoc_type = assoc_entity element0, imports.%PackageHasParam.import_ref.ce2 [concrete]
+// CHECK:STDOUT:   %K.type.311: type = fn_type @K.1 [concrete]
+// CHECK:STDOUT:   %Self.as_type: type = facet_access_type %Self [symbolic]
+// CHECK:STDOUT:   %require_complete: <witness> = require_complete_type %Self.as_type [symbolic]
+// CHECK:STDOUT:   %impl_witness: <witness> = impl_witness (imports.%PackageGenericClass.import_ref.fbf) [concrete]
+// CHECK:STDOUT:   %Y.facet: %Y.type = facet_value %AnyParam.d71, (%impl_witness) [concrete]
+// CHECK:STDOUT:   %.3b2: type = fn_type_with_self_type %K.type.311, %Y.facet [concrete]
+// CHECK:STDOUT:   %K.type.9cf: type = fn_type @K.2 [concrete]
+// CHECK:STDOUT:   %K.039: %K.type.9cf = struct_value () [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %PackageHasParam: <namespace> = namespace file.%PackageHasParam.import, [concrete] {
+// CHECK:STDOUT:     .AnyParam = %PackageHasParam.AnyParam
+// CHECK:STDOUT:     .Y = %PackageHasParam.Y
+// CHECK:STDOUT:     import PackageHasParam//default
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %PackageGenericClass: <namespace> = namespace file.%PackageGenericClass.import, [concrete] {
+// CHECK:STDOUT:     .GenericClass = %PackageGenericClass.GenericClass
+// CHECK:STDOUT:     import PackageGenericClass//default
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %PackageHasParam.AnyParam: %AnyParam.type = import_ref PackageHasParam//default, AnyParam, loaded [concrete = constants.%AnyParam.generic]
+// CHECK:STDOUT:   %PackageHasParam.import_ref.5ab: type = import_ref PackageHasParam//default, loc4_16, loaded [symbolic = @AnyParam.%T (constants.%T)]
+// CHECK:STDOUT:   %PackageHasParam.import_ref.34c: @AnyParam.%T (%T) = import_ref PackageHasParam//default, loc4_26, loaded [symbolic = @AnyParam.%X (constants.%X)]
+// CHECK:STDOUT:   %PackageHasParam.import_ref.8f2: <witness> = import_ref PackageHasParam//default, loc4_34, loaded [concrete = constants.%complete_type]
+// CHECK:STDOUT:   %PackageHasParam.import_ref.601 = import_ref PackageHasParam//default, inst31 [no loc], unloaded
+// CHECK:STDOUT:   %PackageGenericClass.GenericClass: %GenericClass.type = import_ref PackageGenericClass//default, GenericClass, loaded [concrete = constants.%GenericClass.generic]
+// CHECK:STDOUT:   %PackageGenericClass.import_ref.5ab: type = import_ref PackageGenericClass//default, loc6_20, loaded [symbolic = @GenericClass.%U (constants.%U)]
+// CHECK:STDOUT:   %PackageGenericClass.import_ref.8f2: <witness> = import_ref PackageGenericClass//default, loc6_31, loaded [concrete = constants.%complete_type]
+// CHECK:STDOUT:   %PackageGenericClass.import_ref.065 = import_ref PackageGenericClass//default, inst26 [no loc], unloaded
+// CHECK:STDOUT:   %PackageHasParam.Y: type = import_ref PackageHasParam//default, Y, loaded [concrete = constants.%Y.type]
+// CHECK:STDOUT:   %PackageHasParam.import_ref.dc1 = import_ref PackageHasParam//default, inst37 [no loc], unloaded
+// CHECK:STDOUT:   %PackageHasParam.import_ref.5e7: %Y.assoc_type = import_ref PackageHasParam//default, loc7_22, loaded [concrete = constants.%assoc0]
+// CHECK:STDOUT:   %PackageHasParam.K = import_ref PackageHasParam//default, K, unloaded
+// CHECK:STDOUT:   %PackageHasParam.import_ref.292: %Y.type = import_ref PackageHasParam//default, inst37 [no loc], loaded [symbolic = constants.%Self]
+// CHECK:STDOUT:   %PackageGenericClass.import_ref.f23: <witness> = import_ref PackageGenericClass//default, loc8_66, loaded [concrete = constants.%impl_witness]
+// CHECK:STDOUT:   %PackageGenericClass.import_ref.a0e: type = import_ref PackageGenericClass//default, loc8_43, loaded [concrete = constants.%AnyParam.d71]
+// CHECK:STDOUT:   %PackageGenericClass.import_ref.ca6: type = import_ref PackageGenericClass//default, loc8_63, loaded [concrete = constants.%Y.type]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .PackageHasParam = imports.%PackageHasParam
+// CHECK:STDOUT:     .PackageGenericClass = imports.%PackageGenericClass
+// CHECK:STDOUT:     .M = %M.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %PackageHasParam.import = import PackageHasParam
+// CHECK:STDOUT:   %PackageGenericClass.import = import PackageGenericClass
+// CHECK:STDOUT:   %M.decl: %M.type = fn_decl @M [concrete = constants.%M] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Y [from "has_param.carbon"] {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = imports.%PackageHasParam.import_ref.dc1
+// CHECK:STDOUT:   .K = imports.%PackageHasParam.import_ref.5e7
+// CHECK:STDOUT:   witness = (imports.%PackageHasParam.K)
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl: imports.%PackageGenericClass.import_ref.a0e as imports.%PackageGenericClass.import_ref.ca6 [from "has_generic_class.carbon"] {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   witness = imports.%PackageGenericClass.import_ref.f23
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic class @AnyParam(imports.%PackageHasParam.import_ref.5ab: type, imports.%PackageHasParam.import_ref.34c: @AnyParam.%T (%T)) [from "has_param.carbon"] {
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T, 0 [symbolic = %T (constants.%T)]
+// CHECK:STDOUT:   %T.patt: type = symbolic_binding_pattern T, 0 [symbolic = %T.patt (constants.%T.patt)]
+// CHECK:STDOUT:   %X: %T = bind_symbolic_name X, 1 [symbolic = %X (constants.%X)]
+// CHECK:STDOUT:   %X.patt: %T = symbolic_binding_pattern X, 1 [symbolic = %X.patt (constants.%X.patt.51ccc0.2)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:
+// CHECK:STDOUT:   class {
+// CHECK:STDOUT:     complete_type_witness = imports.%PackageHasParam.import_ref.8f2
+// CHECK:STDOUT:
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     .Self = imports.%PackageHasParam.import_ref.601
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic class @GenericClass(imports.%PackageGenericClass.import_ref.5ab: type) [from "has_generic_class.carbon"] {
+// CHECK:STDOUT:   %U: type = bind_symbolic_name U, 0 [symbolic = %U (constants.%U)]
+// CHECK:STDOUT:   %U.patt: type = symbolic_binding_pattern U, 0 [symbolic = %U.patt (constants.%U.patt)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:
+// CHECK:STDOUT:   class {
+// CHECK:STDOUT:     complete_type_witness = imports.%PackageGenericClass.import_ref.8f2
+// CHECK:STDOUT:
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     .Self = imports.%PackageGenericClass.import_ref.065
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @M() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   name_binding_decl {
+// CHECK:STDOUT:     %obj.patt: %AnyParam.d71 = binding_pattern obj
+// CHECK:STDOUT:     %.loc8_3.1: %AnyParam.d71 = var_pattern %obj.patt
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %obj.var: ref %AnyParam.d71 = var obj
+// CHECK:STDOUT:   %.loc8_74.1: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:   %.loc8_74.2: init %AnyParam.d71 = class_init (), %obj.var [concrete = constants.%AnyParam.val]
+// CHECK:STDOUT:   %.loc8_3.2: init %AnyParam.d71 = converted %.loc8_74.1, %.loc8_74.2 [concrete = constants.%AnyParam.val]
+// CHECK:STDOUT:   assign %obj.var, %.loc8_3.2
+// CHECK:STDOUT:   %.loc8_69: type = splice_block %AnyParam [concrete = constants.%AnyParam.d71] {
+// CHECK:STDOUT:     %PackageHasParam.ref.loc8: <namespace> = name_ref PackageHasParam, imports.%PackageHasParam [concrete = imports.%PackageHasParam]
+// CHECK:STDOUT:     %AnyParam.ref: %AnyParam.type = name_ref AnyParam, imports.%PackageHasParam.AnyParam [concrete = constants.%AnyParam.generic]
+// CHECK:STDOUT:     %PackageGenericClass.ref: <namespace> = name_ref PackageGenericClass, imports.%PackageGenericClass [concrete = imports.%PackageGenericClass]
+// CHECK:STDOUT:     %GenericClass.ref: %GenericClass.type = name_ref GenericClass, imports.%PackageGenericClass.GenericClass [concrete = constants.%GenericClass.generic]
+// CHECK:STDOUT:     %AnyParam: type = class_type @AnyParam, @AnyParam(constants.%GenericClass.type, constants.%GenericClass.generic) [concrete = constants.%AnyParam.d71]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %obj: ref %AnyParam.d71 = bind_name obj, %obj.var
+// CHECK:STDOUT:   %obj.ref: ref %AnyParam.d71 = name_ref obj, %obj
+// CHECK:STDOUT:   %PackageHasParam.ref.loc9: <namespace> = name_ref PackageHasParam, imports.%PackageHasParam [concrete = imports.%PackageHasParam]
+// CHECK:STDOUT:   %Y.ref: type = name_ref Y, imports.%PackageHasParam.Y [concrete = constants.%Y.type]
+// CHECK:STDOUT:   %K.ref: %Y.assoc_type = name_ref K, imports.%PackageHasParam.import_ref.5e7 [concrete = constants.%assoc0]
+// CHECK:STDOUT:   %impl.elem0: %.3b2 = impl_witness_access constants.%impl_witness, element0 [concrete = constants.%K.039]
+// CHECK:STDOUT:   %bound_method: <bound method> = bound_method %obj.ref, %impl.elem0
+// CHECK:STDOUT:   %.loc9: %AnyParam.d71 = bind_value %obj.ref
+// CHECK:STDOUT:   %K.call: init %empty_tuple.type = call %bound_method(%.loc9)
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic fn @K.1(imports.%PackageHasParam.import_ref.292: %Y.type) [from "has_param.carbon"] {
+// CHECK:STDOUT:   %Self: %Y.type = bind_symbolic_name Self, 0 [symbolic = %Self (constants.%Self)]
+// CHECK:STDOUT:   %Self.as_type: type = facet_access_type %Self [symbolic = %Self.as_type (constants.%Self.as_type)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %require_complete: <witness> = require_complete_type @K.1.%Self.as_type (%Self.as_type) [symbolic = %require_complete (constants.%require_complete)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   fn[%self.param_patt: @K.1.%Self.as_type (%Self.as_type)]();
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @K.2[%self.param_patt: %AnyParam.d71]() [from "has_generic_class.carbon"];
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @AnyParam(constants.%T, constants.%X) {
+// CHECK:STDOUT:   %T => constants.%T
+// CHECK:STDOUT:   %T.patt => constants.%T
+// CHECK:STDOUT:   %X => constants.%X
+// CHECK:STDOUT:   %X.patt => constants.%X
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @GenericClass(constants.%U) {
+// CHECK:STDOUT:   %U => constants.%U
+// CHECK:STDOUT:   %U.patt => constants.%U
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @AnyParam(constants.%GenericClass.type, constants.%GenericClass.generic) {
+// CHECK:STDOUT:   %T => constants.%GenericClass.type
+// CHECK:STDOUT:   %T.patt => constants.%GenericClass.type
+// CHECK:STDOUT:   %X => constants.%GenericClass.generic
+// CHECK:STDOUT:   %X.patt => constants.%GenericClass.generic
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @K.1(constants.%Self) {
+// CHECK:STDOUT:   %Self => constants.%Self
+// CHECK:STDOUT:   %Self.as_type => constants.%Self.as_type
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
 // CHECK:STDOUT: --- has_extra_interfaces.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {

+ 433 - 0
toolchain/check/type_structure.cpp

@@ -0,0 +1,433 @@
+// 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 "toolchain/check/type_structure.h"
+
+#include <variant>
+
+#include "toolchain/base/kind_switch.h"
+#include "toolchain/check/context.h"
+#include "toolchain/sem_ir/constant.h"
+#include "toolchain/sem_ir/facet_type_info.h"
+#include "toolchain/sem_ir/ids.h"
+#include "toolchain/sem_ir/impl.h"
+#include "toolchain/sem_ir/typed_insts.h"
+
+namespace Carbon::Check {
+
+auto TypeStructure::IsCompatibleWith(const TypeStructure& other) const -> bool {
+  const auto& lhs = structure_;
+  const auto& rhs = other.structure_;
+
+  const auto* lhs_cursor = lhs.begin();
+  const auto* rhs_cursor = rhs.begin();
+
+  while (true) {
+    // If both structures end at the same time, they match.
+    if (lhs_cursor == lhs.end() && rhs_cursor == rhs.end()) {
+      return true;
+    }
+    // If one structure ends sooner than the other, they don't match.
+    if (lhs_cursor == lhs.end() || rhs_cursor == rhs.end()) {
+      return false;
+    }
+    // Same structural element on both sides, they match and both are consumed.
+    //
+    // TODO: If we kept the constant value of the concrete element in the type
+    // structure, then we could compare them and use that to eliminate matching
+    // impls that are not actually compatible.
+    if (*lhs_cursor == *rhs_cursor) {
+      ++lhs_cursor;
+      ++rhs_cursor;
+      continue;
+    }
+    // If the element on each side is concrete but they not the same structural
+    // shape, then the structures don't match.
+    if (*lhs_cursor != Structural::Symbolic &&
+        *rhs_cursor != Structural::Symbolic) {
+      return false;
+    }
+
+    // From here we know one side is a Symbolic and the other is not. We can
+    // match the Symbolic against either a single Concrete or a larger bracketed
+    // set of Concrete structural elements.
+
+    // Returns false if the lhs and rhs can not match, true if we should
+    // continue checking for compatibility.
+    auto consume_symbolic = [](const auto*& lhs_cursor,
+                               const auto*& rhs_cursor) -> bool {
+      // Consume the symbolic on the RHS.
+      ++rhs_cursor;
+
+      // The symbolic on the RHS is in the same position as a close paren on the
+      // LHS, which means the structures can not match.
+      //
+      // Example:
+      // - ((c))
+      // - ((c?))
+      if (*lhs_cursor == Structural::ConcreteCloseParen) {
+        return false;
+      }
+
+      // There's either a Concrete element or an open paren on the LHS. If it's
+      // the former, the Symbolic just matches with it. If it's the latter, the
+      // Symbolic matches with everything on the LHS up to the matching closing
+      // paren.
+      CARBON_CHECK(*lhs_cursor == Structural::Concrete ||
+                   *lhs_cursor == Structural::ConcreteOpenParen);
+      int depth = 0;
+      do {
+        switch (*lhs_cursor) {
+          case Structural::ConcreteOpenParen:
+            depth += 1;
+            break;
+          case Structural::ConcreteCloseParen:
+            depth -= 1;
+            break;
+          case Structural::Concrete:
+            break;
+          case Structural::Symbolic:
+            break;
+        }
+        ++lhs_cursor;
+      } while (depth > 0);
+      return true;
+    };
+
+    // We move the symbolic to the RHS to make only one case to handle in the
+    // lambda.
+    if (*lhs_cursor == Structural::Symbolic) {
+      if (!consume_symbolic(rhs_cursor, lhs_cursor)) {
+        return false;
+      }
+    } else {
+      if (!consume_symbolic(lhs_cursor, rhs_cursor)) {
+        return false;
+      }
+    }
+  }
+
+  return true;
+}
+
+// A class that builds a `TypeStructure` for an `Impl`, or an impl lookup query,
+// that represents its self type and interface.
+class TypeStructureBuilder {
+ public:
+  explicit TypeStructureBuilder(Context& context) : context_(context) {}
+
+  auto Run(SemIR::InstId self_inst_id,
+           SemIR::SpecificInterface interface_constraint) -> TypeStructure {
+    CARBON_CHECK(work_list_.empty());
+
+    first_symbolic_distance_ = TypeStructure::InfiniteDistance;
+    structure_.clear();
+
+    // The self type comes first in the type structure, so we push it last, as
+    // the queue works from the back.
+    Push(interface_constraint);
+    PushInstId(self_inst_id);
+    BuildTypeStructure();
+
+    return TypeStructure(std::exchange(structure_, {}),
+                         first_symbolic_distance_);
+  }
+
+ private:
+  auto BuildTypeStructure() -> void {
+    while (!work_list_.empty()) {
+      auto next = work_list_.back();
+      work_list_.pop_back();
+
+      if (std::holds_alternative<CloseType>(next)) {
+        AppendStructural(TypeStructure::Structural::ConcreteCloseParen);
+        continue;
+      }
+
+      if (const auto* interface =
+              std::get_if<SemIR::SpecificInterface>(&next)) {
+        auto args = GetSpecificArgs(interface->specific_id);
+        if (args.empty()) {
+          AppendStructural(TypeStructure::Structural::Concrete);
+        } else {
+          AppendStructural(TypeStructure::Structural::ConcreteOpenParen);
+          Push(CloseType());
+          PushArgs(args);
+        }
+        continue;
+      }
+
+      if (std::holds_alternative<SymbolicType>(next)) {
+        AppendStructural(TypeStructure::Structural::Symbolic);
+        continue;
+      }
+
+      if (std::holds_alternative<NonTypeValue>(next)) {
+        // TODO: Include the value's type into the structure, with the type
+        // coming first and paired together with the value, like:
+        // `{TypeWithPossibleNestedTypes, Concrete}`.
+        // We might want a different bracket marker than ConcreteOpenParen for
+        // this so that it can look different in the type structure when dumped.
+        AppendStructural(TypeStructure::Structural::Concrete);
+        continue;
+      }
+
+      SemIR::TypeId next_type_id = std::get<SemIR::TypeId>(next);
+      auto inst_id = context_.types().GetInstId(next_type_id);
+      auto inst = context_.insts().Get(inst_id);
+      CARBON_KIND_SWITCH(inst) {
+          // ==== Symbolic types ====
+
+        case SemIR::BindSymbolicName::Kind:
+        case SemIR::SymbolicBindingPattern::Kind:
+        case SemIR::FacetAccessType::Kind: {
+          Push(SymbolicType());
+          break;
+        }
+
+          // ==== Concrete types ====
+
+        case SemIR::AssociatedEntityType::Kind:
+        case SemIR::BoolType::Kind:
+        case SemIR::FloatType::Kind:
+        case SemIR::GenericClassType::Kind:
+        case SemIR::GenericInterfaceType::Kind:
+        case SemIR::ImplWitnessAccess::Kind:
+        case SemIR::IntLiteralType::Kind:
+        case SemIR::LegacyFloatType::Kind:
+        case SemIR::StringType::Kind:
+        case SemIR::TypeType::Kind: {
+          AppendStructural(TypeStructure::Structural::Concrete);
+          break;
+        }
+
+        case CARBON_KIND(SemIR::FacetType facet_type): {
+          (void)facet_type;
+          // A `FacetType` instruction shows up in the self type of impl lookup
+          // queries like `C(D)` where `C` requires its parameter to satisfy
+          // some `FacetType` `Z`. The `D` argument is converted to a
+          // `FacetValue` satisfying `Z`, and the type of `C` in the self type
+          // has a specific with the type of that `FacetValue`, which is the
+          // `FacetType` satisfying `Z` we see here.
+          //
+          // The `FacetValue` may still be symbolic in generic code but its
+          // type, the `FacetType` here, is concrete.
+          AppendStructural(TypeStructure::Structural::Concrete);
+          break;
+        }
+        case CARBON_KIND(SemIR::IntType int_type): {
+          if (context_.constant_values().Get(inst_id).is_concrete()) {
+            AppendStructural(TypeStructure::Structural::Concrete);
+          } else {
+            AppendStructural(TypeStructure::Structural::ConcreteOpenParen);
+            Push(CloseType());
+            PushArgs({int_type.bit_width_id});
+          }
+          break;
+        }
+
+          // ==== Aggregate types ====
+
+        case CARBON_KIND(SemIR::ArrayType array_type): {
+          AppendStructural(TypeStructure::Structural::ConcreteOpenParen);
+          Push(CloseType());
+          Push(array_type.element_type_id);
+          PushInstId(array_type.bound_id);
+          break;
+        }
+        case CARBON_KIND(SemIR::ClassType class_type): {
+          auto args = GetSpecificArgs(class_type.specific_id);
+          if (args.empty()) {
+            AppendStructural(TypeStructure::Structural::Concrete);
+          } else {
+            AppendStructural(TypeStructure::Structural::ConcreteOpenParen);
+            Push(CloseType());
+            PushArgs(args);
+          }
+          break;
+        }
+        case CARBON_KIND(SemIR::ConstType const_type): {
+          // We don't put the `const` into the type structure since it is a
+          // modifier; just move to the inner type.
+          Push(const_type.inner_id);
+          break;
+        }
+        case CARBON_KIND(SemIR::PointerType pointer_type): {
+          AppendStructural(TypeStructure::Structural::ConcreteOpenParen);
+          Push(CloseType());
+          Push(pointer_type.pointee_id);
+          break;
+        }
+        case CARBON_KIND(SemIR::TupleType tuple_type): {
+          auto inner_types = context_.type_blocks().Get(tuple_type.elements_id);
+          if (inner_types.empty()) {
+            AppendStructural(TypeStructure::Structural::Concrete);
+          } else {
+            AppendStructural(TypeStructure::Structural::ConcreteOpenParen);
+            Push(CloseType());
+            for (auto type :
+                 context_.type_blocks().Get(tuple_type.elements_id)) {
+              Push(type);
+            }
+          }
+          break;
+        }
+        case CARBON_KIND(SemIR::StructType struct_type): {
+          auto fields =
+              context_.struct_type_fields().Get(struct_type.fields_id);
+          if (fields.empty()) {
+            AppendStructural(TypeStructure::Structural::Concrete);
+          } else {
+            AppendStructural(TypeStructure::Structural::ConcreteOpenParen);
+            Push(CloseType());
+            for (const auto& field : fields) {
+              Push(field.type_id);
+            }
+          }
+          break;
+        }
+        case CARBON_KIND(SemIR::TypeOfInst type_of): {
+          (void)type_of;
+          auto const_id = context_.constant_values().Get(inst_id);
+          // TODO: TypeOfInst should not be encountered in impl lookup with a
+          // template-dependent value, since impl lookup on such values should
+          // be deferred to type-checking the specific. So this should become a
+          // CARBON_FATAL code path. For now it is possible, though, to reach
+          // here and it results in a diagnostic.
+          //
+          // However, TypeOfInst can be determined to be a concrete value for a
+          // template value with a non-template dependent type. This currently
+          // does not happen though, the TypeOfInst is always template
+          // dependent. If it does in the future then we will want to use the
+          // concrete constant value instruction of `inst_id` in the type
+          // structure:
+          //   if (const_id.is_concrete()) {
+          //     PushInstId(context_.constant_values().GetInstId(const_id));
+          //   }
+          //
+          // If TypeOfInst was used for more general metaprogramming then we may
+          // need to handle both concrete and perhaps symbolic (non-template)
+          // values of TypeOfInst.
+          CARBON_CHECK(const_id.is_symbolic());
+          auto sym = context_.constant_values().GetSymbolicConstant(const_id);
+          CARBON_CHECK(sym.dependence == SemIR::ConstantDependence::Template,
+                       "TypeOfInst with non-template symbolic value?");
+          context_.TODO(context_.insts().GetLocId(inst_id),
+                        "Impl lookup on template-dependent type value");
+          break;
+        }
+        default:
+          CARBON_FATAL("Unhandled type instruction {0}", inst_id);
+      }
+    }
+  }
+
+  // A work item to mark the closing paren for an aggregate concrete type.
+  struct CloseType {};
+  // A work item to mark a symbolic type.
+  struct SymbolicType {};
+  // A work item to mark a non-type value.
+  struct NonTypeValue {};
+
+  using WorkItem = std::variant<SemIR::TypeId, SymbolicType, NonTypeValue,
+                                SemIR::SpecificInterface, CloseType>;
+
+  // Get the TypeId for an instruction that is not a facet value, otherwise
+  // return SymbolicType to indicate the instruction is a symbolic facet value.
+  //
+  // If the instruction is not a type value, the return is TypeId::None.
+  //
+  // We reuse the `SymbolicType` work item here to give a nice return type.
+  auto TryGetInstIdAsTypeId(SemIR::InstId inst_id) const
+      -> std::variant<SemIR::TypeId, SymbolicType> {
+    if (auto facet_value =
+            context_.insts().TryGetAs<SemIR::FacetValue>(inst_id)) {
+      inst_id = facet_value->type_inst_id;
+    }
+
+    auto type_id_of_inst_id = context_.insts().Get(inst_id).type_id();
+    // All instructions of type FacetType are symbolic except for FacetValue:
+    // - In non-generic code, values of type FacetType are only created through
+    //   conversion to a FacetType (e.g. `Class as Iface`), which produces a
+    //   non-symbolic FacetValue.
+    // - In generic code, binding values of type FacetType are symbolic as they
+    //   refer to an unknown type. Non-binding values would be FacetValues like
+    //   in non-generic code, but would be symbolic as well.
+    // - In specifics of generic code, when deducing a value for a symbolic
+    //   binding of type FacetType, we always produce a FacetValue (which may or
+    //   may not itself be symbolic) through conversion.
+    //
+    // FacetValues are handled earlier by getting the type instruction from
+    // them. That type instruction is never of type FacetType. If it refers to a
+    // FacetType it does so through a FacetAccessType, which is of type TypeType
+    // and thus does not match here.
+    if (context_.types().Is<SemIR::FacetType>(type_id_of_inst_id)) {
+      return SymbolicType();
+    }
+    // Non-type values are concrete, only types are symbolic.
+    if (type_id_of_inst_id != SemIR::TypeType::SingletonTypeId) {
+      return SemIR::TypeId::None;
+    }
+    return context_.types().GetTypeIdForTypeInstId(inst_id);
+  }
+
+  // Get the instructions in the specific's instruction block as an ArrayRef.
+  auto GetSpecificArgs(SemIR::SpecificId specific_id)
+      -> llvm::ArrayRef<SemIR::InstId> {
+    if (specific_id == SemIR::SpecificId::None) {
+      return {};
+    }
+    auto specific = context_.specifics().Get(specific_id);
+    return context_.inst_blocks().Get(specific.args_id);
+  }
+
+  // Push all arguments from the array into the work queue.
+  auto PushArgs(llvm::ArrayRef<SemIR::InstId> args) -> void {
+    for (auto arg_id : llvm::reverse(args)) {
+      PushInstId(arg_id);
+    }
+  }
+
+  // Push an instruction's type value into the work queue, or a marker if the
+  // instruction has a symbolic value.
+  auto PushInstId(SemIR::InstId inst_id) -> void {
+    auto maybe_type_id = TryGetInstIdAsTypeId(inst_id);
+    if (std::holds_alternative<SymbolicType>(maybe_type_id)) {
+      Push(SymbolicType());
+    } else if (auto type_id = std::get<SemIR::TypeId>(maybe_type_id);
+               type_id.has_value()) {
+      Push(type_id);
+    } else {
+      Push(NonTypeValue());
+    }
+  }
+
+  // Push the next step into the work queue.
+  auto Push(WorkItem item) -> void { work_list_.push_back(item); }
+
+  // Append a structural element to the TypeStructure being built.
+  auto AppendStructural(TypeStructure::Structural structural) -> void {
+    if (structural == TypeStructure::Structural::Symbolic) {
+      // Sets the `distance` in `first_symbolic_distance_` if it does not
+      // already have a non-infinite value.
+      if (first_symbolic_distance_ == TypeStructure::InfiniteDistance) {
+        first_symbolic_distance_ = structure_.size();
+      }
+    }
+    structure_.push_back(structural);
+  }
+
+  Context& context_;
+  llvm::SmallVector<WorkItem> work_list_;
+  int first_symbolic_distance_;
+  llvm::SmallVector<TypeStructure::Structural> structure_;
+};
+
+auto BuildTypeStructure(Context& context, SemIR::InstId self_inst_id,
+                        SemIR::SpecificInterface interface) -> TypeStructure {
+  TypeStructureBuilder builder(context);
+  return builder.Run(self_inst_id, interface);
+}
+
+}  // namespace Carbon::Check

+ 107 - 0
toolchain/check/type_structure.h

@@ -0,0 +1,107 @@
+// 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
+
+#ifndef CARBON_TOOLCHAIN_CHECK_TYPE_STRUCTURE_H_
+#define CARBON_TOOLCHAIN_CHECK_TYPE_STRUCTURE_H_
+
+#include <compare>
+
+#include "common/ostream.h"
+#include "toolchain/check/context.h"
+#include "toolchain/sem_ir/ids.h"
+#include "toolchain/sem_ir/impl.h"
+
+namespace Carbon::Check {
+
+// The "type structure" for an impl declaration.
+//
+// See
+// https://docs.carbon-lang.dev/docs/design/generics/overview.html#parameterized-impl-declarations.
+//
+// Type structures are ordered, and a type structure that is ordered higher is a
+// better, more specified, match.
+class TypeStructure : public Printable<TypeStructure> {
+ public:
+  // Returns whether the type structure is compatible with `other`. If false,
+  // they can not possibly match with one being an `impl` for the other as a
+  // lookup query.
+  auto IsCompatibleWith(const TypeStructure& other) const -> bool;
+
+  // Ordering of type structures. A higher value is a better match.
+  friend auto operator<=>(const TypeStructure& lhs, const TypeStructure& rhs)
+      -> std::weak_ordering {
+    // Higher distance is a better match, and `InfiniteDistance` is treated
+    // specially as the best possible match.
+    if (lhs.distance_to_first_symbolic_type_ !=
+        rhs.distance_to_first_symbolic_type_) {
+      if (lhs.distance_to_first_symbolic_type_ == InfiniteDistance) {
+        return std::weak_ordering::greater;
+      } else if (rhs.distance_to_first_symbolic_type_ == InfiniteDistance) {
+        return std::weak_ordering::less;
+      } else {
+        return lhs.distance_to_first_symbolic_type_ <=>
+               rhs.distance_to_first_symbolic_type_;
+      }
+    }
+    // TODO: If they have a symbolic in the same position, we could use further
+    // symbolic types to get an ordering.
+    return std::weak_ordering::equivalent;
+  }
+
+  auto Print(llvm::raw_ostream& out) const -> void {
+    out << "TypeStructure = ";
+    for (auto s : structure_) {
+      switch (s) {
+        case Structural::Concrete:
+          out << 'c';
+          break;
+        case Structural::Symbolic:
+          out << '?';
+          break;
+        case Structural::ConcreteOpenParen:
+          out << "(";
+          break;
+        case Structural::ConcreteCloseParen:
+          out << ')';
+          break;
+      }
+    }
+  }
+
+ private:
+  friend class TypeStructureBuilder;
+
+  enum class Structural : uint8_t {
+    Concrete,
+    ConcreteOpenParen,
+    ConcreteCloseParen,
+    Symbolic,
+  };
+
+  static constexpr int InfiniteDistance = -1;
+
+  TypeStructure(llvm::SmallVector<Structural> structure,
+                int distance_to_first_symbolic_type)
+      : structure_(std::move(structure)),
+        distance_to_first_symbolic_type_(distance_to_first_symbolic_type) {}
+
+  // The structural position of concrete and symbolic values in the type.
+  llvm::SmallVector<Structural> structure_;
+
+  // Number of concrete types traversed before finding a symbolic type.
+  int distance_to_first_symbolic_type_;
+};
+
+// Constructs the TypeStructure for a self type or facet value and an interface
+// constraint (e.g. `Iface(A, B(C))`), which represents the location of unknown
+// symbolic values in the combined signature and which is ordered by them.
+//
+// Given `impl C as Z {}` the `self_const_id` would be a `C` and the interface
+// constraint would be `Z`.
+auto BuildTypeStructure(Context& context, SemIR::InstId self_inst_id,
+                        SemIR::SpecificInterface interface) -> TypeStructure;
+
+}  // namespace Carbon::Check
+
+#endif  // CARBON_TOOLCHAIN_CHECK_TYPE_STRUCTURE_H_

+ 1 - 1
toolchain/sem_ir/constant.h

@@ -43,7 +43,7 @@ struct SymbolicConstant : Printable<SymbolicConstant> {
   // constants, or `None` if `generic_id` is `None`.
   GenericInstIndex index;
   // The kind of dependence this symbolic constant exhibits. Should never be
-  // `Concrete`.
+  // `None`.
   ConstantDependence dependence;
 
   auto Print(llvm::raw_ostream& out) const -> void {

+ 3 - 1
toolchain/sem_ir/typed_insts.h

@@ -708,7 +708,9 @@ struct FacetType {
 // of witnesses that it satisfies the required interfaces of the facet type.
 struct FacetValue {
   static constexpr auto Kind = InstKind::FacetValue.Define<Parse::NodeId>(
-      {.ir_name = "facet_value", .constant_kind = InstConstantKind::Always});
+      {.ir_name = "facet_value",
+       .constant_kind = InstConstantKind::Always,
+       .deduce_through = true});
 
   // A `FacetType`.
   TypeId type_id;