Procházet zdrojové kódy

Type-check `require` declarations (#6286)

They don't get stored anywhere yet, but this type checks the
declarations and diagnoses errors in their form, such as not placing a
facet type after `impls` or a type before it.
Dana Jansens před 6 měsíci
rodič
revize
43e09e8e81

+ 173 - 4
toolchain/check/handle_require.cpp

@@ -2,29 +2,198 @@
 // Exceptions. See /LICENSE for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 
+#include "toolchain/base/kind_switch.h"
 #include "toolchain/check/context.h"
+#include "toolchain/check/convert.h"
 #include "toolchain/check/handle.h"
+#include "toolchain/check/modifiers.h"
+#include "toolchain/check/name_lookup.h"
+#include "toolchain/check/subst.h"
+#include "toolchain/check/type_completion.h"
 #include "toolchain/parse/node_ids.h"
+#include "toolchain/sem_ir/named_constraint.h"
+#include "toolchain/sem_ir/type_iterator.h"
+#include "toolchain/sem_ir/typed_insts.h"
 
 namespace Carbon::Check {
 
 auto HandleParseNode(Context& context, Parse::RequireIntroducerId node_id)
     -> bool {
-  return context.TODO(node_id, "require");
+  // Create an instruction block to hold the instructions created for the type
+  // and constraint.
+  context.inst_block_stack().Push();
+
+  // Optional modifiers follow.
+  context.decl_introducer_state_stack().Push<Lex::TokenKind::Require>();
+
+  auto scope_id = context.scope_stack().PeekNameScopeId();
+  auto scope_inst_id = context.name_scopes().Get(scope_id).inst_id();
+  auto scope_inst = context.insts().Get(scope_inst_id);
+  if (!scope_inst.Is<SemIR::InterfaceDecl>() &&
+      !scope_inst.Is<SemIR::NamedConstraintDecl>()) {
+    CARBON_DIAGNOSTIC(
+        RequireInWrongScope, Error,
+        "`require` can only be used in an `interface` or `constraint`");
+    context.emitter().Emit(node_id, RequireInWrongScope);
+    scope_inst_id = SemIR::ErrorInst::InstId;
+  }
+
+  context.node_stack().Push(node_id, scope_inst_id);
+  return true;
 }
 
 auto HandleParseNode(Context& context, Parse::RequireDefaultSelfImplsId node_id)
     -> bool {
-  return context.TODO(node_id, "require");
+  auto scope_inst_id =
+      context.node_stack().Peek<Parse::NodeKind::RequireIntroducer>();
+  if (scope_inst_id == SemIR::ErrorInst::InstId) {
+    context.node_stack().Push(node_id, SemIR::ErrorInst::TypeInstId);
+    return true;
+  }
+
+  auto scope_id = context.scope_stack().PeekNameScopeId();
+  auto lookup_result =
+      LookupNameInExactScope(context, node_id, SemIR::NameId::SelfType,
+                             scope_id, context.name_scopes().Get(scope_id),
+                             /*is_being_declared=*/false);
+  CARBON_CHECK(lookup_result.is_found());
+
+  auto self_inst_id = lookup_result.target_inst_id();
+  auto self_type_id = context.insts().Get(self_inst_id).type_id();
+  CARBON_CHECK(context.types().Is<SemIR::FacetType>(self_type_id));
+
+  auto self_facet_as_type = AddTypeInst<SemIR::FacetAccessType>(
+      context, node_id,
+      {.type_id = SemIR::TypeType::TypeId,
+       .facet_value_inst_id = self_inst_id});
+  context.node_stack().Push(node_id, self_facet_as_type);
+  return true;
 }
 
 auto HandleParseNode(Context& context, Parse::RequireTypeImplsId node_id)
     -> bool {
-  return context.TODO(node_id, "require");
+  auto [self_node_id, self_inst_id] = context.node_stack().PopExprWithNodeId();
+  auto self_type = ExprAsType(context, self_node_id, self_inst_id);
+  context.node_stack().Push(node_id, self_type.inst_id);
+  return true;
+}
+
+static auto TypeStructureReferencesSelf(
+    Context& context, SemIR::TypeInstId inst_id,
+    const SemIR::IdentifiedFacetType& identified_facet_type) -> bool {
+  if (inst_id == SemIR::ErrorInst::TypeInstId) {
+    // Don't generate more diagnostics.
+    return true;
+  }
+
+  auto find_self = [&](SemIR::TypeIterator& type_iter) -> bool {
+    while (true) {
+      auto step = type_iter.Next();
+      if (step.Is<SemIR::TypeIterator::Step::Done>()) {
+        break;
+      }
+      CARBON_KIND_SWITCH(step.any) {
+        case CARBON_KIND(SemIR::TypeIterator::Step::Error _): {
+          // Don't generate more diagnostics.
+          return true;
+        }
+        case CARBON_KIND(SemIR::TypeIterator::Step::SymbolicBinding bind): {
+          if (context.entity_names().Get(bind.entity_name_id).name_id ==
+              SemIR::NameId::SelfType) {
+            return true;
+          }
+          break;
+        }
+        default:
+          break;
+      }
+    }
+    return false;
+  };
+
+  {
+    SemIR::TypeIterator type_iter(&context.sem_ir());
+    type_iter.Add(context.constant_values().GetConstantTypeInstId(inst_id));
+    if (find_self(type_iter)) {
+      return true;
+    }
+  }
+
+  if (identified_facet_type.required_interfaces().empty()) {
+    return false;
+  }
+
+  for (auto specific_interface : identified_facet_type.required_interfaces()) {
+    SemIR::TypeIterator type_iter(&context.sem_ir());
+    type_iter.Add(specific_interface);
+    if (!find_self(type_iter)) {
+      return false;
+    }
+  }
+
+  return true;
 }
 
 auto HandleParseNode(Context& context, Parse::RequireDeclId node_id) -> bool {
-  return context.TODO(node_id, "require");
+  auto [constraint_node_id, constraint_inst_id] =
+      context.node_stack().PopExprWithNodeId();
+  auto [self_node_id, self_inst_id] =
+      context.node_stack().PopWithNodeId<Parse::NodeCategory::RequireImpls>();
+
+  [[maybe_unused]] auto decl_block_id = context.inst_block_stack().Pop();
+
+  // Process modifiers.
+  auto introducer =
+      context.decl_introducer_state_stack().Pop<Lex::TokenKind::Require>();
+  LimitModifiersOnDecl(context, introducer, KeywordModifierSet::Extend);
+
+  auto scope_inst_id =
+      context.node_stack().Pop<Parse::NodeKind::RequireIntroducer>();
+
+  auto constraint_constant_value_inst_id =
+      context.constant_values().GetConstantInstId(constraint_inst_id);
+  auto constraint_facet_type = context.insts().TryGetAs<SemIR::FacetType>(
+      constraint_constant_value_inst_id);
+  if (!constraint_facet_type) {
+    if (constraint_constant_value_inst_id != SemIR::ErrorInst::InstId) {
+      CARBON_DIAGNOSTIC(
+          RequireImplsMissingFacetType, Error,
+          "`require` declaration constrained by a non-facet type; "
+          "expected an `interface` or `constraint` name after `impls`");
+      context.emitter().Emit(constraint_node_id, RequireImplsMissingFacetType);
+    }
+    // Can't continue without a constraint to use.
+    return true;
+  }
+
+  auto identified_facet_type_id =
+      RequireIdentifiedFacetType(context, *constraint_facet_type);
+  const auto& identified =
+      context.identified_facet_types().Get(identified_facet_type_id);
+
+  if (!TypeStructureReferencesSelf(context, self_inst_id, identified)) {
+    CARBON_DIAGNOSTIC(RequireImplsMissingSelf, Error,
+                      "no `Self` reference found in `require` declaration; "
+                      "`Self` must appear in the self-type or as a generic "
+                      "parameter for each `interface` or `constraint`");
+    context.emitter().Emit(node_id, RequireImplsMissingSelf);
+    return true;
+  }
+
+  if (scope_inst_id == SemIR::ErrorInst::InstId) {
+    // `require` is in the wrong scope.
+    return true;
+  }
+
+  if (identified.required_interfaces().empty()) {
+    // A `require T impls type` adds no actual constraints.
+    return true;
+  }
+
+  // TODO: Add the `require` constraint to the InterfaceDecl or ConstraintDecl
+  // from `scope_inst_id`.
+
+  return true;
 }
 
 }  // namespace Carbon::Check

+ 2 - 0
toolchain/check/name_lookup.cpp

@@ -161,6 +161,8 @@ auto LookupUnqualifiedName(Context& context, SemIR::LocId loc_id,
     DiagnoseNameNotFound(context, loc_id, name_id);
   }
 
+  // TODO: Should this return MakeNotFound if `required` is false, so that
+  // `is_found()` would be false?
   return {.specific_id = SemIR::SpecificId::None,
           .scope_result = SemIR::ScopeLookupResult::MakeError()};
 }

+ 3 - 3
toolchain/check/node_stack.h

@@ -397,6 +397,8 @@ class NodeStack {
         Id::KindFor<SemIR::NameId>());
     set_id_if_category_is(Parse::NodeCategory::ImplAs,
                           Id::KindFor<SemIR::TypeInstId>());
+    set_id_if_category_is(Parse::NodeCategory::RequireImpls,
+                          Id::KindFor<SemIR::TypeInstId>());
     set_id_if_category_is(Parse::NodeCategory::Decl |
                               Parse::NodeCategory::Statement |
                               Parse::NodeCategory::Modifier,
@@ -412,6 +414,7 @@ class NodeStack {
       case Parse::NodeKind::CallExprStart:
       case Parse::NodeKind::FieldNameAndType:
       case Parse::NodeKind::IfExprThen:
+      case Parse::NodeKind::RequireIntroducer:
       case Parse::NodeKind::ReturnType:
       case Parse::NodeKind::ShortCircuitOperandAnd:
       case Parse::NodeKind::ShortCircuitOperandOr:
@@ -524,9 +527,6 @@ class NodeStack {
       case Parse::NodeKind::ParenExprStart:
       case Parse::NodeKind::PatternListComma:
       case Parse::NodeKind::Placeholder:
-      case Parse::NodeKind::RequireIntroducer:
-      case Parse::NodeKind::RequireDefaultSelfImpls:
-      case Parse::NodeKind::RequireTypeImpls:
       case Parse::NodeKind::RequirementAnd:
       case Parse::NodeKind::RequirementEqual:
       case Parse::NodeKind::RequirementEqualEqual:

+ 63 - 5
toolchain/check/testdata/facet/require_invalid.carbon

@@ -10,25 +10,24 @@
 // TIP: To dump output, run:
 // TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/facet/require_invalid.carbon
 
-// --- fail_todo_require_outside_scope.carbon
+// --- fail_require_outside_scope.carbon
 library "[[@TEST_NAME]]";
 
 interface Y {}
 
-// CHECK:STDERR: fail_todo_require_outside_scope.carbon:[[@LINE+4]]:1: error: semantics TODO: `require` [SemanticsTodo]
+// CHECK:STDERR: fail_require_outside_scope.carbon:[[@LINE+4]]:1: error: `require` can only be used in an `interface` or `constraint` [RequireInWrongScope]
 // CHECK:STDERR: require impls Y;
 // CHECK:STDERR: ^~~~~~~
 // CHECK:STDERR:
 require impls Y;
 
-// --- fail_todo_require_in_class.carbon
+// --- fail_require_in_class.carbon
 library "[[@TEST_NAME]]";
 
 interface Y {}
 
 class C {
-  // TODO: require is not allowed outside of `interface` or `constraint`.
-  // CHECK:STDERR: fail_todo_require_in_class.carbon:[[@LINE+4]]:3: error: semantics TODO: `require` [SemanticsTodo]
+  // CHECK:STDERR: fail_require_in_class.carbon:[[@LINE+4]]:3: error: `require` can only be used in an `interface` or `constraint` [RequireInWrongScope]
   // CHECK:STDERR:   require impls Y;
   // CHECK:STDERR:   ^~~~~~~
   // CHECK:STDERR:
@@ -42,6 +41,7 @@ interface Y {}
 
 fn F() {
   // require is not allowed outside of `interface` or `constraint`.
+  //
   // CHECK:STDERR: fail_require_in_fn.carbon:[[@LINE+8]]:3: error: expected expression [ExpectedExpr]
   // CHECK:STDERR:   require impls Y;
   // CHECK:STDERR:   ^~~~~~~
@@ -52,3 +52,61 @@ fn F() {
   // CHECK:STDERR:
   require impls Y;
 }
+
+// --- fail_require_in_nested_class.carbon
+library "[[@TEST_NAME]]";
+
+interface Y {}
+
+interface Z {
+  // TODO: Add `default` modifier.
+  fn F() {
+    class C {
+      // CHECK:STDERR: fail_require_in_nested_class.carbon:[[@LINE+4]]:7: error: `require` can only be used in an `interface` or `constraint` [RequireInWrongScope]
+      // CHECK:STDERR:       require impls Y;
+      // CHECK:STDERR:       ^~~~~~~
+      // CHECK:STDERR:
+      require impls Y;
+    }
+  }
+}
+
+// --- fail_require_in_nested_fn.carbon
+library "[[@TEST_NAME]]";
+
+interface Y {}
+
+interface Z {
+  // TODO: Add `default` modifier.
+  fn F() {
+    // require is not allowed outside of `interface` or `constraint`.
+    //
+    // CHECK:STDERR: fail_require_in_nested_fn.carbon:[[@LINE+8]]:5: error: expected expression [ExpectedExpr]
+    // CHECK:STDERR:     require impls Y;
+    // CHECK:STDERR:     ^~~~~~~
+    // CHECK:STDERR:
+    // CHECK:STDERR: fail_require_in_nested_fn.carbon:[[@LINE+4]]:5: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo]
+    // CHECK:STDERR:     require impls Y;
+    // CHECK:STDERR:     ^~~~~~~
+    // CHECK:STDERR:
+    require impls Y;
+  }
+}
+
+// --- fail_errors_in_require_still_found.carbon
+library "[[@TEST_NAME]]";
+
+class C {
+  // The `require` is diagnosed as being in the wrong place. But we can still
+  // diagnose issues inside the decl too.
+  //
+  // CHECK:STDERR: fail_errors_in_require_still_found.carbon:[[@LINE+8]]:3: error: `require` can only be used in an `interface` or `constraint` [RequireInWrongScope]
+  // CHECK:STDERR:   require impls Y;
+  // CHECK:STDERR:   ^~~~~~~
+  // CHECK:STDERR:
+  // CHECK:STDERR: fail_errors_in_require_still_found.carbon:[[@LINE+4]]:17: error: name `Y` not found [NameNotFound]
+  // CHECK:STDERR:   require impls Y;
+  // CHECK:STDERR:                 ^
+  // CHECK:STDERR:
+  require impls Y;
+}

+ 325 - 89
toolchain/check/testdata/interface/require.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/none.carbon
+// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon
 //
 // AUTOUPDATE
 // TIP: To test this file alone, run:
@@ -10,7 +10,7 @@
 // TIP: To dump output, run:
 // TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interface/require.carbon
 
-// --- fail_todo_implicit_self_impls.carbon
+// --- fail_todo_extend.carbon
 library "[[@TEST_NAME]]";
 
 interface Y {
@@ -19,15 +19,41 @@ interface Y {
 
 //@dump-sem-ir-begin
 interface Z {
-  // CHECK:STDERR: fail_todo_implicit_self_impls.carbon:[[@LINE+4]]:3: error: semantics TODO: `require` [SemanticsTodo]
-  // CHECK:STDERR:   require impls Y;
-  // CHECK:STDERR:   ^~~~~~~
+  extend require impls Y;
+}
+//@dump-sem-ir-end
+
+fn F(T:! Z) {
+  // CHECK:STDERR: fail_todo_extend.carbon:[[@LINE+4]]:3: error: member name `YY` not found in `Z` [MemberNameNotFoundInInstScope]
+  // CHECK:STDERR:   T.YY();
+  // 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
+library "[[@TEST_NAME]]";
+
+interface Y {
+  fn YY();
+}
+
+//@dump-sem-ir-begin
+interface Z {
   require impls Y;
 }
 //@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)();
 }
 
@@ -40,19 +66,19 @@ interface Y {
 
 //@dump-sem-ir-begin
 interface Z {
-  // CHECK:STDERR: fail_todo_explicit_self_impls.carbon:[[@LINE+4]]:3: error: semantics TODO: `require` [SemanticsTodo]
-  // CHECK:STDERR:   require Self impls Y;
-  // CHECK:STDERR:   ^~~~~~~
-  // CHECK:STDERR:
   require Self impls Y;
 }
 //@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)();
 }
 
-// --- fail_todo_implicit_self_no_extend_name_lookup_fails.carbon
+// --- fail_implicit_self_no_extend_name_lookup_fails.carbon
 library "[[@TEST_NAME]]";
 
 interface Y {
@@ -60,19 +86,20 @@ interface Y {
 }
 
 interface Z {
-  // CHECK:STDERR: fail_todo_implicit_self_no_extend_name_lookup_fails.carbon:[[@LINE+4]]:3: error: semantics TODO: `require` [SemanticsTodo]
-  // CHECK:STDERR:   require impls Y;
-  // CHECK:STDERR:   ^~~~~~~
-  // CHECK:STDERR:
   require impls Y;
 }
 
 fn F(T:! Z) {
-  // TODO: This should fail name lookup since Z does not extend Y.
+  // This should fail name lookup since Z does not extend Y.
+  //
+  // CHECK:STDERR: fail_implicit_self_no_extend_name_lookup_fails.carbon:[[@LINE+4]]:3: error: member name `YY` not found in `Z` [MemberNameNotFoundInInstScope]
+  // CHECK:STDERR:   T.YY();
+  // CHECK:STDERR:   ^~~~
+  // CHECK:STDERR:
   T.YY();
 }
 
-// --- fail_todo_explicit_self_no_extend_name_lookup_fails.carbon
+// --- fail_explicit_self_no_extend_name_lookup_fails.carbon
 library "[[@TEST_NAME]]";
 
 interface Y {
@@ -80,19 +107,20 @@ interface Y {
 }
 
 interface Z {
-  // CHECK:STDERR: fail_todo_explicit_self_no_extend_name_lookup_fails.carbon:[[@LINE+4]]:3: error: semantics TODO: `require` [SemanticsTodo]
-  // CHECK:STDERR:   require Self impls Y;
-  // CHECK:STDERR:   ^~~~~~~
-  // CHECK:STDERR:
   require Self impls Y;
 }
 
 fn F(T:! Z) {
-  // TODO: This should fail name lookup since Z does not extend Y.
+  // This should fail name lookup since Z does not extend Y.
+  //
+  // CHECK:STDERR: fail_explicit_self_no_extend_name_lookup_fails.carbon:[[@LINE+4]]:3: error: member name `YY` not found in `Z` [MemberNameNotFoundInInstScope]
+  // CHECK:STDERR:   T.YY();
+  // CHECK:STDERR:   ^~~~
+  // CHECK:STDERR:
   T.YY();
 }
 
-// --- fail_todo_explicit_self_specific_impls.carbon
+// --- explicit_self_specific_impls.carbon
 library "[[@TEST_NAME]]";
 
 interface Y {}
@@ -101,175 +129,383 @@ class C(T:! type);
 
 //@dump-sem-ir-begin
 interface Z {
-  // CHECK:STDERR: fail_todo_explicit_self_specific_impls.carbon:[[@LINE+4]]:3: error: semantics TODO: `require` [SemanticsTodo]
-  // CHECK:STDERR:   require C(Self) impls Y;
-  // CHECK:STDERR:   ^~~~~~~
-  // CHECK:STDERR:
   require C(Self) impls Y;
 }
 //@dump-sem-ir-end
 
-// --- fail_todo_impls_where.carbon
+// --- require_impls_where.carbon
 library "[[@TEST_NAME]]";
 
 interface Y { let Y1:! type; }
 
 //@dump-sem-ir-begin
 interface Z {
-  // CHECK:STDERR: fail_todo_impls_where.carbon:[[@LINE+4]]:3: error: semantics TODO: `require` [SemanticsTodo]
-  // CHECK:STDERR:   require impls Y where .Y1 = ();
-  // CHECK:STDERR:   ^~~~~~~
-  // CHECK:STDERR:
   require impls Y where .Y1 = ();
 }
 //@dump-sem-ir-end
 
-// --- fail_todo_other_impls_with_self.carbon
+// --- require_impls_self_specific.carbon
 library "[[@TEST_NAME]]";
 
-interface Y {}
-
 //@dump-sem-ir-begin
 interface Z(T:! type) {
-  // CHECK:STDERR: fail_todo_other_impls_with_self.carbon:[[@LINE+4]]:3: error: semantics TODO: `require` [SemanticsTodo]
-  // CHECK:STDERR:   require T impls Z(Self);
-  // CHECK:STDERR:   ^~~~~~~
-  // CHECK:STDERR:
   require T impls Z(Self);
 }
 //@dump-sem-ir-end
 
-// --- fail_todo_other_impls_without_self.carbon
+// --- fail_require_impls_without_self.carbon
+library "[[@TEST_NAME]]";
+
+interface Y {
+  let Y1:! type;
+}
+
+interface Z(T:! type) {
+  // Either the type `T` or the facet type `Y` must mention `Self` in a way that
+  // it would appear in the type structure used for impl lookup (so inside a
+  // `where` does not count). But they don't.
+  //
+  // CHECK:STDERR: fail_require_impls_without_self.carbon:[[@LINE+4]]:3: error: no `Self` reference found in `require` declaration; `Self` must appear in the self-type or as a generic parameter for each `interface` or `constraint` [RequireImplsMissingSelf]
+  // CHECK:STDERR:   require T impls Y where .Y1 = Self;
+  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  // CHECK:STDERR:
+  require T impls Y where .Y1 = Self;
+}
+
+// --- fail_require_impls_without_self_in_one_interface.carbon
+library "[[@TEST_NAME]]";
+
+interface Y(T:! type) {}
+
+interface Z(T:! type) {
+  // Self is in one interface but not the other.
+  //
+  // CHECK:STDERR: fail_require_impls_without_self_in_one_interface.carbon:[[@LINE+4]]:3: error: no `Self` reference found in `require` declaration; `Self` must appear in the self-type or as a generic parameter for each `interface` or `constraint` [RequireImplsMissingSelf]
+  // CHECK:STDERR:   require T impls Y(Self) & Y({});
+  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  // CHECK:STDERR:
+  require T impls Y(Self) & Y({});
+}
+
+// --- fail_self_impls_self.carbon
+library "[[@TEST_NAME]]";
+
+interface Z(T:! type) {
+  // CHECK:STDERR: fail_self_impls_self.carbon:[[@LINE+4]]:17: error: `require` declaration constrained by a non-facet type; expected an `interface` or `constraint` name after `impls` [RequireImplsMissingFacetType]
+  // CHECK:STDERR:   require impls Self;
+  // CHECK:STDERR:                 ^~~~
+  // CHECK:STDERR:
+  require impls Self;
+}
+
+// --- fail_impls_type.carbon
+library "[[@TEST_NAME]]";
+
+class C(T:! type) {}
+
+interface Z(T:! type) {
+  // CHECK:STDERR: fail_impls_type.carbon:[[@LINE+4]]:19: error: `require` declaration constrained by a non-facet type; expected an `interface` or `constraint` name after `impls` [RequireImplsMissingFacetType]
+  // CHECK:STDERR:   require T impls C(Self);
+  // CHECK:STDERR:                   ^~~~~~~
+  // CHECK:STDERR:
+  require T impls C(Self);
+}
+
+// --- fail_non_type_impls.carbon
 library "[[@TEST_NAME]]";
 
 interface Y {}
 
-//@dump-sem-ir-begin
 interface Z(T:! type) {
-  // TODO: Either the type `T` or the facet type `Y` must mention `Self`, but
-  // they don't.
-  // CHECK:STDERR: fail_todo_other_impls_without_self.carbon:[[@LINE+4]]:3: error: semantics TODO: `require` [SemanticsTodo]
-  // CHECK:STDERR:   require T impls Y;
-  // CHECK:STDERR:   ^~~~~~~
+  // CHECK:STDERR: fail_non_type_impls.carbon:[[@LINE+7]]:11: error: cannot implicitly convert non-type value of type `Core.IntLiteral` to `type` [ConversionFailureNonTypeToFacet]
+  // CHECK:STDERR:   require 1 impls Y;
+  // CHECK:STDERR:           ^
+  // CHECK:STDERR: fail_non_type_impls.carbon:[[@LINE+4]]:11: note: type `Core.IntLiteral` does not implement interface `Core.ImplicitAs(type)` [MissingImplInMemberAccessNote]
+  // CHECK:STDERR:   require 1 impls Y;
+  // CHECK:STDERR:           ^
   // CHECK:STDERR:
-  require T impls Y;
+  require 1 impls Y;
 }
-//@dump-sem-ir-end
 
-// --- fail_todo_extend.carbon
+// --- fail_impls_non_type.carbon
+library "[[@TEST_NAME]]";
+
+interface Y {}
+
+interface Z(T:! type) {
+  // CHECK:STDERR: fail_impls_non_type.carbon:[[@LINE+4]]:17: error: `require` declaration constrained by a non-facet type; expected an `interface` or `constraint` name after `impls` [RequireImplsMissingFacetType]
+  // CHECK:STDERR:   require impls 1;
+  // CHECK:STDERR:                 ^
+  // CHECK:STDERR:
+  require impls 1;
+}
+
+// --- require_self_in_requirement.carbon
 library "[[@TEST_NAME]]";
 
 interface Y {
-  fn YY();
+  let Y1:! type;
 }
 
 //@dump-sem-ir-begin
 interface Z {
-  // CHECK:STDERR: fail_todo_extend.carbon:[[@LINE+4]]:10: error: semantics TODO: `require` [SemanticsTodo]
-  // CHECK:STDERR:   extend require impls Y;
-  // CHECK:STDERR:          ^~~~~~~
-  // CHECK:STDERR:
-  extend require impls Y;
+  // Self can appear in a requirement.
+  require impls Y where .Y1 = Self;
 }
 //@dump-sem-ir-end
 
-fn F(T:! Z) {
-  T.YY();
-  T.(Y.YY)();
+// --- require_same.carbon
+library "[[@TEST_NAME]]";
+
+interface Y {
+  require impls Y;
 }
 
-// CHECK:STDOUT: --- fail_todo_implicit_self_impls.carbon
+//@dump-sem-ir-begin
+interface Z(T:! type) {
+  // This is okay because an interface Z can be identified before it's complete,
+  // unlike a named constraint.
+  require impls Z(T);
+}
+//@dump-sem-ir-end
+
+// CHECK:STDOUT: --- fail_todo_extend.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %Z.type: type = facet_type <@Z> [concrete]
+// CHECK:STDOUT:   %Self.3b3: %Z.type = symbolic_binding Self, 0 [symbolic]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   %Z.decl: type = interface_decl @Z [concrete = constants.%Z.type] {} {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: interface @Z {
+// CHECK:STDOUT:   %Self: %Z.type = symbolic_binding Self, 0 [symbolic = constants.%Self.3b3]
+// CHECK:STDOUT:
 // CHECK:STDOUT: !members:
-// CHECK:STDOUT:   .Self = <unexpected>.inst{{[0-9A-F]+}}.loc8_13
-// CHECK:STDOUT:   witness = invalid
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .Y = <poisoned>
+// CHECK:STDOUT:   .YY = <poisoned>
+// CHECK:STDOUT:   witness = ()
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: --- fail_todo_explicit_self_impls.carbon
+// CHECK:STDOUT: --- fail_todo_implicit_self_impls.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %Z.type: type = facet_type <@Z> [concrete]
+// CHECK:STDOUT:   %Self.3b3: %Z.type = symbolic_binding Self, 0 [symbolic]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   %Z.decl: type = interface_decl @Z [concrete = constants.%Z.type] {} {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: interface @Z {
+// CHECK:STDOUT:   %Self: %Z.type = symbolic_binding Self, 0 [symbolic = constants.%Self.3b3]
+// CHECK:STDOUT:
 // CHECK:STDOUT: !members:
-// CHECK:STDOUT:   .Self = <unexpected>.inst{{[0-9A-F]+}}.loc8_13
-// CHECK:STDOUT:   witness = invalid
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .Y = <poisoned>
+// CHECK:STDOUT:   witness = ()
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: --- fail_todo_explicit_self_specific_impls.carbon
+// CHECK:STDOUT: --- fail_todo_explicit_self_impls.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %Z.type: type = facet_type <@Z> [concrete]
+// CHECK:STDOUT:   %Self.3b3: %Z.type = symbolic_binding Self, 0 [symbolic]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   %Z.decl: type = interface_decl @Z [concrete = constants.%Z.type] {} {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: interface @Z {
+// CHECK:STDOUT:   %Self: %Z.type = symbolic_binding Self, 0 [symbolic = constants.%Self.3b3]
+// CHECK:STDOUT:
 // CHECK:STDOUT: !members:
-// CHECK:STDOUT:   .Self = <unexpected>.inst{{[0-9A-F]+}}.loc8_13
-// CHECK:STDOUT:   witness = invalid
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .Y = <poisoned>
+// CHECK:STDOUT:   witness = ()
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: --- fail_todo_impls_where.carbon
+// CHECK:STDOUT: --- explicit_self_specific_impls.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %Z.type: type = facet_type <@Z> [concrete]
+// CHECK:STDOUT:   %Self.3b3: %Z.type = symbolic_binding Self, 0 [symbolic]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   %Z.decl: type = interface_decl @Z [concrete = constants.%Z.type] {} {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: interface @Z {
+// CHECK:STDOUT:   %Self: %Z.type = symbolic_binding Self, 0 [symbolic = constants.%Self.3b3]
+// CHECK:STDOUT:
 // CHECK:STDOUT: !members:
-// CHECK:STDOUT:   .Self = <unexpected>.inst{{[0-9A-F]+}}.loc6_13
-// CHECK:STDOUT:   witness = invalid
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .C = <poisoned>
+// CHECK:STDOUT:   .Y = <poisoned>
+// CHECK:STDOUT:   witness = ()
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: --- fail_todo_other_impls_with_self.carbon
+// CHECK:STDOUT: --- require_impls_where.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
-// CHECK:STDOUT:   %T: type = symbolic_binding T, 0 [symbolic]
+// CHECK:STDOUT:   %Z.type: type = facet_type <@Z> [concrete]
+// CHECK:STDOUT:   %Self.3b3: %Z.type = symbolic_binding Self, 0 [symbolic]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: generic interface @Z(<unexpected>.inst{{[0-9A-F]+}}.loc6_13: type) {
-// CHECK:STDOUT:   %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)]
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT:   interface {
-// CHECK:STDOUT:   !members:
-// CHECK:STDOUT:     .Self = <unexpected>.inst{{[0-9A-F]+}}.loc6_23
-// CHECK:STDOUT:     witness = invalid
-// CHECK:STDOUT:   }
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   %Z.decl: type = interface_decl @Z [concrete = constants.%Z.type] {} {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: specific @Z(constants.%T) {
-// CHECK:STDOUT:   %T => constants.%T
+// CHECK:STDOUT: interface @Z {
+// CHECK:STDOUT:   %Self: %Z.type = symbolic_binding Self, 0 [symbolic = constants.%Self.3b3]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .Y = <poisoned>
+// CHECK:STDOUT:   witness = ()
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: --- fail_todo_other_impls_without_self.carbon
+// CHECK:STDOUT: --- require_impls_self_specific.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %T: type = symbolic_binding T, 0 [symbolic]
+// CHECK:STDOUT:   %pattern_type: type = pattern_type type [concrete]
+// CHECK:STDOUT:   %Z.type.9fb: type = generic_interface_type @Z [concrete]
+// CHECK:STDOUT:   %Z.generic: %Z.type.9fb = struct_value () [concrete]
+// CHECK:STDOUT:   %Z.type.4d7: type = facet_type <@Z, @Z(%T)> [symbolic]
+// CHECK:STDOUT:   %Self: %Z.type.4d7 = symbolic_binding Self, 1 [symbolic]
+// CHECK:STDOUT:   %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self [symbolic]
+// CHECK:STDOUT:   %Z.type.49d: type = facet_type <@Z, @Z(%Self.binding.as_type)> [symbolic]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: generic interface @Z(<unexpected>.inst{{[0-9A-F]+}}.loc6_13: type) {
-// CHECK:STDOUT:   %T: type = symbolic_binding T, 0 [symbolic = %T (constants.%T)]
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   %Z.decl: %Z.type.9fb = interface_decl @Z [concrete = constants.%Z.generic] {
+// CHECK:STDOUT:     %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete]
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     <elided>
+// CHECK:STDOUT:     %T.loc4_13.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_13.1 (constants.%T)]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic interface @Z(%T.loc4_13.2: type) {
+// CHECK:STDOUT:   %T.loc4_13.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_13.1 (constants.%T)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %Z.type.loc4: type = facet_type <@Z, @Z(%T.loc4_13.1)> [symbolic = %Z.type.loc4 (constants.%Z.type.4d7)]
+// CHECK:STDOUT:   %Self.loc4_23.2: @Z.%Z.type.loc4 (%Z.type.4d7) = symbolic_binding Self, 1 [symbolic = %Self.loc4_23.2 (constants.%Self)]
+// CHECK:STDOUT:   %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self.loc4_23.2 [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)]
+// CHECK:STDOUT:   %Z.type.loc5: type = facet_type <@Z, @Z(%Self.binding.as_type)> [symbolic = %Z.type.loc5 (constants.%Z.type.49d)]
 // CHECK:STDOUT:
 // CHECK:STDOUT:   interface {
+// CHECK:STDOUT:     %Self.loc4_23.1: @Z.%Z.type.loc4 (%Z.type.4d7) = symbolic_binding Self, 1 [symbolic = %Self.loc4_23.2 (constants.%Self)]
+// CHECK:STDOUT:
 // CHECK:STDOUT:   !members:
-// CHECK:STDOUT:     .Self = <unexpected>.inst{{[0-9A-F]+}}.loc6_23
-// CHECK:STDOUT:     witness = invalid
+// CHECK:STDOUT:     .Self = %Self.loc4_23.1
+// CHECK:STDOUT:     .T = <poisoned>
+// CHECK:STDOUT:     .Z = <poisoned>
+// CHECK:STDOUT:     witness = ()
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @Z(constants.%T) {
-// CHECK:STDOUT:   %T => constants.%T
+// CHECK:STDOUT:   %T.loc4_13.1 => constants.%T
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: --- fail_todo_extend.carbon
+// CHECK:STDOUT: specific @Z(constants.%Self.binding.as_type) {
+// CHECK:STDOUT:   %T.loc4_13.1 => constants.%Self.binding.as_type
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- require_self_in_requirement.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %Z.type: type = facet_type <@Z> [concrete]
+// CHECK:STDOUT:   %Self.3b3: %Z.type = symbolic_binding Self, 0 [symbolic]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   %Z.decl: type = interface_decl @Z [concrete = constants.%Z.type] {} {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: interface @Z {
+// CHECK:STDOUT:   %Self: %Z.type = symbolic_binding Self, 0 [symbolic = constants.%Self.3b3]
+// CHECK:STDOUT:
 // CHECK:STDOUT: !members:
-// CHECK:STDOUT:   .Self = <unexpected>.inst{{[0-9A-F]+}}.loc8_13
-// CHECK:STDOUT:   witness = invalid
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .Y = <poisoned>
+// CHECK:STDOUT:   witness = ()
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- require_same.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %T: type = symbolic_binding T, 0 [symbolic]
+// CHECK:STDOUT:   %pattern_type: type = pattern_type type [concrete]
+// CHECK:STDOUT:   %Z.type.9fb: type = generic_interface_type @Z [concrete]
+// CHECK:STDOUT:   %Z.generic: %Z.type.9fb = struct_value () [concrete]
+// CHECK:STDOUT:   %Z.type.4d7: type = facet_type <@Z, @Z(%T)> [symbolic]
+// CHECK:STDOUT:   %Self.c41: %Z.type.4d7 = symbolic_binding Self, 1 [symbolic]
+// CHECK:STDOUT:   %Self.binding.as_type.6a8: type = symbolic_binding_type Self, 1, %Self.c41 [symbolic]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   %Z.decl: %Z.type.9fb = interface_decl @Z [concrete = constants.%Z.generic] {
+// CHECK:STDOUT:     %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete]
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     <elided>
+// CHECK:STDOUT:     %T.loc8_13.2: type = symbolic_binding T, 0 [symbolic = %T.loc8_13.1 (constants.%T)]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic interface @Z(%T.loc8_13.2: type) {
+// CHECK:STDOUT:   %T.loc8_13.1: type = symbolic_binding T, 0 [symbolic = %T.loc8_13.1 (constants.%T)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %Z.type: type = facet_type <@Z, @Z(%T.loc8_13.1)> [symbolic = %Z.type (constants.%Z.type.4d7)]
+// CHECK:STDOUT:   %Self.loc8_23.2: @Z.%Z.type (%Z.type.4d7) = symbolic_binding Self, 1 [symbolic = %Self.loc8_23.2 (constants.%Self.c41)]
+// CHECK:STDOUT:   %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self.loc8_23.2 [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type.6a8)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   interface {
+// CHECK:STDOUT:     %Self.loc8_23.1: @Z.%Z.type (%Z.type.4d7) = symbolic_binding Self, 1 [symbolic = %Self.loc8_23.2 (constants.%Self.c41)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     .Self = %Self.loc8_23.1
+// CHECK:STDOUT:     .Z = <poisoned>
+// CHECK:STDOUT:     .T = <poisoned>
+// CHECK:STDOUT:     witness = ()
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @Z(constants.%T) {
+// CHECK:STDOUT:   %T.loc8_13.1 => constants.%T
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 158 - 0
toolchain/check/testdata/interface/require_invalid_modifiers.carbon

@@ -0,0 +1,158 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon
+//
+// AUTOUPDATE
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interface/require_invalid_modifiers.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interface/require_invalid_modifiers.carbon
+
+// --- fail_abstract_require.carbon
+library "[[@TEST_NAME]]";
+
+interface Y {}
+
+interface Z {
+  // CHECK:STDERR: fail_abstract_require.carbon:[[@LINE+4]]:3: error: `abstract` not allowed on `require` declaration [ModifierNotAllowedOnDeclaration]
+  // CHECK:STDERR:   abstract require impls Y;
+  // CHECK:STDERR:   ^~~~~~~~
+  // CHECK:STDERR:
+  abstract require impls Y;
+}
+
+// --- fail_base_require.carbon
+library "[[@TEST_NAME]]";
+
+interface Y {}
+
+interface Z {
+  // CHECK:STDERR: fail_base_require.carbon:[[@LINE+4]]:3: error: `base` not allowed on `require` declaration [ModifierNotAllowedOnDeclaration]
+  // CHECK:STDERR:   base require impls Y;
+  // CHECK:STDERR:   ^~~~
+  // CHECK:STDERR:
+  base require impls Y;
+}
+
+// --- fail_default_require.carbon
+library "[[@TEST_NAME]]";
+
+interface Y {}
+
+interface Z {
+  // CHECK:STDERR: fail_default_require.carbon:[[@LINE+4]]:3: error: `default` not allowed on `require` declaration [ModifierNotAllowedOnDeclaration]
+  // CHECK:STDERR:   default require impls Y;
+  // CHECK:STDERR:   ^~~~~~~
+  // CHECK:STDERR:
+  default require impls Y;
+}
+
+// --- fail_extern_require.carbon
+library "[[@TEST_NAME]]";
+
+interface Y {}
+
+interface Z {
+  // CHECK:STDERR: fail_extern_require.carbon:[[@LINE+4]]:3: error: `extern` not allowed on `require` declaration [ModifierNotAllowedOnDeclaration]
+  // CHECK:STDERR:   extern require impls Y;
+  // CHECK:STDERR:   ^~~~~~
+  // CHECK:STDERR:
+  extern require impls Y;
+}
+
+// --- fail_final_require.carbon
+library "[[@TEST_NAME]]";
+
+interface Y {}
+
+interface Z {
+  // CHECK:STDERR: fail_final_require.carbon:[[@LINE+4]]:3: error: `final` not allowed on `require` declaration [ModifierNotAllowedOnDeclaration]
+  // CHECK:STDERR:   final require impls Y;
+  // CHECK:STDERR:   ^~~~~
+  // CHECK:STDERR:
+  final require impls Y;
+}
+
+// --- fail_impl_require.carbon
+library "[[@TEST_NAME]]";
+
+interface Y {}
+
+interface Z {
+  // CHECK:STDERR: fail_impl_require.carbon:[[@LINE+4]]:3: error: `impl` not allowed on `require` declaration [ModifierNotAllowedOnDeclaration]
+  // CHECK:STDERR:   impl require impls Y;
+  // CHECK:STDERR:   ^~~~
+  // CHECK:STDERR:
+  impl require impls Y;
+}
+
+// --- fail_override_require.carbon
+library "[[@TEST_NAME]]";
+
+interface Y {}
+
+interface Z {
+  // CHECK:STDERR: fail_override_require.carbon:[[@LINE+4]]:3: error: `override` not allowed on `require` declaration [ModifierNotAllowedOnDeclaration]
+  // CHECK:STDERR:   override require impls Y;
+  // CHECK:STDERR:   ^~~~~~~~
+  // CHECK:STDERR:
+  override require impls Y;
+}
+
+// --- fail_returned_require.carbon
+library "[[@TEST_NAME]]";
+
+interface Y {}
+
+interface Z {
+  // CHECK:STDERR: fail_returned_require.carbon:[[@LINE+8]]:3: error: unrecognized declaration introducer [UnrecognizedDecl]
+  // CHECK:STDERR:   returned require impls Y;
+  // CHECK:STDERR:   ^~~~~~~~
+  // CHECK:STDERR:
+  // CHECK:STDERR: fail_returned_require.carbon:[[@LINE+4]]:3: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo]
+  // CHECK:STDERR:   returned require impls Y;
+  // CHECK:STDERR:   ^~~~~~~~
+  // CHECK:STDERR:
+  returned require impls Y;
+}
+
+// --- fail_private_require.carbon
+library "[[@TEST_NAME]]";
+
+interface Y {}
+
+interface Z {
+  // CHECK:STDERR: fail_private_require.carbon:[[@LINE+4]]:3: error: `private` not allowed on `require` declaration [ModifierNotAllowedOnDeclaration]
+  // CHECK:STDERR:   private require impls Y;
+  // CHECK:STDERR:   ^~~~~~~
+  // CHECK:STDERR:
+  private require impls Y;
+}
+
+// --- fail_protected_require.carbon
+library "[[@TEST_NAME]]";
+
+interface Y {}
+
+interface Z {
+  // CHECK:STDERR: fail_protected_require.carbon:[[@LINE+4]]:3: error: `protected` not allowed on `require` declaration [ModifierNotAllowedOnDeclaration]
+  // CHECK:STDERR:   protected require impls Y;
+  // CHECK:STDERR:   ^~~~~~~~~
+  // CHECK:STDERR:
+  protected require impls Y;
+}
+
+// --- fail_override_virtual.carbon
+library "[[@TEST_NAME]]";
+
+interface Y {}
+
+interface Z {
+  // CHECK:STDERR: fail_override_virtual.carbon:[[@LINE+4]]:3: error: `virtual` not allowed on `require` declaration [ModifierNotAllowedOnDeclaration]
+  // CHECK:STDERR:   virtual require impls Y;
+  // CHECK:STDERR:   ^~~~~~~
+  // CHECK:STDERR:
+  virtual require impls Y;
+}

+ 1 - 5
toolchain/check/testdata/named_constraint/convert.carbon

@@ -37,16 +37,12 @@ fn G(T:! E2) {
 }
 //@dump-sem-ir-end
 
-// --- fail_todo_unspecified.carbon
+// --- unspecified.carbon
 library "[[@TEST_NAME]]";
 
 interface Z {}
 
 constraint E {
-  // CHECK:STDERR: fail_todo_unspecified.carbon:[[@LINE+4]]:3: error: semantics TODO: `require` [SemanticsTodo]
-  // CHECK:STDERR:   require impls Z;
-  // CHECK:STDERR:   ^~~~~~~
-  // CHECK:STDERR:
   require impls Z;
 }
 

+ 562 - 0
toolchain/check/testdata/named_constraint/require.carbon

@@ -0,0 +1,562 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon
+//
+// AUTOUPDATE
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/named_constraint/require.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/named_constraint/require.carbon
+
+// --- fail_todo_extend.carbon
+library "[[@TEST_NAME]]";
+
+interface Y {
+  fn YY();
+}
+
+//@dump-sem-ir-begin
+constraint Z {
+  extend require impls Y;
+}
+//@dump-sem-ir-end
+
+fn F(T:! Z) {
+  // CHECK:STDERR: fail_todo_extend.carbon:[[@LINE+4]]:3: error: member name `YY` not found [MemberNameNotFound]
+  // CHECK:STDERR:   T.YY();
+  // CHECK:STDERR:   ^~~~
+  // CHECK:STDERR:
+  T.YY();
+  // CHECK:STDERR: fail_todo_extend.carbon:[[@LINE+4]]:3: error: cannot convert type `T` that implements `type` into type implementing `Y` [ConversionFailureFacetToFacet]
+  // CHECK:STDERR:   T.(Y.YY)();
+  // CHECK:STDERR:   ^~~~~~~~
+  // CHECK:STDERR:
+  T.(Y.YY)();
+}
+
+// --- fail_todo_implicit_self_impls.carbon
+library "[[@TEST_NAME]]";
+
+interface Y {
+  fn YY();
+}
+
+//@dump-sem-ir-begin
+constraint Z {
+  require impls Y;
+}
+//@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 `type` into type implementing `Y` [ConversionFailureFacetToFacet]
+  // CHECK:STDERR:   T.(Y.YY)();
+  // CHECK:STDERR:   ^~~~~~~~
+  // CHECK:STDERR:
+  T.(Y.YY)();
+}
+
+// --- fail_todo_explicit_self_impls.carbon
+library "[[@TEST_NAME]]";
+
+interface Y {
+  fn YY();
+}
+
+//@dump-sem-ir-begin
+constraint Z {
+  require Self impls Y;
+}
+//@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 `type` into type implementing `Y` [ConversionFailureFacetToFacet]
+  // CHECK:STDERR:   T.(Y.YY)();
+  // CHECK:STDERR:   ^~~~~~~~
+  // CHECK:STDERR:
+  T.(Y.YY)();
+}
+
+// --- fail_implicit_self_no_extend_name_lookup_fails.carbon
+library "[[@TEST_NAME]]";
+
+interface Y {
+  fn YY();
+}
+
+constraint Z {
+  require impls Y;
+}
+
+fn F(T:! Z) {
+  // This should fail name lookup since Z does not extend Y.
+  //
+  // CHECK:STDERR: fail_implicit_self_no_extend_name_lookup_fails.carbon:[[@LINE+4]]:3: error: member name `YY` not found [MemberNameNotFound]
+  // CHECK:STDERR:   T.YY();
+  // CHECK:STDERR:   ^~~~
+  // CHECK:STDERR:
+  T.YY();
+}
+
+// --- fail_explicit_self_no_extend_name_lookup_fails.carbon
+library "[[@TEST_NAME]]";
+
+interface Y {
+  fn YY();
+}
+
+constraint Z {
+  require Self impls Y;
+}
+
+fn F(T:! Z) {
+  // This should fail name lookup since Z does not extend Y.
+  //
+  // CHECK:STDERR: fail_explicit_self_no_extend_name_lookup_fails.carbon:[[@LINE+4]]:3: error: member name `YY` not found [MemberNameNotFound]
+  // CHECK:STDERR:   T.YY();
+  // CHECK:STDERR:   ^~~~
+  // CHECK:STDERR:
+  T.YY();
+}
+
+// --- explicit_self_specific_impls.carbon
+library "[[@TEST_NAME]]";
+
+interface Y {}
+
+class C(T:! type);
+
+//@dump-sem-ir-begin
+constraint Z {
+  require C(Self) impls Y;
+}
+//@dump-sem-ir-end
+
+// --- require_impls_where.carbon
+library "[[@TEST_NAME]]";
+
+interface Y { let Y1:! type; }
+
+//@dump-sem-ir-begin
+constraint Z {
+  require impls Y where .Y1 = ();
+}
+//@dump-sem-ir-end
+
+// --- todo_fail_require_impls_incomplete_constraint.carbon
+library "[[@TEST_NAME]]";
+
+constraint Y;
+
+//@dump-sem-ir-begin
+constraint Z {
+  // TODO: This should fail since `Y` cannot be identified.
+  require impls Y;
+}
+//@dump-sem-ir-end
+
+// --- todo_fail_require_impls_incomplete_self.carbon
+library "[[@TEST_NAME]]";
+
+//@dump-sem-ir-begin
+constraint Z {
+  // TODO: This should fail since `Z` cannot be identified.
+  require impls Z;
+}
+//@dump-sem-ir-end
+
+// --- fail_require_impls_incomplete_self_specific.carbon
+library "[[@TEST_NAME]]";
+
+//@dump-sem-ir-begin
+constraint Z(T:! type) {
+  // TODO: This should fail since `Z` cannot be identified.
+  // CHECK:STDERR: fail_require_impls_incomplete_self_specific.carbon:[[@LINE+4]]:3: error: no `Self` reference found in `require` declaration; `Self` must appear in the self-type or as a generic parameter for each `interface` or `constraint` [RequireImplsMissingSelf]
+  // CHECK:STDERR:   require T impls Z(Self);
+  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~~~~
+  // CHECK:STDERR:
+  require T impls Z(Self);
+}
+//@dump-sem-ir-end
+
+// --- fail_require_impls_without_self.carbon
+library "[[@TEST_NAME]]";
+
+interface Y {
+  let Y1:! type;
+}
+
+//@dump-sem-ir-begin
+constraint Z(T:! type) {
+  // Either the type `T` or the facet type `Y` must mention `Self` in a way that
+  // it would appear in the type structure used for impl lookup (so inside a
+  // `where` does not count). But they don't.
+  //
+  // CHECK:STDERR: fail_require_impls_without_self.carbon:[[@LINE+4]]:3: error: no `Self` reference found in `require` declaration; `Self` must appear in the self-type or as a generic parameter for each `interface` or `constraint` [RequireImplsMissingSelf]
+  // CHECK:STDERR:   require T impls Y where .Y1 = Self;
+  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  // CHECK:STDERR:
+  require T impls Y where .Y1 = Self;
+}
+//@dump-sem-ir-end
+
+// --- fail_require_impls_without_self_in_one_interface.carbon
+library "[[@TEST_NAME]]";
+
+interface Y(T:! type) {}
+
+constraint Z(T:! type) {
+  // Self is in one interface but not the other.
+  //
+  // CHECK:STDERR: fail_require_impls_without_self_in_one_interface.carbon:[[@LINE+4]]:3: error: no `Self` reference found in `require` declaration; `Self` must appear in the self-type or as a generic parameter for each `interface` or `constraint` [RequireImplsMissingSelf]
+  // CHECK:STDERR:   require T impls Y(Self) & Y({});
+  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  // CHECK:STDERR:
+  require T impls Y(Self) & Y({});
+}
+
+// --- fail_self_impls_self.carbon
+library "[[@TEST_NAME]]";
+
+constraint Z(T:! type) {
+  // CHECK:STDERR: fail_self_impls_self.carbon:[[@LINE+4]]:17: error: `require` declaration constrained by a non-facet type; expected an `interface` or `constraint` name after `impls` [RequireImplsMissingFacetType]
+  // CHECK:STDERR:   require impls Self;
+  // CHECK:STDERR:                 ^~~~
+  // CHECK:STDERR:
+  require impls Self;
+}
+
+// --- fail_impls_type.carbon
+library "[[@TEST_NAME]]";
+
+class C(T:! type) {}
+
+constraint Z(T:! type) {
+  // CHECK:STDERR: fail_impls_type.carbon:[[@LINE+4]]:19: error: `require` declaration constrained by a non-facet type; expected an `interface` or `constraint` name after `impls` [RequireImplsMissingFacetType]
+  // CHECK:STDERR:   require T impls C(Self);
+  // CHECK:STDERR:                   ^~~~~~~
+  // CHECK:STDERR:
+  require T impls C(Self);
+}
+
+// --- fail_non_type_impls.carbon
+library "[[@TEST_NAME]]";
+
+interface Y {}
+
+constraint Z(T:! type) {
+  // CHECK:STDERR: fail_non_type_impls.carbon:[[@LINE+7]]:11: error: cannot implicitly convert non-type value of type `Core.IntLiteral` to `type` [ConversionFailureNonTypeToFacet]
+  // CHECK:STDERR:   require 1 impls Y;
+  // CHECK:STDERR:           ^
+  // CHECK:STDERR: fail_non_type_impls.carbon:[[@LINE+4]]:11: note: type `Core.IntLiteral` does not implement interface `Core.ImplicitAs(type)` [MissingImplInMemberAccessNote]
+  // CHECK:STDERR:   require 1 impls Y;
+  // CHECK:STDERR:           ^
+  // CHECK:STDERR:
+  require 1 impls Y;
+}
+
+// --- fail_impls_non_type.carbon
+library "[[@TEST_NAME]]";
+
+interface Y {}
+
+constraint Z(T:! type) {
+  // CHECK:STDERR: fail_impls_non_type.carbon:[[@LINE+4]]:17: error: `require` declaration constrained by a non-facet type; expected an `interface` or `constraint` name after `impls` [RequireImplsMissingFacetType]
+  // CHECK:STDERR:   require impls 1;
+  // CHECK:STDERR:                 ^
+  // CHECK:STDERR:
+  require impls 1;
+}
+
+// --- require_self_in_requirement.carbon
+library "[[@TEST_NAME]]";
+
+interface Y {
+  let Y1:! type;
+}
+
+//@dump-sem-ir-begin
+constraint Z {
+  // Self can appear in a requirement.
+  require impls Y where .Y1 = Self;
+}
+//@dump-sem-ir-end
+
+// CHECK:STDOUT: --- fail_todo_extend.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %type: type = facet_type <type> [concrete]
+// CHECK:STDOUT:   %Self.861: %type = symbolic_binding Self, 0 [symbolic]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   %Z.decl: type = constraint_decl @Z [concrete = constants.%type] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: constraint @Z {
+// CHECK:STDOUT:   %Self: %type = symbolic_binding Self, 0 [symbolic = constants.%Self.861]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .Y = <poisoned>
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_todo_implicit_self_impls.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %type: type = facet_type <type> [concrete]
+// CHECK:STDOUT:   %Self.861: %type = symbolic_binding Self, 0 [symbolic]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   %Z.decl: type = constraint_decl @Z [concrete = constants.%type] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: constraint @Z {
+// CHECK:STDOUT:   %Self: %type = symbolic_binding Self, 0 [symbolic = constants.%Self.861]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .Y = <poisoned>
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_todo_explicit_self_impls.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %type: type = facet_type <type> [concrete]
+// CHECK:STDOUT:   %Self.861: %type = symbolic_binding Self, 0 [symbolic]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   %Z.decl: type = constraint_decl @Z [concrete = constants.%type] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: constraint @Z {
+// CHECK:STDOUT:   %Self: %type = symbolic_binding Self, 0 [symbolic = constants.%Self.861]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .Y = <poisoned>
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- explicit_self_specific_impls.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %type: type = facet_type <type> [concrete]
+// CHECK:STDOUT:   %Self.861: %type = symbolic_binding Self, 0 [symbolic]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   %Z.decl: type = constraint_decl @Z [concrete = constants.%type] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: constraint @Z {
+// CHECK:STDOUT:   %Self: %type = symbolic_binding Self, 0 [symbolic = constants.%Self.861]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .C = <poisoned>
+// CHECK:STDOUT:   .Y = <poisoned>
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- require_impls_where.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %type: type = facet_type <type> [concrete]
+// CHECK:STDOUT:   %Self.861: %type = symbolic_binding Self, 0 [symbolic]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   %Z.decl: type = constraint_decl @Z [concrete = constants.%type] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: constraint @Z {
+// CHECK:STDOUT:   %Self: %type = symbolic_binding Self, 0 [symbolic = constants.%Self.861]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .Y = <poisoned>
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- todo_fail_require_impls_incomplete_constraint.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %type: type = facet_type <type> [concrete]
+// CHECK:STDOUT:   %Self: %type = symbolic_binding Self, 0 [symbolic]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   %Z.decl: type = constraint_decl @Z [concrete = constants.%type] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: constraint @Z {
+// CHECK:STDOUT:   %Self: %type = symbolic_binding Self, 0 [symbolic = constants.%Self]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .Y = <poisoned>
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- todo_fail_require_impls_incomplete_self.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %type: type = facet_type <type> [concrete]
+// CHECK:STDOUT:   %Self: %type = symbolic_binding Self, 0 [symbolic]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   %Z.decl: type = constraint_decl @Z [concrete = constants.%type] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: constraint @Z {
+// CHECK:STDOUT:   %Self: %type = symbolic_binding Self, 0 [symbolic = constants.%Self]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .Z = <poisoned>
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_require_impls_incomplete_self_specific.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %type: type = facet_type <type> [concrete]
+// CHECK:STDOUT:   %T: type = symbolic_binding T, 0 [symbolic]
+// CHECK:STDOUT:   %pattern_type: type = pattern_type type [concrete]
+// CHECK:STDOUT:   %Z.type: type = generic_named_constaint_type @Z [concrete]
+// CHECK:STDOUT:   %empty_struct: %Z.type = struct_value () [concrete]
+// CHECK:STDOUT:   %Self: %type = symbolic_binding Self, 1 [symbolic]
+// CHECK:STDOUT:   %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self [symbolic]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   %Z.decl: %Z.type = constraint_decl @Z [concrete = constants.%empty_struct] {
+// CHECK:STDOUT:     %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete]
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     <elided>
+// CHECK:STDOUT:     %T.loc4_14.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_14.1 (constants.%T)]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic constraint @Z(%T.loc4_14.2: type) {
+// CHECK:STDOUT:   %T.loc4_14.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_14.1 (constants.%T)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %Self.loc4_24.2: %type = symbolic_binding Self, 1 [symbolic = %Self.loc4_24.2 (constants.%Self)]
+// CHECK:STDOUT:   %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self.loc4_24.2 [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   constraint {
+// CHECK:STDOUT:     %Self.loc4_24.1: %type = symbolic_binding Self, 1 [symbolic = %Self.loc4_24.2 (constants.%Self)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     .Self = %Self.loc4_24.1
+// CHECK:STDOUT:     .T = <poisoned>
+// CHECK:STDOUT:     .Z = <poisoned>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @Z(constants.%T) {
+// CHECK:STDOUT:   %T.loc4_14.1 => constants.%T
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @Z(constants.%Self.binding.as_type) {
+// CHECK:STDOUT:   %T.loc4_14.1 => constants.%Self.binding.as_type
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_require_impls_without_self.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %Y.type: type = facet_type <@Y> [concrete]
+// CHECK:STDOUT:   %type: type = facet_type <type> [concrete]
+// CHECK:STDOUT:   %T: type = symbolic_binding T, 0 [symbolic]
+// CHECK:STDOUT:   %pattern_type: type = pattern_type type [concrete]
+// CHECK:STDOUT:   %Z.type: type = generic_named_constaint_type @Z [concrete]
+// CHECK:STDOUT:   %empty_struct: %Z.type = struct_value () [concrete]
+// CHECK:STDOUT:   %Self.aa1: %type = symbolic_binding Self, 1 [symbolic]
+// CHECK:STDOUT:   %.Self.a96: %Y.type = symbolic_binding .Self [symbolic_self]
+// CHECK:STDOUT:   %Y.lookup_impl_witness: <witness> = lookup_impl_witness %.Self.a96, @Y [symbolic_self]
+// CHECK:STDOUT:   %impl.elem0: type = impl_witness_access %Y.lookup_impl_witness, element0 [symbolic_self]
+// CHECK:STDOUT:   %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self.aa1 [symbolic]
+// CHECK:STDOUT:   %Y_where.type: type = facet_type <@Y where %impl.elem0 = %Self.binding.as_type> [symbolic]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   %Z.decl: %Z.type = constraint_decl @Z [concrete = constants.%empty_struct] {
+// CHECK:STDOUT:     %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete]
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     <elided>
+// CHECK:STDOUT:     %T.loc8_14.2: type = symbolic_binding T, 0 [symbolic = %T.loc8_14.1 (constants.%T)]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic constraint @Z(%T.loc8_14.2: type) {
+// CHECK:STDOUT:   %T.loc8_14.1: type = symbolic_binding T, 0 [symbolic = %T.loc8_14.1 (constants.%T)]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !definition:
+// CHECK:STDOUT:   %Self.loc8_24.2: %type = symbolic_binding Self, 1 [symbolic = %Self.loc8_24.2 (constants.%Self.aa1)]
+// CHECK:STDOUT:   %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self.loc8_24.2 [symbolic = %Self.binding.as_type (constants.%Self.binding.as_type)]
+// CHECK:STDOUT:   %Y_where.type: type = facet_type <@Y where constants.%impl.elem0 = %Self.binding.as_type> [symbolic = %Y_where.type (constants.%Y_where.type)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   constraint {
+// CHECK:STDOUT:     %Self.loc8_24.1: %type = symbolic_binding Self, 1 [symbolic = %Self.loc8_24.2 (constants.%Self.aa1)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     .Self = %Self.loc8_24.1
+// CHECK:STDOUT:     .T = <poisoned>
+// CHECK:STDOUT:     .Y = <poisoned>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @Z(constants.%T) {
+// CHECK:STDOUT:   %T.loc8_14.1 => constants.%T
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- require_self_in_requirement.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %type: type = facet_type <type> [concrete]
+// CHECK:STDOUT:   %Self.861: %type = symbolic_binding Self, 0 [symbolic]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   %Z.decl: type = constraint_decl @Z [concrete = constants.%type] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: constraint @Z {
+// CHECK:STDOUT:   %Self: %type = symbolic_binding Self, 0 [symbolic = constants.%Self.861]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .Y = <poisoned>
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 158 - 0
toolchain/check/testdata/named_constraint/require_invalid_modifiers.carbon

@@ -0,0 +1,158 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon
+//
+// AUTOUPDATE
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/named_constraint/require_invalid_modifiers.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/named_constraint/require_invalid_modifiers.carbon
+
+// --- fail_abstract_require.carbon
+library "[[@TEST_NAME]]";
+
+interface Y {}
+
+constraint Z {
+  // CHECK:STDERR: fail_abstract_require.carbon:[[@LINE+4]]:3: error: `abstract` not allowed on `require` declaration [ModifierNotAllowedOnDeclaration]
+  // CHECK:STDERR:   abstract require impls Y;
+  // CHECK:STDERR:   ^~~~~~~~
+  // CHECK:STDERR:
+  abstract require impls Y;
+}
+
+// --- fail_base_require.carbon
+library "[[@TEST_NAME]]";
+
+interface Y {}
+
+constraint Z {
+  // CHECK:STDERR: fail_base_require.carbon:[[@LINE+4]]:3: error: `base` not allowed on `require` declaration [ModifierNotAllowedOnDeclaration]
+  // CHECK:STDERR:   base require impls Y;
+  // CHECK:STDERR:   ^~~~
+  // CHECK:STDERR:
+  base require impls Y;
+}
+
+// --- fail_default_require.carbon
+library "[[@TEST_NAME]]";
+
+interface Y {}
+
+constraint Z {
+  // CHECK:STDERR: fail_default_require.carbon:[[@LINE+4]]:3: error: `default` not allowed on `require` declaration [ModifierNotAllowedOnDeclaration]
+  // CHECK:STDERR:   default require impls Y;
+  // CHECK:STDERR:   ^~~~~~~
+  // CHECK:STDERR:
+  default require impls Y;
+}
+
+// --- fail_extern_require.carbon
+library "[[@TEST_NAME]]";
+
+interface Y {}
+
+constraint Z {
+  // CHECK:STDERR: fail_extern_require.carbon:[[@LINE+4]]:3: error: `extern` not allowed on `require` declaration [ModifierNotAllowedOnDeclaration]
+  // CHECK:STDERR:   extern require impls Y;
+  // CHECK:STDERR:   ^~~~~~
+  // CHECK:STDERR:
+  extern require impls Y;
+}
+
+// --- fail_final_require.carbon
+library "[[@TEST_NAME]]";
+
+interface Y {}
+
+constraint Z {
+  // CHECK:STDERR: fail_final_require.carbon:[[@LINE+4]]:3: error: `final` not allowed on `require` declaration [ModifierNotAllowedOnDeclaration]
+  // CHECK:STDERR:   final require impls Y;
+  // CHECK:STDERR:   ^~~~~
+  // CHECK:STDERR:
+  final require impls Y;
+}
+
+// --- fail_impl_require.carbon
+library "[[@TEST_NAME]]";
+
+interface Y {}
+
+constraint Z {
+  // CHECK:STDERR: fail_impl_require.carbon:[[@LINE+4]]:3: error: `impl` not allowed on `require` declaration [ModifierNotAllowedOnDeclaration]
+  // CHECK:STDERR:   impl require impls Y;
+  // CHECK:STDERR:   ^~~~
+  // CHECK:STDERR:
+  impl require impls Y;
+}
+
+// --- fail_override_require.carbon
+library "[[@TEST_NAME]]";
+
+interface Y {}
+
+constraint Z {
+  // CHECK:STDERR: fail_override_require.carbon:[[@LINE+4]]:3: error: `override` not allowed on `require` declaration [ModifierNotAllowedOnDeclaration]
+  // CHECK:STDERR:   override require impls Y;
+  // CHECK:STDERR:   ^~~~~~~~
+  // CHECK:STDERR:
+  override require impls Y;
+}
+
+// --- fail_returned_require.carbon
+library "[[@TEST_NAME]]";
+
+interface Y {}
+
+constraint Z {
+  // CHECK:STDERR: fail_returned_require.carbon:[[@LINE+8]]:3: error: unrecognized declaration introducer [UnrecognizedDecl]
+  // CHECK:STDERR:   returned require impls Y;
+  // CHECK:STDERR:   ^~~~~~~~
+  // CHECK:STDERR:
+  // CHECK:STDERR: fail_returned_require.carbon:[[@LINE+4]]:3: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo]
+  // CHECK:STDERR:   returned require impls Y;
+  // CHECK:STDERR:   ^~~~~~~~
+  // CHECK:STDERR:
+  returned require impls Y;
+}
+
+// --- fail_private_require.carbon
+library "[[@TEST_NAME]]";
+
+interface Y {}
+
+constraint Z {
+  // CHECK:STDERR: fail_private_require.carbon:[[@LINE+4]]:3: error: `private` not allowed on `require` declaration [ModifierNotAllowedOnDeclaration]
+  // CHECK:STDERR:   private require impls Y;
+  // CHECK:STDERR:   ^~~~~~~
+  // CHECK:STDERR:
+  private require impls Y;
+}
+
+// --- fail_protected_require.carbon
+library "[[@TEST_NAME]]";
+
+interface Y {}
+
+constraint Z {
+  // CHECK:STDERR: fail_protected_require.carbon:[[@LINE+4]]:3: error: `protected` not allowed on `require` declaration [ModifierNotAllowedOnDeclaration]
+  // CHECK:STDERR:   protected require impls Y;
+  // CHECK:STDERR:   ^~~~~~~~~
+  // CHECK:STDERR:
+  protected require impls Y;
+}
+
+// --- fail_override_virtual.carbon
+library "[[@TEST_NAME]]";
+
+interface Y {}
+
+constraint Z {
+  // CHECK:STDERR: fail_override_virtual.carbon:[[@LINE+4]]:3: error: `virtual` not allowed on `require` declaration [ModifierNotAllowedOnDeclaration]
+  // CHECK:STDERR:   virtual require impls Y;
+  // CHECK:STDERR:   ^~~~~~~
+  // CHECK:STDERR:
+  virtual require impls Y;
+}

+ 5 - 0
toolchain/diagnostics/diagnostic_kind.def

@@ -352,6 +352,11 @@ CARBON_DIAGNOSTIC_KIND(FinalImplOverlapsDifferentFileNote)
 CARBON_DIAGNOSTIC_KIND(MissingImplInMemberAccess)
 CARBON_DIAGNOSTIC_KIND(MissingImplInMemberAccessNote)
 
+// Require checking.
+CARBON_DIAGNOSTIC_KIND(RequireImplsMissingFacetType)
+CARBON_DIAGNOSTIC_KIND(RequireImplsMissingSelf)
+CARBON_DIAGNOSTIC_KIND(RequireInWrongScope)
+
 // Let declaration checking.
 CARBON_DIAGNOSTIC_KIND(ExpectedInitializerAfterLet)
 

+ 1 - 1
toolchain/lex/token_kind.def

@@ -163,6 +163,7 @@ CARBON_DECL_INTRODUCER_TOKEN(Let,         "let")
 CARBON_DECL_INTRODUCER_TOKEN(Library,     "library")
 CARBON_DECL_INTRODUCER_TOKEN(Namespace,   "namespace")
 CARBON_DECL_INTRODUCER_TOKEN(Package,     "package")
+CARBON_DECL_INTRODUCER_TOKEN(Require,     "require")
 CARBON_TOKEN_WITH_VIRTUAL_NODE(
   CARBON_DECL_INTRODUCER_TOKEN(Var,       "var"))
 
@@ -204,7 +205,6 @@ CARBON_KEYWORD_TOKEN(Partial,             "partial")
 CARBON_KEYWORD_TOKEN(Private,             "private")
 CARBON_KEYWORD_TOKEN(Protected,           "protected")
 CARBON_KEYWORD_TOKEN(Ref,                 "ref")
-CARBON_KEYWORD_TOKEN(Require,             "require")
 CARBON_KEYWORD_TOKEN(Return,              "return")
 CARBON_KEYWORD_TOKEN(Returned,            "returned")
 CARBON_KEYWORD_TOKEN(SelfTypeIdentifier,  "Self")

+ 27 - 0
toolchain/parse/testdata/generics/interface/require.carbon

@@ -48,6 +48,14 @@ interface Z {
   extend require impls Y;
 }
 
+// --- type_and.carbon
+
+interface Y {}
+
+interface Z {
+  require impls Y & Y;
+}
+
 // --- fail_missing_impls.carbon
 
 interface Y {}
@@ -190,6 +198,25 @@ interface Z {
 // CHECK:STDOUT:     {kind: 'InterfaceDefinition', text: '}', subtree_size: 9},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
 // CHECK:STDOUT:   ]
+// CHECK:STDOUT: - filename: type_and.carbon
+// CHECK:STDOUT:   parse_tree: [
+// CHECK:STDOUT:     {kind: 'FileStart', text: ''},
+// CHECK:STDOUT:         {kind: 'InterfaceIntroducer', text: 'interface'},
+// CHECK:STDOUT:         {kind: 'IdentifierNameNotBeforeParams', text: 'Y'},
+// CHECK:STDOUT:       {kind: 'InterfaceDefinitionStart', text: '{', subtree_size: 3},
+// CHECK:STDOUT:     {kind: 'InterfaceDefinition', text: '}', subtree_size: 4},
+// CHECK:STDOUT:         {kind: 'InterfaceIntroducer', text: 'interface'},
+// CHECK:STDOUT:         {kind: 'IdentifierNameNotBeforeParams', text: 'Z'},
+// CHECK:STDOUT:       {kind: 'InterfaceDefinitionStart', text: '{', subtree_size: 3},
+// CHECK:STDOUT:         {kind: 'RequireIntroducer', text: 'require'},
+// CHECK:STDOUT:         {kind: 'RequireDefaultSelfImpls', text: 'impls'},
+// CHECK:STDOUT:           {kind: 'IdentifierNameExpr', text: 'Y'},
+// CHECK:STDOUT:           {kind: 'IdentifierNameExpr', text: 'Y'},
+// CHECK:STDOUT:         {kind: 'InfixOperatorAmp', text: '&', subtree_size: 3},
+// CHECK:STDOUT:       {kind: 'RequireDecl', text: ';', subtree_size: 6},
+// CHECK:STDOUT:     {kind: 'InterfaceDefinition', text: '}', subtree_size: 10},
+// CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
+// CHECK:STDOUT:   ]
 // CHECK:STDOUT: - filename: fail_missing_impls.carbon
 // CHECK:STDOUT:   parse_tree: [
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},