浏览代码

Identify and complete facet types as needed for p5168 (#6369)

Proposal #5168 defines when a facet type must be identified or complete,
and what it means for an interface and a named constraint to be
identified or complete. This updates the toolchain to match the
requirements.

This implements identification of a facet type to require completed
named constraints and to include any interfaces from named constraints
into the resulting IdentifiedFacetType.

To complete a facet type, each interface in the IdentifiedFacetType, and
any interface named though a require declaration from them, must be
complete.
Dana Jansens 5 月之前
父节点
当前提交
e62678e682

+ 13 - 0
toolchain/check/handle_require.cpp

@@ -13,6 +13,7 @@
 #include "toolchain/check/subst.h"
 #include "toolchain/check/type.h"
 #include "toolchain/check/type_completion.h"
+#include "toolchain/diagnostics/diagnostic.h"
 #include "toolchain/parse/node_ids.h"
 #include "toolchain/sem_ir/ids.h"
 #include "toolchain/sem_ir/named_constraint.h"
@@ -81,6 +82,15 @@ auto HandleParseNode(Context& context, Parse::RequireDefaultSelfImplsId node_id)
 auto HandleParseNode(Context& context, Parse::RequireTypeImplsId node_id)
     -> bool {
   auto [self_node_id, self_inst_id] = context.node_stack().PopExprWithNodeId();
+
+  auto introducer = context.decl_introducer_state_stack().innermost();
+  if (introducer.modifier_set.HasAnyOf(KeywordModifierSet::Extend)) {
+    CARBON_DIAGNOSTIC(RequireImplsExtendWithExplicitSelf, Error,
+                      "`extend require impls` with explicit type");
+    context.emitter().Emit(self_node_id, RequireImplsExtendWithExplicitSelf);
+    self_inst_id = SemIR::ErrorInst::InstId;
+  }
+
   auto self_type = ExprAsType(context, self_node_id, self_inst_id);
   context.node_stack().Push(node_id, self_type.inst_id);
   return true;
@@ -241,6 +251,8 @@ auto HandleParseNode(Context& context, Parse::RequireDeclId node_id) -> bool {
     return true;
   }
 
+  bool extend = introducer.modifier_set.HasAnyOf(KeywordModifierSet::Extend);
+
   auto require_impls_decl =
       SemIR::RequireImplsDecl{// To be filled in after.
                               .require_impls_id = SemIR::RequireImplsId::None,
@@ -251,6 +263,7 @@ auto HandleParseNode(Context& context, Parse::RequireDeclId node_id) -> bool {
        .facet_type_inst_id =
            context.types().GetAsTypeInstId(constraint_inst_id),
        .facet_type_id = constraint_facet_type.facet_type_id,
+       .extend_self = extend,
        .decl_id = decl_id,
        .parent_scope_id = context.scope_stack().PeekNameScopeId(),
        .generic_id = BuildGenericDecl(context, decl_id)});

+ 1 - 0
toolchain/check/impl.cpp

@@ -121,6 +121,7 @@ auto ImplWitnessStartDefinition(Context& context, SemIR::Impl& impl) -> void {
       witness_block.empty()) {
     if (!RequireCompleteFacetTypeForImplDefinition(
             context, SemIR::LocId(impl.latest_decl_id()), impl.constraint_id)) {
+      FillImplWitnessWithErrors(context, impl);
       return;
     }
 

+ 4 - 4
toolchain/check/impl_lookup.cpp

@@ -203,10 +203,10 @@ static auto GetInterfacesFromConstantId(
       context.facet_types().Get(facet_type_inst.facet_type_id);
   auto identified_id =
       RequireIdentifiedFacetType(context, facet_type_inst, [&] {
-        CARBON_DIAGNOSTIC(ImplLookupInIncompleteFacetType, Error,
-                          "facet type {0} is incomplete", InstIdAsType);
-        return context.emitter().Build(loc_id, ImplLookupInIncompleteFacetType,
-                                       facet_type_inst_id);
+        CARBON_DIAGNOSTIC(ImplLookupInUnidentifiedFacetType, Error,
+                          "facet type {0} can not be identified", InstIdAsType);
+        return context.emitter().Build(
+            loc_id, ImplLookupInUnidentifiedFacetType, facet_type_inst_id);
       });
   if (!identified_id.has_value()) {
     return std::nullopt;

+ 1 - 0
toolchain/check/import_ref.cpp

@@ -2468,6 +2468,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
       resolver.local_insts().GetAs<SemIR::FacetType>(
           new_canonical_facet_type_inst_id);
   new_require.facet_type_id = new_canonical_facet_type.facet_type_id;
+  new_require.extend_self = import_require.extend_self;
   new_require.parent_scope_id = parent_scope_id;
 
   SetGenericData(resolver, import_require.generic_id, new_require.generic_id,

+ 95 - 76
toolchain/check/testdata/facet/fail_incomplete.carbon

@@ -2,7 +2,7 @@
 // Exceptions. See /LICENSE for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
-// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon
+// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon
 //
 // AUTOUPDATE
 // TIP: To test this file alone, run:
@@ -13,95 +13,114 @@
 // --- fail_incomplete_interface.carbon
 library "[[@TEST_NAME]]";
 
-interface A;
-interface B {}
+interface X;
 class C {}
 
-fn G[T:! A](t: T) {}
-fn H[T:! A & B](t: T) {}
+// Requires X identified.
+impl C as X;
 
-fn F() {
-  // CHECK:STDERR: fail_incomplete_interface.carbon:[[@LINE+4]]:3: error: cannot convert type `C` into type implementing `A` [ConversionFailureTypeToFacet]
-  // CHECK:STDERR:   C as A;
-  // CHECK:STDERR:   ^~~~~~
-  // CHECK:STDERR:
-  C as A;
+// Requires X complete.
+// CHECK:STDERR: fail_incomplete_interface.carbon:[[@LINE+7]]:1: error: definition of impl as incomplete facet type `X` [ImplAsIncompleteFacetTypeDefinition]
+// CHECK:STDERR: impl C as X {}
+// CHECK:STDERR: ^~~~~~~~~~~~~
+// CHECK:STDERR: fail_incomplete_interface.carbon:[[@LINE-10]]:1: note: interface was forward declared here [InterfaceForwardDeclaredHere]
+// CHECK:STDERR: interface X;
+// CHECK:STDERR: ^~~~~~~~~~~~
+// CHECK:STDERR:
+impl C as X {}
 
-  // CHECK:STDERR: fail_incomplete_interface.carbon:[[@LINE+4]]:3: error: cannot convert type `C` into type implementing `A & B` [ConversionFailureTypeToFacet]
-  // CHECK:STDERR:   C as (A & B);
-  // CHECK:STDERR:   ^~~~~~~~~~~~
-  // CHECK:STDERR:
-  C as (A & B);
-
-  // CHECK:STDERR: fail_incomplete_interface.carbon:[[@LINE+7]]:3: error: cannot convert type `C` into type implementing `A` [ConversionFailureTypeToFacet]
-  // CHECK:STDERR:   G({} as C);
-  // CHECK:STDERR:   ^~~~~~~~~~
-  // CHECK:STDERR: fail_incomplete_interface.carbon:[[@LINE-19]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere]
-  // CHECK:STDERR: fn G[T:! A](t: T) {}
-  // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~
-  // CHECK:STDERR:
-  G({} as C);
-
-  // CHECK:STDERR: fail_incomplete_interface.carbon:[[@LINE+7]]:3: error: cannot convert type `C` into type implementing `A & B` [ConversionFailureTypeToFacet]
-  // CHECK:STDERR:   H({} as C);
-  // CHECK:STDERR:   ^~~~~~~~~~
-  // CHECK:STDERR: fail_incomplete_interface.carbon:[[@LINE-27]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere]
-  // CHECK:STDERR: fn H[T:! A & B](t: T) {}
-  // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~
-  // CHECK:STDERR:
-  H({} as C);
+// --- fail_incomplete_interface_without_forward_decl.carbon
+library "[[@TEST_NAME]]";
+
+interface X;
+class C {}
+
+// Requires X complete.
+// CHECK:STDERR: fail_incomplete_interface_without_forward_decl.carbon:[[@LINE+7]]:1: error: definition of impl as incomplete facet type `X` [ImplAsIncompleteFacetTypeDefinition]
+// CHECK:STDERR: impl C as X {}
+// CHECK:STDERR: ^~~~~~~~~~~~~
+// CHECK:STDERR: fail_incomplete_interface_without_forward_decl.carbon:[[@LINE-7]]:1: note: interface was forward declared here [InterfaceForwardDeclaredHere]
+// CHECK:STDERR: interface X;
+// CHECK:STDERR: ^~~~~~~~~~~~
+// CHECK:STDERR:
+impl C as X {}
+
+// --- fail_unidentified_constraint.carbon
+library "[[@TEST_NAME]]";
+
+constraint X;
+class C {}
+
+// Requires X identified.
+// CHECK:STDERR: fail_unidentified_constraint.carbon:[[@LINE+7]]:1: error: facet type `X` cannot be identified in `impl as` [ImplOfUnidentifiedFacetType]
+// CHECK:STDERR: impl C as X;
+// CHECK:STDERR: ^~~~~~~~~~~~
+// CHECK:STDERR: fail_unidentified_constraint.carbon:[[@LINE-7]]:1: note: constraint was forward declared here [NamedConstraintForwardDeclaredHere]
+// CHECK:STDERR: constraint X;
+// CHECK:STDERR: ^~~~~~~~~~~~~
+// CHECK:STDERR:
+impl C as X;
+
+// --- nested_require_incomplete_interface.carbon
+library "[[@TEST_NAME]]";
+
+interface Z;
+constraint Y {
+  require impls Z;
 }
+interface X {
+  require impls Y;
+}
+
+class C {}
+
+// Requires X identified.
+impl C as X;
+
+// Requires X complete.
+impl C as X {}
 
-// --- fail_incomplete_constraint.carbon
+// --- fail_incomplete_through_constraint.carbon
 library "[[@TEST_NAME]]";
 
-constraint A;
-interface B {}
+interface Z;
+constraint Y {
+  extend require impls Z;
+}
+
 class C {}
 
-fn G[T:! A](t: T) {}
-fn H[T:! A & B](t: T) {}
+// Requires Y identified.
+impl C as Y;
 
-fn F() {
-  // CHECK:STDERR: fail_incomplete_constraint.carbon:[[@LINE+7]]:3: error: facet type `A` is incomplete [ImplLookupInIncompleteFacetType]
-  // CHECK:STDERR:   C as A;
-  // CHECK:STDERR:   ^~~~~~
-  // CHECK:STDERR: fail_incomplete_constraint.carbon:[[@LINE-11]]:1: note: constraint was forward declared here [NamedConstraintForwardDeclaredHere]
-  // CHECK:STDERR: constraint A;
-  // CHECK:STDERR: ^~~~~~~~~~~~~
-  // CHECK:STDERR:
-  C as A;
+// Requires Y complete.
+// CHECK:STDERR: fail_incomplete_through_constraint.carbon:[[@LINE+7]]:1: error: definition of impl as incomplete facet type `Y` [ImplAsIncompleteFacetTypeDefinition]
+// CHECK:STDERR: impl C as Y {}
+// CHECK:STDERR: ^~~~~~~~~~~~~
+// CHECK:STDERR: fail_incomplete_through_constraint.carbon:[[@LINE-14]]:1: note: interface was forward declared here [InterfaceForwardDeclaredHere]
+// CHECK:STDERR: interface Z;
+// CHECK:STDERR: ^~~~~~~~~~~~
+// CHECK:STDERR:
+impl C as Y {}
 
-  // CHECK:STDERR: fail_incomplete_constraint.carbon:[[@LINE+7]]:3: error: facet type `B & A` is incomplete [ImplLookupInIncompleteFacetType]
-  // CHECK:STDERR:   C as (A & B);
-  // CHECK:STDERR:   ^~~~~~~~~~~~
-  // CHECK:STDERR: fail_incomplete_constraint.carbon:[[@LINE-20]]:1: note: constraint was forward declared here [NamedConstraintForwardDeclaredHere]
-  // CHECK:STDERR: constraint A;
-  // CHECK:STDERR: ^~~~~~~~~~~~~
-  // CHECK:STDERR:
-  C as (A & B);
+// --- fail_impl_lookup_incomplete.carbon
+library "[[@TEST_NAME]]";
 
-  // CHECK:STDERR: fail_incomplete_constraint.carbon:[[@LINE+10]]:3: error: facet type `A` is incomplete [ImplLookupInIncompleteFacetType]
-  // CHECK:STDERR:   G({} as C);
-  // CHECK:STDERR:   ^~~~~~~~~~
-  // CHECK:STDERR: fail_incomplete_constraint.carbon:[[@LINE-29]]:1: note: constraint was forward declared here [NamedConstraintForwardDeclaredHere]
-  // CHECK:STDERR: constraint A;
-  // CHECK:STDERR: ^~~~~~~~~~~~~
-  // CHECK:STDERR: fail_incomplete_constraint.carbon:[[@LINE-28]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere]
-  // CHECK:STDERR: fn G[T:! A](t: T) {}
-  // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~
-  // CHECK:STDERR:
-  G({} as C);
+constraint Z;
 
-  // CHECK:STDERR: fail_incomplete_constraint.carbon:[[@LINE+10]]:3: error: facet type `B & A` is incomplete [ImplLookupInIncompleteFacetType]
-  // CHECK:STDERR:   H({} as C);
-  // CHECK:STDERR:   ^~~~~~~~~~
-  // CHECK:STDERR: fail_incomplete_constraint.carbon:[[@LINE-41]]:1: note: constraint was forward declared here [NamedConstraintForwardDeclaredHere]
-  // CHECK:STDERR: constraint A;
+fn AsZ(T:! Z) {}
+
+fn F() {
+  // Requires Z identified.
+  // CHECK:STDERR: fail_impl_lookup_incomplete.carbon:[[@LINE+10]]:3: error: facet type `Z` can not be identified [ImplLookupInUnidentifiedFacetType]
+  // CHECK:STDERR:   AsZ(());
+  // CHECK:STDERR:   ^~~~~~~
+  // CHECK:STDERR: fail_impl_lookup_incomplete.carbon:[[@LINE-9]]:1: note: constraint was forward declared here [NamedConstraintForwardDeclaredHere]
+  // CHECK:STDERR: constraint Z;
   // CHECK:STDERR: ^~~~~~~~~~~~~
-  // CHECK:STDERR: fail_incomplete_constraint.carbon:[[@LINE-39]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere]
-  // CHECK:STDERR: fn H[T:! A & B](t: T) {}
-  // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~
+  // CHECK:STDERR: fail_impl_lookup_incomplete.carbon:[[@LINE-10]]:8: note: initializing generic parameter `T` declared here [InitializingGenericParam]
+  // CHECK:STDERR: fn AsZ(T:! Z) {}
+  // CHECK:STDERR:        ^
   // CHECK:STDERR:
-  H({} as C);
+  AsZ(());
 }

+ 13 - 0
toolchain/check/testdata/facet/require_invalid.carbon

@@ -110,3 +110,16 @@ class C {
   // CHECK:STDERR:
   require impls Y;
 }
+
+// --- fail_extend_require_type.carbon
+library "[[@TEST_NAME]]";
+
+interface Y {}
+
+interface Z {
+  // CHECK:STDERR: fail_extend_require_type.carbon:[[@LINE+4]]:18: error: `extend require impls` with explicit type [RequireImplsExtendWithExplicitSelf]
+  // CHECK:STDERR:   extend require () impls Y;
+  // CHECK:STDERR:                  ^~
+  // CHECK:STDERR:
+  extend require () impls Y;
+}

+ 86 - 45
toolchain/check/testdata/impl/impl_as_named_constraint.carbon

@@ -10,34 +10,16 @@
 // TIP: To dump output, run:
 // TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/impl_as_named_constraint.carbon
 
-// --- fail_incomplete_constraint.carbon
-library "[[@TEST_NAME]]";
-
-constraint A;
-
-class C {}
-
-// CHECK:STDERR: fail_incomplete_constraint.carbon:[[@LINE+7]]:1: error: facet type `A` cannot be identified in `impl as` [ImplOfUnidentifiedFacetType]
-// CHECK:STDERR: impl C as A {}
-// CHECK:STDERR: ^~~~~~~~~~~~~
-// CHECK:STDERR: fail_incomplete_constraint.carbon:[[@LINE-7]]:1: note: constraint was forward declared here [NamedConstraintForwardDeclaredHere]
-// CHECK:STDERR: constraint A;
-// CHECK:STDERR: ^~~~~~~~~~~~~
-// CHECK:STDERR:
-impl C as A {}
-
 // --- fail_empty_constraint.carbon
 library "[[@TEST_NAME]]";
 
 constraint A {}
 
-class C {}
-
 // CHECK:STDERR: fail_empty_constraint.carbon:[[@LINE+4]]:1: error: impl as 0 interfaces, expected 1 [ImplOfNotOneInterface]
-// CHECK:STDERR: impl C as A {}
-// CHECK:STDERR: ^~~~~~~~~~~~~
+// CHECK:STDERR: impl () as A {}
+// CHECK:STDERR: ^~~~~~~~~~~~~~
 // CHECK:STDERR:
-impl C as A {}
+impl () as A {}
 
 // --- fail_too_many_interfaces_in_constraint.carbon
 library "[[@TEST_NAME]]";
@@ -45,50 +27,109 @@ library "[[@TEST_NAME]]";
 interface A1;
 interface A2;
 constraint B {
-  require impls A1;
-  require impls A2;
+  extend require impls A1;
+  extend require impls A2;
 }
 
-class C {}
-
-// TODO: This should fail since B does not name a single interface, it names
-// more than one.
-// CHECK:STDERR: fail_too_many_interfaces_in_constraint.carbon:[[@LINE+4]]:1: error: impl as 0 interfaces, expected 1 [ImplOfNotOneInterface]
-// CHECK:STDERR: impl C as B {}
-// CHECK:STDERR: ^~~~~~~~~~~~~
+// CHECK:STDERR: fail_too_many_interfaces_in_constraint.carbon:[[@LINE+4]]:1: error: impl as 2 interfaces, expected 1 [ImplOfNotOneInterface]
+// CHECK:STDERR: impl () as B {}
+// CHECK:STDERR: ^~~~~~~~~~~~~~
 // CHECK:STDERR:
-impl C as B {}
+impl () as B {}
 
-// --- fail_todo_one_declared_interface_in_constraint.carbon
+// --- one_extend_impls_interface_in_constraint.carbon
 library "[[@TEST_NAME]]";
 
-// TODO: This should work since B can be identified to have one interface.
+interface A;
+constraint B {
+  extend require impls A;
+}
+
+// Requries B identified.
+impl () as B;
+
+interface A {}
+
+// Requries B complete.
+impl () as B {}
+
+// --- fail_one_impls_interface_in_constraint.carbon
+library "[[@TEST_NAME]]";
 
 interface A;
 constraint B {
   require impls A;
 }
 
-class C {}
-// CHECK:STDERR: fail_todo_one_declared_interface_in_constraint.carbon:[[@LINE+4]]:1: error: impl as 0 interfaces, expected 1 [ImplOfNotOneInterface]
-// CHECK:STDERR: impl C as B {}
-// CHECK:STDERR: ^~~~~~~~~~~~~
+// CHECK:STDERR: fail_one_impls_interface_in_constraint.carbon:[[@LINE+4]]:1: error: impl as 0 interfaces, expected 1 [ImplOfNotOneInterface]
+// CHECK:STDERR: impl () as B {}
+// CHECK:STDERR: ^~~~~~~~~~~~~~
 // CHECK:STDERR:
-impl C as B {}
+impl () as B {}
 
-// --- fail_todo_one_defined_interface_in_constraint.carbon
+// --- nested_constraints.carbon
 library "[[@TEST_NAME]]";
 
-// TODO: This should work since B can be identified to have one interface.
+interface A {}
+constraint B {
+  extend require impls A;
+}
+constraint C {
+  extend require impls B;
+}
+
+impl () as C {}
+
+// --- fail_nested_constraints_not_extend_outer.carbon
+library "[[@TEST_NAME]]";
 
 interface A {}
+
+constraint B {
+  extend require impls A;
+}
+constraint C {
+  require impls B;
+}
+
+// CHECK:STDERR: fail_nested_constraints_not_extend_outer.carbon:[[@LINE+4]]:1: error: impl as 0 interfaces, expected 1 [ImplOfNotOneInterface]
+// CHECK:STDERR: impl () as C {}
+// CHECK:STDERR: ^~~~~~~~~~~~~~
+// CHECK:STDERR:
+impl () as C {}
+
+// --- fail_nested_constraints_not_extend_inner.carbon
+library "[[@TEST_NAME]]";
+
+interface A {}
+
 constraint B {
   require impls A;
 }
+constraint C {
+  extend require impls B;
+}
+
+// CHECK:STDERR: fail_nested_constraints_not_extend_inner.carbon:[[@LINE+4]]:1: error: impl as 0 interfaces, expected 1 [ImplOfNotOneInterface]
+// CHECK:STDERR: impl () as C {}
+// CHECK:STDERR: ^~~~~~~~~~~~~~
+// CHECK:STDERR:
+impl () as C {}
+
+// --- fail_nested_constraints_not_extend_both.carbon
+library "[[@TEST_NAME]]";
+
+interface A {}
+
+constraint B {
+  require impls A;
+}
+constraint C {
+  require impls B;
+}
 
-class C {}
-// CHECK:STDERR: fail_todo_one_defined_interface_in_constraint.carbon:[[@LINE+4]]:1: error: impl as 0 interfaces, expected 1 [ImplOfNotOneInterface]
-// CHECK:STDERR: impl C as B {}
-// CHECK:STDERR: ^~~~~~~~~~~~~
+// CHECK:STDERR: fail_nested_constraints_not_extend_both.carbon:[[@LINE+4]]:1: error: impl as 0 interfaces, expected 1 [ImplOfNotOneInterface]
+// CHECK:STDERR: impl () as C {}
+// CHECK:STDERR: ^~~~~~~~~~~~~~
 // CHECK:STDERR:
-impl C as B {}
+impl () as C {}

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

@@ -49,7 +49,7 @@ class C {}
 // CHECK:STDERR:
 impl C as A;
 
-// --- fail_todo_import_complete.carbon
+// --- import_complete.carbon
 library "[[@TEST_NAME]]";
 
 import library "b";
@@ -57,19 +57,9 @@ import library "b";
 class C {}
 
 // This requires that B is identified.
-// TODO: This should work.
-// CHECK:STDERR: fail_todo_import_complete.carbon:[[@LINE+4]]:1: error: impl as 0 interfaces, expected 1 [ImplOfNotOneInterface]
-// CHECK:STDERR: impl C as B;
-// CHECK:STDERR: ^~~~~~~~~~~~
-// CHECK:STDERR:
 impl C as B;
 
 // This requires that B is complete.
-// TODO: This should work.
-// CHECK:STDERR: fail_todo_import_complete.carbon:[[@LINE+4]]:1: error: impl as 0 interfaces, expected 1 [ImplOfNotOneInterface]
-// CHECK:STDERR: impl C as B {}
-// CHECK:STDERR: ^~~~~~~~~~~~~
-// CHECK:STDERR:
 impl C as B {}
 
 // CHECK:STDOUT: --- a.impl.carbon

+ 4 - 16
toolchain/check/testdata/named_constraint/require.carbon

@@ -29,14 +29,10 @@ fn F(T:! Z) {
   // CHECK:STDERR:   ^~~~
   // CHECK:STDERR:
   T.YY();
-  // CHECK:STDERR: fail_todo_extend.carbon:[[@LINE+4]]:3: error: cannot convert type `T` that implements `Z` into type implementing `Y` [ConversionFailureFacetToFacet]
-  // CHECK:STDERR:   T.(Y.YY)();
-  // CHECK:STDERR:   ^~~~~~~~
-  // CHECK:STDERR:
   T.(Y.YY)();
 }
 
-// --- fail_todo_implicit_self_impls.carbon
+// --- implicit_self_impls.carbon
 library "[[@TEST_NAME]]";
 
 interface Y {
@@ -50,14 +46,10 @@ constraint Z {
 //@dump-sem-ir-end
 
 fn F(T:! Z) {
-  // CHECK:STDERR: fail_todo_implicit_self_impls.carbon:[[@LINE+4]]:3: error: cannot convert type `T` that implements `Z` into type implementing `Y` [ConversionFailureFacetToFacet]
-  // CHECK:STDERR:   T.(Y.YY)();
-  // CHECK:STDERR:   ^~~~~~~~
-  // CHECK:STDERR:
   T.(Y.YY)();
 }
 
-// --- fail_todo_explicit_self_impls.carbon
+// --- explicit_self_impls.carbon
 library "[[@TEST_NAME]]";
 
 interface Y {
@@ -71,10 +63,6 @@ constraint Z {
 //@dump-sem-ir-end
 
 fn F(T:! Z) {
-  // CHECK:STDERR: fail_todo_explicit_self_impls.carbon:[[@LINE+4]]:3: error: cannot convert type `T` that implements `Z` into type implementing `Y` [ConversionFailureFacetToFacet]
-  // CHECK:STDERR:   T.(Y.YY)();
-  // CHECK:STDERR:   ^~~~~~~~
-  // CHECK:STDERR:
   T.(Y.YY)();
 }
 
@@ -342,7 +330,7 @@ constraint Z {
 // CHECK:STDOUT:   %Self.binding.as_type => constants.%Self.binding.as_type
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: --- fail_todo_implicit_self_impls.carbon
+// CHECK:STDOUT: --- implicit_self_impls.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %Y.type: type = facet_type <@Y> [concrete]
@@ -387,7 +375,7 @@ constraint Z {
 // CHECK:STDOUT:   %Self.binding.as_type => constants.%Self.binding.as_type
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: --- fail_todo_explicit_self_impls.carbon
+// CHECK:STDOUT: --- explicit_self_impls.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %Y.type: type = facet_type <@Y> [concrete]

+ 93 - 18
toolchain/check/type_completion.cpp

@@ -4,6 +4,7 @@
 
 #include "toolchain/check/type_completion.h"
 
+#include "common/concepts.h"
 #include "llvm/ADT/SmallVector.h"
 #include "toolchain/base/kind_switch.h"
 #include "toolchain/check/cpp/import.h"
@@ -69,6 +70,18 @@ static auto NoteIncompleteNamedConstraint(
   }
 }
 
+template <typename T>
+  requires SameAsOneOf<T, SemIR::Interface, SemIR::NamedConstraint>
+static auto ForEachRequireImpls(
+    Context& context, const T& entity,
+    llvm::function_ref<auto(const SemIR::RequireImpls&)->void> f) -> void {
+  for (auto require_impls_id :
+       context.require_impls_blocks().Get(entity.require_impls_block_id)) {
+    const auto& require = context.require_impls().Get(require_impls_id);
+    f(require);
+  }
+}
+
 namespace {
 // Worklist-based type completion mechanism.
 //
@@ -397,15 +410,15 @@ auto TypeCompleter::AddNestedIncompleteTypes(SemIR::Inst type_inst) -> bool {
       break;
     }
     case CARBON_KIND(SemIR::FacetType inst): {
-      // TODO: Get the complete facet type here.
       auto identified_id =
           RequireIdentifiedFacetType(*context_, inst, diagnoser_);
       if (!identified_id.has_value()) {
         return false;
       }
+
       const auto& identified =
           context_->identified_facet_types().Get(identified_id);
-      // Every mentioned interface needs to be complete.
+
       for (auto req_interface : identified.required_interfaces()) {
         auto interface_id = req_interface.interface_id;
         const auto& interface = context_->interfaces().Get(interface_id);
@@ -771,18 +784,11 @@ auto RequireConcreteType(Context& context, SemIR::TypeId type_id,
   return true;
 }
 
-auto RequireIdentifiedFacetType(Context& context,
-                                const SemIR::FacetType& facet_type,
-                                MakeDiagnosticBuilderFn diagnoser)
-    -> SemIR::IdentifiedFacetTypeId {
-  if (auto identified_id =
-          context.identified_facet_types().TryGetId(facet_type.facet_type_id);
-      identified_id.has_value()) {
-    return identified_id;
-  }
-  const auto& facet_type_info =
-      context.facet_types().Get(facet_type.facet_type_id);
-
+// Require all named constraints in the facet type are identified. For a named
+// constraint, this means the constraint definition is complete.
+static auto RequireIdentifiedNamedConstraints(
+    Context& context, const SemIR::FacetTypeInfo& facet_type_info,
+    MakeDiagnosticBuilderFn diagnoser) -> bool {
   auto named_constraint_ids = llvm::map_range(
       llvm::concat<const SemIR::SpecificNamedConstraint>(
           facet_type_info.extend_named_constraints,
@@ -797,15 +803,84 @@ auto RequireIdentifiedFacetType(Context& context,
         NoteIncompleteNamedConstraint(context, named_constraint_id, builder);
         builder.Emit();
       }
+      return false;
+    }
+  }
+  return true;
+}
+
+auto RequireIdentifiedFacetType(Context& context,
+                                const SemIR::FacetType& facet_type,
+                                MakeDiagnosticBuilderFn diagnoser)
+    -> SemIR::IdentifiedFacetTypeId {
+  if (auto identified_id =
+          context.identified_facet_types().TryGetId(facet_type.facet_type_id);
+      identified_id.has_value()) {
+    return identified_id;
+  }
+
+  // Work queue.
+  llvm::SmallVector<SemIR::FacetTypeId> extend_facet_types = {
+      facet_type.facet_type_id};
+  llvm::SmallVector<SemIR::FacetTypeId> impls_facet_types;
+
+  // Outputs for the IdentifiedFacetType.
+  llvm::SmallVector<SemIR::SpecificInterface> extends;
+  llvm::SmallVector<SemIR::SpecificInterface> self_impls;
+
+  while (true) {
+    auto next_facet_type_id = SemIR::FacetTypeId::None;
+    bool facet_type_extends = false;
+    if (!extend_facet_types.empty()) {
+      next_facet_type_id = extend_facet_types.pop_back_val();
+      facet_type_extends = true;
+    } else if (!impls_facet_types.empty()) {
+      next_facet_type_id = impls_facet_types.pop_back_val();
+      facet_type_extends = false;
+    } else {
+      break;
+    }
+
+    const auto& facet_type_info = context.facet_types().Get(next_facet_type_id);
+
+    if (!RequireIdentifiedNamedConstraints(context, facet_type_info,
+                                           diagnoser)) {
       return SemIR::IdentifiedFacetTypeId::None;
     }
+
+    if (facet_type_extends) {
+      llvm::append_range(extends, facet_type_info.extend_constraints);
+    } else {
+      llvm::append_range(self_impls, facet_type_info.extend_constraints);
+    }
+    llvm::append_range(self_impls, facet_type_info.self_impls_constraints);
+
+    for (auto extend : facet_type_info.extend_named_constraints) {
+      const auto& constraint =
+          context.named_constraints().Get(extend.named_constraint_id);
+      ForEachRequireImpls(
+          context, constraint, [&](const SemIR::RequireImpls& require) {
+            if (facet_type_extends && require.extend_self) {
+              extend_facet_types.push_back(require.facet_type_id);
+            } else {
+              impls_facet_types.push_back(require.facet_type_id);
+            }
+          });
+    }
+
+    for (auto impls : facet_type_info.self_impls_named_constraints) {
+      const auto& constraint =
+          context.named_constraints().Get(impls.named_constraint_id);
+      ForEachRequireImpls(context, constraint,
+                          [&](const SemIR::RequireImpls& require) {
+                            impls_facet_types.push_back(require.facet_type_id);
+                          });
+    }
   }
 
-  // TODO: expand named constraints
   // TODO: Process other kinds of requirements.
-  return context.identified_facet_types().Add(
-      facet_type.facet_type_id, {facet_type_info.extend_constraints,
-                                 facet_type_info.self_impls_constraints});
+  return context.identified_facet_types().Add(facet_type.facet_type_id,
+                                              {extends, self_impls});
 }
 
 auto AsCompleteType(Context& context, SemIR::TypeId type_id,

+ 3 - 2
toolchain/diagnostics/diagnostic_kind.def

@@ -360,13 +360,14 @@ CARBON_DIAGNOSTIC_KIND(MissingImplInMemberAccess)
 CARBON_DIAGNOSTIC_KIND(MissingImplInMemberAccessNote)
 CARBON_DIAGNOSTIC_KIND(ImplLookupCycle)
 CARBON_DIAGNOSTIC_KIND(ImplLookupCycleNote)
-CARBON_DIAGNOSTIC_KIND(ImplLookupInIncompleteFacetType)
+CARBON_DIAGNOSTIC_KIND(ImplLookupInUnidentifiedFacetType)
 
 // Require checking.
+CARBON_DIAGNOSTIC_KIND(RequireImplsExtendWithExplicitSelf)
 CARBON_DIAGNOSTIC_KIND(RequireImplsMissingFacetType)
 CARBON_DIAGNOSTIC_KIND(RequireImplsMissingSelf)
-CARBON_DIAGNOSTIC_KIND(RequireInWrongScope)
 CARBON_DIAGNOSTIC_KIND(RequireImplsUnidentifiedFacetType)
+CARBON_DIAGNOSTIC_KIND(RequireInWrongScope)
 
 // Let declaration checking.
 CARBON_DIAGNOSTIC_KIND(ExpectedInitializerAfterLet)

+ 4 - 2
toolchain/sem_ir/inst_fingerprinter.cpp

@@ -246,8 +246,10 @@ struct Worklist {
   auto Add(RequireImplsId require_id) -> void {
     CARBON_CHECK(require_id.has_value());
     const auto& require = sem_ir->require_impls().Get(require_id);
-    Add(require.self_id);
-    Add(require.facet_type_id);
+    Add(sem_ir->constant_values().Get(require.self_id));
+    Add(sem_ir->constant_values().Get(require.facet_type_inst_id));
+    contents.push_back(require.extend_self);
+    Add(require.parent_scope_id);
   }
 
   auto Add(AssociatedConstantId assoc_const_id) -> void {

+ 2 - 0
toolchain/sem_ir/require_impls.h

@@ -24,6 +24,8 @@ struct RequireImpls : Printable<RequireImpls> {
   TypeInstId facet_type_inst_id;
   // The `FacetTypeInfo` derived from the `facet_type_inst_id` instruction.
   FacetTypeId facet_type_id;
+  // If the facet type extends `Self`. When true, the `self_id` will be `Self`.
+  bool extend_self;
 
   // The location of the `require` declaration.
   InstId decl_id;