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

Handle parsing of `require`...`impls` declarations (#6255)

Check is not implemented yet, but some tests are added.
Dana Jansens 6 месяцев назад
Родитель
Сommit
26381f6eaf
30 измененных файлов с 790 добавлено и 61 удалено
  1. 2 2
      toolchain/check/handle_impl.cpp
  2. 30 0
      toolchain/check/handle_require.cpp
  3. 2 2
      toolchain/check/impl.cpp
  4. 4 4
      toolchain/check/merge.cpp
  5. 3 0
      toolchain/check/node_stack.h
  6. 54 0
      toolchain/check/testdata/facet/require_invalid.carbon
  7. 275 0
      toolchain/check/testdata/interface/require.carbon
  8. 1 5
      toolchain/check/testdata/named_constraint/convert.carbon
  9. 1 0
      toolchain/diagnostics/diagnostic_kind.def
  10. 3 0
      toolchain/parse/handle_decl_scope_loop.cpp
  11. 2 2
      toolchain/parse/handle_impl.cpp
  12. 48 0
      toolchain/parse/handle_require.cpp
  13. 15 12
      toolchain/parse/node_category.h
  14. 1 0
      toolchain/parse/node_ids.h
  15. 7 2
      toolchain/parse/node_kind.def
  16. 4 0
      toolchain/parse/precedence.h
  17. 34 2
      toolchain/parse/state.def
  18. 3 3
      toolchain/parse/testdata/function/decl_statement.carbon
  19. 1 1
      toolchain/parse/testdata/function/definition.carbon
  20. 1 1
      toolchain/parse/testdata/generics/impl/basic.carbon
  21. 5 5
      toolchain/parse/testdata/generics/impl/class.carbon
  22. 1 1
      toolchain/parse/testdata/generics/impl/declaration.carbon
  23. 1 1
      toolchain/parse/testdata/generics/impl/empty_body.carbon
  24. 4 4
      toolchain/parse/testdata/generics/impl/fail_impl.carbon
  25. 2 2
      toolchain/parse/testdata/generics/impl/fail_out_of_line_member.carbon
  26. 2 2
      toolchain/parse/testdata/generics/impl/forall.carbon
  27. 240 0
      toolchain/parse/testdata/generics/interface/require.carbon
  28. 2 2
      toolchain/parse/testdata/packages/package/impl.carbon
  29. 5 5
      toolchain/parse/testdata/where_expr/impl_where.carbon
  30. 37 3
      toolchain/parse/typed_nodes.h

+ 2 - 2
toolchain/check/handle_impl.cpp

@@ -54,7 +54,7 @@ auto HandleParseNode(Context& context, Parse::ForallId /*node_id*/) -> bool {
   return true;
 }
 
-auto HandleParseNode(Context& context, Parse::TypeImplAsId node_id) -> bool {
+auto HandleParseNode(Context& context, Parse::ImplTypeAsId node_id) -> bool {
   auto [self_node, self_id] = context.node_stack().PopExprWithNodeId();
   auto self_type_inst_id = ExprAsType(context, self_node, self_id).inst_id;
   context.node_stack().Push(node_id, self_type_inst_id);
@@ -67,7 +67,7 @@ auto HandleParseNode(Context& context, Parse::TypeImplAsId node_id) -> bool {
   return true;
 }
 
-auto HandleParseNode(Context& context, Parse::DefaultSelfImplAsId node_id)
+auto HandleParseNode(Context& context, Parse::ImplDefaultSelfAsId node_id)
     -> bool {
   auto self_inst_id = SemIR::TypeInstId::None;
 

+ 30 - 0
toolchain/check/handle_require.cpp

@@ -0,0 +1,30 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#include "toolchain/check/context.h"
+#include "toolchain/check/handle.h"
+#include "toolchain/parse/node_ids.h"
+
+namespace Carbon::Check {
+
+auto HandleParseNode(Context& context, Parse::RequireIntroducerId node_id)
+    -> bool {
+  return context.TODO(node_id, "require");
+}
+
+auto HandleParseNode(Context& context, Parse::RequireDefaultSelfImplsId node_id)
+    -> bool {
+  return context.TODO(node_id, "require");
+}
+
+auto HandleParseNode(Context& context, Parse::RequireTypeImplsId node_id)
+    -> bool {
+  return context.TODO(node_id, "require");
+}
+
+auto HandleParseNode(Context& context, Parse::RequireDeclId node_id) -> bool {
+  return context.TODO(node_id, "require");
+}
+
+}  // namespace Carbon::Check

+ 2 - 2
toolchain/check/impl.cpp

@@ -433,7 +433,7 @@ static auto ExtendImpl(Context& context, Parse::NodeId extend_node,
   const auto& impl = context.impls().Get(impl_id);
 
   if (context.parse_tree().node_kind(self_type_node_id) ==
-      Parse::NodeKind::TypeImplAs) {
+      Parse::NodeKind::ImplTypeAs) {
     CARBON_DIAGNOSTIC(ExtendImplSelfAs, Error,
                       "cannot `extend` an `impl` with an explicit self type");
     auto diag = context.emitter().Build(extend_node, ExtendImplSelfAs);
@@ -448,7 +448,7 @@ static auto ExtendImpl(Context& context, Parse::NodeId extend_node,
     // The explicit self type is the same as the default self type, so suggest
     // removing it and recover as if it were not present.
     if (auto self_as =
-            context.parse_tree_and_subtrees().ExtractAs<Parse::TypeImplAs>(
+            context.parse_tree_and_subtrees().ExtractAs<Parse::ImplTypeAs>(
                 self_type_node_id)) {
       CARBON_DIAGNOSTIC(ExtendImplSelfAsDefault, Note,
                         "remove the explicit `Self` type here");

+ 4 - 4
toolchain/check/merge.cpp

@@ -461,17 +461,17 @@ static auto CheckRedeclParamSyntax(Context& context,
       // https://github.com/carbon-language/carbon-lang/blob/trunk/proposals/p3763.md#redeclarations
       auto new_node_kind = context.parse_tree().node_kind(new_node_id);
       auto prev_node_kind = context.parse_tree().node_kind(prev_node_id);
-      if (new_node_kind == Parse::NodeKind::DefaultSelfImplAs &&
+      if (new_node_kind == Parse::NodeKind::ImplDefaultSelfAs &&
           prev_node_kind == Parse::NodeKind::SelfTypeNameExpr &&
           context.parse_tree().node_kind(prev_iter[1]) ==
-              Parse::NodeKind::TypeImplAs) {
+              Parse::NodeKind::ImplTypeAs) {
         ++prev_iter;
         continue;
       }
-      if (prev_node_kind == Parse::NodeKind::DefaultSelfImplAs &&
+      if (prev_node_kind == Parse::NodeKind::ImplDefaultSelfAs &&
           new_node_kind == Parse::NodeKind::SelfTypeNameExpr &&
           context.parse_tree().node_kind(new_iter[1]) ==
-              Parse::NodeKind::TypeImplAs) {
+              Parse::NodeKind::ImplTypeAs) {
         ++new_iter;
         continue;
       }

+ 3 - 0
toolchain/check/node_stack.h

@@ -523,6 +523,9 @@ 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:

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

@@ -0,0 +1,54 @@
+// 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/facet/require_invalid.carbon
+// 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
+library "[[@TEST_NAME]]";
+
+interface Y {}
+
+// CHECK:STDERR: fail_todo_require_outside_scope.carbon:[[@LINE+4]]:1: error: semantics TODO: `require` [SemanticsTodo]
+// CHECK:STDERR: require impls Y;
+// CHECK:STDERR: ^~~~~~~
+// CHECK:STDERR:
+require impls Y;
+
+// --- fail_todo_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:   require impls Y;
+  // CHECK:STDERR:   ^~~~~~~
+  // CHECK:STDERR:
+  require impls Y;
+}
+
+// --- fail_require_in_fn.carbon
+library "[[@TEST_NAME]]";
+
+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:   ^~~~~~~
+  // CHECK:STDERR:
+  // CHECK:STDERR: fail_require_in_fn.carbon:[[@LINE+4]]:3: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo]
+  // CHECK:STDERR:   require impls Y;
+  // CHECK:STDERR:   ^~~~~~~
+  // CHECK:STDERR:
+  require impls Y;
+}

+ 275 - 0
toolchain/check/testdata/interface/require.carbon

@@ -0,0 +1,275 @@
+// 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.carbon
+// 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
+library "[[@TEST_NAME]]";
+
+interface Y {
+  fn YY();
+}
+
+//@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:   ^~~~~~~
+  // CHECK:STDERR:
+  require impls Y;
+}
+//@dump-sem-ir-end
+
+fn F(T:! Z) {
+  T.(Y.YY)();
+}
+
+// --- fail_todo_explicit_self_impls.carbon
+library "[[@TEST_NAME]]";
+
+interface Y {
+  fn YY();
+}
+
+//@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) {
+  T.(Y.YY)();
+}
+
+// --- fail_todo_implicit_self_no_extend_name_lookup_fails.carbon
+library "[[@TEST_NAME]]";
+
+interface Y {
+  fn YY();
+}
+
+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.
+  T.YY();
+}
+
+// --- fail_todo_explicit_self_no_extend_name_lookup_fails.carbon
+library "[[@TEST_NAME]]";
+
+interface Y {
+  fn YY();
+}
+
+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.
+  T.YY();
+}
+
+// --- fail_todo_explicit_self_specific_impls.carbon
+library "[[@TEST_NAME]]";
+
+interface Y {}
+
+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
+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
+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
+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:
+  require T impls Y;
+}
+//@dump-sem-ir-end
+
+// --- fail_todo_extend.carbon
+library "[[@TEST_NAME]]";
+
+interface Y {
+  fn YY();
+}
+
+//@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;
+}
+//@dump-sem-ir-end
+
+fn F(T:! Z) {
+  T.YY();
+  T.(Y.YY)();
+}
+
+// CHECK:STDOUT: --- fail_todo_implicit_self_impls.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Z {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = <unexpected>.inst6000001D
+// CHECK:STDOUT:   witness = invalid
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_todo_explicit_self_impls.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Z {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = <unexpected>.inst5000001D
+// CHECK:STDOUT:   witness = invalid
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_todo_explicit_self_specific_impls.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Z {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = <unexpected>.inst68000023
+// CHECK:STDOUT:   witness = invalid
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_todo_impls_where.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Z {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = <unexpected>.inst5800001A
+// CHECK:STDOUT:   witness = invalid
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_todo_other_impls_with_self.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T, 0 [symbolic]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic interface @Z(<unexpected>.inst78000017.loc6_13: type) {
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T, 0 [symbolic = %T (constants.%T)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   interface {
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     .Self = <unexpected>.inst78000021
+// CHECK:STDOUT:     witness = invalid
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @Z(constants.%T) {
+// CHECK:STDOUT:   %T => constants.%T
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_todo_other_impls_without_self.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T, 0 [symbolic]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic interface @Z(<unexpected>.inst44000017.loc6_13: type) {
+// CHECK:STDOUT:   %T: type = bind_symbolic_name T, 0 [symbolic = %T (constants.%T)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   interface {
+// CHECK:STDOUT:   !members:
+// CHECK:STDOUT:     .Self = <unexpected>.inst44000021
+// CHECK:STDOUT:     witness = invalid
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @Z(constants.%T) {
+// CHECK:STDOUT:   %T => constants.%T
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_todo_extend.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @Z {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = <unexpected>.inst6400001D
+// CHECK:STDOUT:   witness = invalid
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

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

@@ -43,11 +43,7 @@ library "[[@TEST_NAME]]";
 interface Z {}
 
 constraint E {
-  // CHECK:STDERR: fail_todo_unspecified.carbon:[[@LINE+8]]:3: error: unrecognized declaration introducer [UnrecognizedDecl]
-  // CHECK:STDERR:   require impls Z;
-  // CHECK:STDERR:   ^~~~~~~
-  // CHECK:STDERR:
-  // CHECK:STDERR: fail_todo_unspecified.carbon:[[@LINE+4]]:3: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo]
+  // CHECK:STDERR: fail_todo_unspecified.carbon:[[@LINE+4]]:3: error: semantics TODO: `require` [SemanticsTodo]
   // CHECK:STDERR:   require impls Z;
   // CHECK:STDERR:   ^~~~~~~
   // CHECK:STDERR:

+ 1 - 0
toolchain/diagnostics/diagnostic_kind.def

@@ -162,6 +162,7 @@ CARBON_DIAGNOSTIC_KIND(ImplExpectedAfterForall)
 CARBON_DIAGNOSTIC_KIND(ImplExpectedAs)
 CARBON_DIAGNOSTIC_KIND(ExpectedAssociatedConstantIdentifier)
 CARBON_DIAGNOSTIC_KIND(ExpectedAssociatedConstantColonExclaim)
+CARBON_DIAGNOSTIC_KIND(RequireExpectedImpls)
 
 // Alias diagnostics.
 CARBON_DIAGNOSTIC_KIND(ExpectedAliasInitializer)

+ 3 - 0
toolchain/parse/handle_decl_scope_loop.cpp

@@ -130,6 +130,8 @@ static constexpr auto DeclIntroducers = [] {
       StateKind::TypeAfterIntroducerAsInterface);
   set(Lex::TokenKind::Namespace, NodeKind::NamespaceStart,
       StateKind::Namespace);
+  set(Lex::TokenKind::Require, NodeKind::RequireIntroducer,
+      StateKind::RequireAfterIntroducer);
   set_contextual(Lex::TokenKind::Let, RegularContext, NodeKind::LetIntroducer,
                  StateKind::Let);
   set_contextual(Lex::TokenKind::Let, ClassContext, NodeKind::LetIntroducer,
@@ -225,6 +227,7 @@ static auto ResolveAmbiguousTokenAsDeclaration(Context& context,
         case Lex::TokenKind::Let:
         case Lex::TokenKind::Library:
         case Lex::TokenKind::Namespace:
+        case Lex::TokenKind::Require:
         case Lex::TokenKind::Var:
 #define CARBON_PARSE_NODE_KIND(Name)
 #define CARBON_PARSE_NODE_KIND_TOKEN_MODIFIER(Name) case Lex::TokenKind::Name:

+ 2 - 2
toolchain/parse/handle_impl.cpp

@@ -10,7 +10,7 @@ namespace Carbon::Parse {
 static auto ExpectAsOrTypeExpression(Context& context) -> void {
   if (context.PositionIs(Lex::TokenKind::As)) {
     // as <expression> ...
-    context.AddLeafNode(NodeKind::DefaultSelfImplAs, context.Consume());
+    context.AddLeafNode(NodeKind::ImplDefaultSelfAs, context.Consume());
     context.PushState(StateKind::Expr);
   } else {
     // <expression> as <expression>...
@@ -62,7 +62,7 @@ auto HandleImplAfterForall(Context& context) -> void {
 auto HandleImplBeforeAs(Context& context) -> void {
   auto state = context.PopState();
   if (auto as = context.ConsumeIf(Lex::TokenKind::As)) {
-    context.AddNode(NodeKind::TypeImplAs, *as, state.has_error);
+    context.AddNode(NodeKind::ImplTypeAs, *as, state.has_error);
     context.PushState(StateKind::Expr);
   } else {
     if (!state.has_error) {

+ 48 - 0
toolchain/parse/handle_require.cpp

@@ -0,0 +1,48 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#include "toolchain/parse/context.h"
+#include "toolchain/parse/handle.h"
+
+namespace Carbon::Parse {
+
+auto HandleRequireAfterIntroducer(Context& context) -> void {
+  auto state = context.PopState();
+  state.kind = StateKind::RequireDecl;
+  context.PushState(state);
+
+  if (context.PositionIs(Lex::TokenKind::Impls)) {
+    // impls <expression> ...
+    context.AddLeafNode(NodeKind::RequireDefaultSelfImpls, context.Consume());
+    context.PushState(StateKind::Expr);
+  } else {
+    // <expression> impls <expression>...
+    context.PushState(StateKind::RequireBeforeImpls);
+    context.PushStateForExpr(PrecedenceGroup::ForRequirements());
+  }
+}
+
+auto HandleRequireBeforeImpls(Context& context) -> void {
+  auto state = context.PopState();
+  if (auto impls = context.ConsumeIf(Lex::TokenKind::Impls)) {
+    context.AddNode(NodeKind::RequireTypeImpls, *impls, state.has_error);
+    context.PushState(StateKind::Expr);
+  } else {
+    if (!state.has_error) {
+      CARBON_DIAGNOSTIC(RequireExpectedImpls, Error,
+                        "expected `impls` in `require` declaration");
+      context.emitter().Emit(*context.position(), RequireExpectedImpls);
+    }
+    context.ReturnErrorOnState();
+  }
+}
+
+auto HandleRequireDecl(Context& context) -> void {
+  auto state = context.PopState();
+  context.AddNodeExpectingDeclSemi(state, NodeKind::RequireDecl,
+                                   Lex::TokenKind::Require,
+                                   /*is_def_allowed=*/false);
+}
+
+}  // namespace Carbon::Parse

+ 15 - 12
toolchain/parse/node_category.h

@@ -15,18 +15,21 @@ namespace Carbon::Parse {
 //   #define CARBON_NODE_CATEGORY_FOR_XYZ(Name) ...
 //   CARBON_NODE_CATEGORY(CARBON_NODE_CATEGORY_FOR_XYZ)
 //   #undef CARBON_NODE_CATEGORY_FOR_XYZ
-#define CARBON_NODE_CATEGORY(X) \
-  X(Decl)                       \
-  X(Expr)                       \
-  X(ImplAs)                     \
-  X(IntConst)                   \
-  X(MemberExpr)                 \
-  X(MemberName)                 \
-  X(Modifier)                   \
-  X(NonExprName)                \
-  X(PackageName)                \
-  X(Pattern)                    \
-  X(Requirement)                \
+#define CARBON_NODE_CATEGORY(X)                        \
+  X(Decl)                                              \
+  X(Expr)                                              \
+  /* `impl <type> as` or just `impl as` */             \
+  X(ImplAs)                                            \
+  X(IntConst)                                          \
+  X(MemberExpr)                                        \
+  X(MemberName)                                        \
+  X(Modifier)                                          \
+  X(NonExprName)                                       \
+  X(PackageName)                                       \
+  X(Pattern)                                           \
+  /* `require <type> impls` or just `require impls` */ \
+  X(RequireImpls)                                      \
+  X(Requirement)                                       \
   X(Statement)
 
 // We expect this to grow, so are using a bigger size than needed.

+ 1 - 0
toolchain/parse/node_ids.h

@@ -108,6 +108,7 @@ using AnyModifierId = NodeIdInCategory<NodeCategory::Modifier>;
 using AnyPatternId = NodeIdInCategory<NodeCategory::Pattern>;
 using AnyStatementId =
     NodeIdInCategory<NodeCategory::Statement | NodeCategory::Decl>;
+using AnyRequireImplsId = NodeIdInCategory<NodeCategory::RequireImpls>;
 using AnyRequirementId = NodeIdInCategory<NodeCategory::Requirement>;
 using AnyNonExprNameId = NodeIdInCategory<NodeCategory::NonExprName>;
 using AnyPackageNameId = NodeIdInCategory<NodeCategory::PackageName>;

+ 7 - 2
toolchain/parse/node_kind.def

@@ -367,8 +367,13 @@ CARBON_PARSE_NODE_KIND(ImplDefinitionStart)
 CARBON_PARSE_NODE_KIND(ImplDefinition)
 CARBON_PARSE_NODE_KIND(ImplDecl)
 CARBON_PARSE_NODE_KIND(Forall)
-CARBON_PARSE_NODE_KIND(TypeImplAs)
-CARBON_PARSE_NODE_KIND(DefaultSelfImplAs)
+CARBON_PARSE_NODE_KIND(ImplTypeAs)
+CARBON_PARSE_NODE_KIND(ImplDefaultSelfAs)
+
+CARBON_PARSE_NODE_KIND(RequireIntroducer)
+CARBON_PARSE_NODE_KIND(RequireTypeImpls)
+CARBON_PARSE_NODE_KIND(RequireDefaultSelfImpls)
+CARBON_PARSE_NODE_KIND(RequireDecl)
 
 CARBON_PARSE_NODE_KIND(NamedConstraintIntroducer)
 CARBON_PARSE_NODE_KIND(NamedConstraintDefinitionStart)

+ 4 - 0
toolchain/parse/precedence.h

@@ -58,6 +58,10 @@ class PrecedenceGroup {
   // `impl` and `as`.
   static auto ForImplAs() -> PrecedenceGroup;
 
+  // Get the precedence level at which to parse the type expression between
+  // `require` and `impls`.
+  static auto ForRequireImpls() -> PrecedenceGroup;
+
   // Get the precedence level at which to parse expressions in requirements
   // after `where` or `require`.
   static auto ForRequirements() -> PrecedenceGroup;

+ 34 - 2
toolchain/parse/state.def

@@ -359,6 +359,10 @@ CARBON_PARSE_STATE(DeclNameAndParamsAfterParams)
 // ^~~~~~~
 //   1. Package
 //
+// require ...
+// ^~~~~~~
+//   1. RequireAfterIntroducer
+//
 // var ...        (variant is Regular)
 // ^~~
 //   1. VarAsRegular
@@ -1336,7 +1340,7 @@ CARBON_PARSE_STATE(BaseDecl)
 //      ^~
 //   1. Expr
 //   2. DeclOrDefinitionAsImpl
-// impl type_expression as ...
+// impl TypeExpression as ...
 //     ^
 //   1. Expr
 //   2. ImplBeforeAs
@@ -1349,7 +1353,7 @@ CARBON_PARSE_STATE(ImplAfterIntroducer)
 // impl forall [ ... ] as ...
 //                     ^~
 //   1. Expr
-// impl forall [ ... ] type_expression as ...
+// impl forall [ ... ] TypeExpression as ...
 //                    ^
 //   1. Expr
 //   2. ImplBeforeAs
@@ -1363,6 +1367,34 @@ CARBON_PARSE_STATE(ImplAfterForall)
 //   1. Expr
 CARBON_PARSE_STATE(ImplBeforeAs)
 
+// Handles processing of a `require...impls` declaration after the introducer.
+//
+// require impls ...
+//         ^~~~~
+//   1. Expr
+//   2. RequireDecl
+// require TypeExpression impls ...
+//        ^
+//   1. Expr
+//   2. RequireBeforeImpls
+//   3. RequireDecl
+CARBON_PARSE_STATE(RequireAfterIntroducer)
+
+// Handles processing of the `impls` in a `require` declaration after the type
+// expression.
+//
+// require TypeExpression impls ...
+//                        ^~~~~
+//   1. Expr
+CARBON_PARSE_STATE(RequireBeforeImpls)
+
+// Handles processing of a completed `require` declaration.
+//
+// require TypeExpression impls FacetTypeExpression ;
+//                                                  ^
+//   (state done)
+CARBON_PARSE_STATE(RequireDecl)
+
 // Handles the start of a `var` or `returned var` in a non-class context.
 //
 // var ...             (variant is Regular)

+ 3 - 3
toolchain/parse/testdata/function/decl_statement.carbon

@@ -86,14 +86,14 @@ fn F() {
 // CHECK:STDOUT:       {kind: 'InterfaceDefinition', text: '}', subtree_size: 4},
 // CHECK:STDOUT:           {kind: 'ImplIntroducer', text: 'impl'},
 // CHECK:STDOUT:             {kind: 'IdentifierNameExpr', text: 'A'},
-// CHECK:STDOUT:           {kind: 'TypeImplAs', text: 'as', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ImplTypeAs', text: 'as', subtree_size: 2},
 // CHECK:STDOUT:           {kind: 'IdentifierNameExpr', text: 'I'},
 // CHECK:STDOUT:         {kind: 'ImplDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:       {kind: 'ImplDefinition', text: '}', subtree_size: 6},
 // CHECK:STDOUT:           {kind: 'ImplIntroducer', text: 'impl'},
 // CHECK:STDOUT:           {kind: 'FinalModifier', text: 'final'},
 // CHECK:STDOUT:             {kind: 'IdentifierNameExpr', text: 'A'},
-// CHECK:STDOUT:           {kind: 'TypeImplAs', text: 'as', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ImplTypeAs', text: 'as', subtree_size: 2},
 // CHECK:STDOUT:           {kind: 'IdentifierNameExpr', text: 'I'},
 // CHECK:STDOUT:         {kind: 'ImplDefinitionStart', text: '{', subtree_size: 6},
 // CHECK:STDOUT:       {kind: 'ImplDefinition', text: '}', subtree_size: 7},
@@ -139,7 +139,7 @@ fn F() {
 // CHECK:STDOUT:       {kind: 'BaseDecl', text: ';', subtree_size: 5},
 // CHECK:STDOUT:         {kind: 'ImplIntroducer', text: 'impl'},
 // CHECK:STDOUT:         {kind: 'ExtendModifier', text: 'extend'},
-// CHECK:STDOUT:         {kind: 'DefaultSelfImplAs', text: 'as'},
+// CHECK:STDOUT:         {kind: 'ImplDefaultSelfAs', text: 'as'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameExpr', text: 'I'},
 // CHECK:STDOUT:       {kind: 'ImplDecl', text: ';', subtree_size: 5},
 // CHECK:STDOUT:         {kind: 'NamespaceStart', text: 'namespace'},

+ 1 - 1
toolchain/parse/testdata/function/definition.carbon

@@ -186,7 +186,7 @@ fn TestRecoveryFromSpuriousEquals();
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:         {kind: 'ImplIntroducer', text: 'impl'},
 // CHECK:STDOUT:           {kind: 'IdentifierNameExpr', text: 'T'},
-// CHECK:STDOUT:         {kind: 'TypeImplAs', text: 'as', subtree_size: 2},
+// CHECK:STDOUT:         {kind: 'ImplTypeAs', text: 'as', subtree_size: 2},
 // CHECK:STDOUT:         {kind: 'IdentifierNameExpr', text: 'I'},
 // CHECK:STDOUT:       {kind: 'ImplDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:           {kind: 'FunctionIntroducer', text: 'fn'},

+ 1 - 1
toolchain/parse/testdata/generics/impl/basic.carbon

@@ -17,7 +17,7 @@ impl i32 as Interface {
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:         {kind: 'ImplIntroducer', text: 'impl'},
 // CHECK:STDOUT:           {kind: 'IntTypeLiteral', text: 'i32'},
-// CHECK:STDOUT:         {kind: 'TypeImplAs', text: 'as', subtree_size: 2},
+// CHECK:STDOUT:         {kind: 'ImplTypeAs', text: 'as', subtree_size: 2},
 // CHECK:STDOUT:         {kind: 'IdentifierNameExpr', text: 'Interface'},
 // CHECK:STDOUT:       {kind: 'ImplDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},

+ 5 - 5
toolchain/parse/testdata/generics/impl/class.carbon

@@ -26,23 +26,23 @@ class C {
 // CHECK:STDOUT:         {kind: 'IdentifierNameNotBeforeParams', text: 'C'},
 // CHECK:STDOUT:       {kind: 'ClassDefinitionStart', text: '{', subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'ImplIntroducer', text: 'impl'},
-// CHECK:STDOUT:         {kind: 'DefaultSelfImplAs', text: 'as'},
+// CHECK:STDOUT:         {kind: 'ImplDefaultSelfAs', text: 'as'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameExpr', text: 'ForwardDeclared'},
 // CHECK:STDOUT:       {kind: 'ImplDecl', text: ';', subtree_size: 4},
 // CHECK:STDOUT:           {kind: 'ImplIntroducer', text: 'impl'},
 // CHECK:STDOUT:             {kind: 'IdentifierNameExpr', text: 'C'},
-// CHECK:STDOUT:           {kind: 'TypeImplAs', text: 'as', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ImplTypeAs', text: 'as', subtree_size: 2},
 // CHECK:STDOUT:           {kind: 'IdentifierNameExpr', text: 'ExplicitType'},
 // CHECK:STDOUT:         {kind: 'ImplDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:       {kind: 'ImplDefinition', text: '}', subtree_size: 6},
 // CHECK:STDOUT:         {kind: 'ImplIntroducer', text: 'impl'},
 // CHECK:STDOUT:           {kind: 'SelfTypeNameExpr', text: 'Self'},
-// CHECK:STDOUT:         {kind: 'TypeImplAs', text: 'as', subtree_size: 2},
+// CHECK:STDOUT:         {kind: 'ImplTypeAs', text: 'as', subtree_size: 2},
 // CHECK:STDOUT:         {kind: 'IdentifierNameExpr', text: 'ExplicitSelf'},
 // CHECK:STDOUT:       {kind: 'ImplDecl', text: ';', subtree_size: 5},
 // CHECK:STDOUT:           {kind: 'ImplIntroducer', text: 'impl'},
 // CHECK:STDOUT:           {kind: 'ExtendModifier', text: 'extend'},
-// CHECK:STDOUT:           {kind: 'DefaultSelfImplAs', text: 'as'},
+// CHECK:STDOUT:           {kind: 'ImplDefaultSelfAs', text: 'as'},
 // CHECK:STDOUT:           {kind: 'IdentifierNameExpr', text: 'ExtendImpl'},
 // CHECK:STDOUT:         {kind: 'ImplDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:           {kind: 'FunctionIntroducer', text: 'fn'},
@@ -54,7 +54,7 @@ class C {
 // CHECK:STDOUT:         {kind: 'ImplIntroducer', text: 'impl'},
 // CHECK:STDOUT:         {kind: 'ExtendModifier', text: 'extend'},
 // CHECK:STDOUT:           {kind: 'IdentifierNameExpr', text: 'C'},
-// CHECK:STDOUT:         {kind: 'TypeImplAs', text: 'as', subtree_size: 2},
+// CHECK:STDOUT:         {kind: 'ImplTypeAs', text: 'as', subtree_size: 2},
 // CHECK:STDOUT:         {kind: 'IdentifierNameExpr', text: 'InvalidButDiagnosedInCheck'},
 // CHECK:STDOUT:       {kind: 'ImplDecl', text: ';', subtree_size: 6},
 // CHECK:STDOUT:     {kind: 'ClassDefinition', text: '}', subtree_size: 36},

+ 1 - 1
toolchain/parse/testdata/generics/impl/declaration.carbon

@@ -15,7 +15,7 @@ impl bool as Interface;
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:       {kind: 'ImplIntroducer', text: 'impl'},
 // CHECK:STDOUT:         {kind: 'BoolTypeLiteral', text: 'bool'},
-// CHECK:STDOUT:       {kind: 'TypeImplAs', text: 'as', subtree_size: 2},
+// CHECK:STDOUT:       {kind: 'ImplTypeAs', text: 'as', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'IdentifierNameExpr', text: 'Interface'},
 // CHECK:STDOUT:     {kind: 'ImplDecl', text: ';', subtree_size: 5},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},

+ 1 - 1
toolchain/parse/testdata/generics/impl/empty_body.carbon

@@ -16,7 +16,7 @@ impl str as Interface {
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:         {kind: 'ImplIntroducer', text: 'impl'},
 // CHECK:STDOUT:           {kind: 'StringTypeLiteral', text: 'str'},
-// CHECK:STDOUT:         {kind: 'TypeImplAs', text: 'as', subtree_size: 2},
+// CHECK:STDOUT:         {kind: 'ImplTypeAs', text: 'as', subtree_size: 2},
 // CHECK:STDOUT:         {kind: 'IdentifierNameExpr', text: 'Interface'},
 // CHECK:STDOUT:       {kind: 'ImplDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:     {kind: 'ImplDefinition', text: '}', subtree_size: 6},

+ 4 - 4
toolchain/parse/testdata/generics/impl/fail_impl.carbon

@@ -108,12 +108,12 @@ impl
 // CHECK:STDOUT:     {kind: 'ImplDecl', text: ';', has_error: yes, subtree_size: 3},
 // CHECK:STDOUT:       {kind: 'ImplIntroducer', text: 'impl'},
 // CHECK:STDOUT:         {kind: 'IntTypeLiteral', text: 'i32'},
-// CHECK:STDOUT:       {kind: 'TypeImplAs', text: 'as', subtree_size: 2},
+// CHECK:STDOUT:       {kind: 'ImplTypeAs', text: 'as', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'InvalidParse', text: ';', has_error: yes},
 // CHECK:STDOUT:     {kind: 'ImplDecl', text: ';', has_error: yes, subtree_size: 5},
 // CHECK:STDOUT:       {kind: 'ImplIntroducer', text: 'impl'},
 // CHECK:STDOUT:         {kind: 'BoolTypeLiteral', text: 'bool'},
-// CHECK:STDOUT:       {kind: 'TypeImplAs', text: 'as', subtree_size: 2},
+// CHECK:STDOUT:       {kind: 'ImplTypeAs', text: 'as', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'IdentifierNameExpr', text: 'bar'},
 // CHECK:STDOUT:     {kind: 'ImplDecl', text: ';', has_error: yes, subtree_size: 5},
 // CHECK:STDOUT:       {kind: 'ImplIntroducer', text: 'impl'},
@@ -146,7 +146,7 @@ impl
 // CHECK:STDOUT:       {kind: 'Forall', text: 'forall'},
 // CHECK:STDOUT:       {kind: 'InvalidParse', text: 'f16', has_error: yes},
 // CHECK:STDOUT:         {kind: 'FloatTypeLiteral', text: 'f16'},
-// CHECK:STDOUT:       {kind: 'TypeImplAs', text: 'as', subtree_size: 2},
+// CHECK:STDOUT:       {kind: 'ImplTypeAs', text: 'as', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'IdentifierNameExpr', text: 'Quux'},
 // CHECK:STDOUT:     {kind: 'ImplDecl', text: ';', has_error: yes, subtree_size: 7},
 // CHECK:STDOUT:       {kind: 'ImplIntroducer', text: 'impl'},
@@ -178,7 +178,7 @@ impl
 // CHECK:STDOUT:         {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 4},
 // CHECK:STDOUT:       {kind: 'ImplicitParamList', text: ']', subtree_size: 6},
 // CHECK:STDOUT:         {kind: 'IdentifierNameExpr', text: 'T'},
-// CHECK:STDOUT:       {kind: 'TypeImplAs', text: 'as', subtree_size: 2},
+// CHECK:STDOUT:       {kind: 'ImplTypeAs', text: 'as', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'IdentifierNameExpr', text: 'Interface'},
 // CHECK:STDOUT:     {kind: 'ImplDecl', text: ';', has_error: yes, subtree_size: 12},
 // CHECK:STDOUT:       {kind: 'ImplIntroducer', text: 'impl'},

+ 2 - 2
toolchain/parse/testdata/generics/impl/fail_out_of_line_member.carbon

@@ -50,7 +50,7 @@ fn C.(Self as Interface).F() {}
 // CHECK:STDOUT:     {kind: 'InterfaceDefinition', text: '}', subtree_size: 9},
 // CHECK:STDOUT:         {kind: 'ImplIntroducer', text: 'impl'},
 // CHECK:STDOUT:           {kind: 'BoolTypeLiteral', text: 'bool'},
-// CHECK:STDOUT:         {kind: 'TypeImplAs', text: 'as', subtree_size: 2},
+// CHECK:STDOUT:         {kind: 'ImplTypeAs', text: 'as', subtree_size: 2},
 // CHECK:STDOUT:         {kind: 'IdentifierNameExpr', text: 'Interface'},
 // CHECK:STDOUT:       {kind: 'ImplDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
@@ -67,7 +67,7 @@ fn C.(Self as Interface).F() {}
 // CHECK:STDOUT:       {kind: 'ClassDefinitionStart', text: '{', subtree_size: 3},
 // CHECK:STDOUT:           {kind: 'ImplIntroducer', text: 'impl'},
 // CHECK:STDOUT:             {kind: 'SelfTypeNameExpr', text: 'Self'},
-// CHECK:STDOUT:           {kind: 'TypeImplAs', text: 'as', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ImplTypeAs', text: 'as', subtree_size: 2},
 // CHECK:STDOUT:           {kind: 'IdentifierNameExpr', text: 'Interface'},
 // CHECK:STDOUT:         {kind: 'ImplDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:           {kind: 'FunctionIntroducer', text: 'fn'},

+ 2 - 2
toolchain/parse/testdata/generics/impl/forall.carbon

@@ -25,7 +25,7 @@ impl forall [T:! type, U:! Interface] U as Interface(T) {
 // CHECK:STDOUT:         {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 4},
 // CHECK:STDOUT:       {kind: 'ImplicitParamList', text: ']', subtree_size: 6},
 // CHECK:STDOUT:         {kind: 'IdentifierNameExpr', text: 'T'},
-// CHECK:STDOUT:       {kind: 'TypeImplAs', text: 'as', subtree_size: 2},
+// CHECK:STDOUT:       {kind: 'ImplTypeAs', text: 'as', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'IdentifierNameExpr', text: 'Interface'},
 // CHECK:STDOUT:     {kind: 'ImplDecl', text: ';', subtree_size: 12},
 // CHECK:STDOUT:         {kind: 'ImplIntroducer', text: 'impl'},
@@ -42,7 +42,7 @@ impl forall [T:! type, U:! Interface] U as Interface(T) {
 // CHECK:STDOUT:           {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 4},
 // CHECK:STDOUT:         {kind: 'ImplicitParamList', text: ']', subtree_size: 11},
 // CHECK:STDOUT:           {kind: 'IdentifierNameExpr', text: 'U'},
-// CHECK:STDOUT:         {kind: 'TypeImplAs', text: 'as', subtree_size: 2},
+// CHECK:STDOUT:         {kind: 'ImplTypeAs', text: 'as', subtree_size: 2},
 // CHECK:STDOUT:             {kind: 'IdentifierNameExpr', text: 'Interface'},
 // CHECK:STDOUT:           {kind: 'CallExprStart', text: '(', subtree_size: 2},
 // CHECK:STDOUT:           {kind: 'IdentifierNameExpr', text: 'T'},

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

@@ -0,0 +1,240 @@
+// 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
+//
+// AUTOUPDATE
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/parse/testdata/generics/interface/require.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/generics/interface/require.carbon
+
+// --- implicit_self_impls.carbon
+
+interface Y {}
+
+interface Z {
+  require impls Y;
+}
+
+// --- explicit_self_impls.carbon
+
+interface Y {}
+
+interface Z {
+  require Self impls Y;
+}
+
+// --- impls_where.carbon
+
+interface Y { let Y1:! type; }
+
+interface Z {
+  require impls Y where .Y1 = ();
+}
+
+// --- other_impls.carbon
+
+interface Y {}
+
+interface Z(T:! type) {
+  require T impls Y;
+}
+
+// --- extend.carbon
+
+interface Y {}
+
+interface Z {
+  extend require impls Y;
+}
+
+// --- fail_missing_impls.carbon
+
+interface Y {}
+
+interface Z {
+  // CHECK:STDERR: fail_missing_impls.carbon:[[@LINE+4]]:12: error: expected `impls` in `require` declaration [RequireExpectedImpls]
+  // CHECK:STDERR:   require Y;
+  // CHECK:STDERR:            ^
+  // CHECK:STDERR:
+  require Y;
+}
+
+// --- fail_wrong_keyword.carbon
+
+interface Y {}
+
+interface Z {
+  // CHECK:STDERR: fail_wrong_keyword.carbon:[[@LINE+4]]:11: error: expected expression [ExpectedExpr]
+  // CHECK:STDERR:   require as Y;
+  // CHECK:STDERR:           ^~
+  // CHECK:STDERR:
+  require as Y;
+}
+
+// --- fail_as_in_self.carbon
+
+interface Y {}
+
+interface Z {
+  // CHECK:STDERR: fail_as_in_self.carbon:[[@LINE+4]]:16: error: expected `impls` in `require` declaration [RequireExpectedImpls]
+  // CHECK:STDERR:   require Self as Y impls Y;
+  // CHECK:STDERR:                ^~
+  // CHECK:STDERR:
+  require Self as Y impls Y;
+}
+
+// CHECK:STDOUT: - filename: implicit_self_impls.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: 'RequireDecl', text: ';', subtree_size: 4},
+// CHECK:STDOUT:     {kind: 'InterfaceDefinition', text: '}', subtree_size: 8},
+// CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
+// CHECK:STDOUT:   ]
+// CHECK:STDOUT: - filename: explicit_self_impls.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: 'SelfTypeNameExpr', text: 'Self'},
+// CHECK:STDOUT:         {kind: 'RequireTypeImpls', text: 'impls', subtree_size: 2},
+// CHECK:STDOUT:         {kind: 'IdentifierNameExpr', text: 'Y'},
+// CHECK:STDOUT:       {kind: 'RequireDecl', text: ';', subtree_size: 5},
+// CHECK:STDOUT:     {kind: 'InterfaceDefinition', text: '}', subtree_size: 9},
+// CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
+// CHECK:STDOUT:   ]
+// CHECK:STDOUT: - filename: impls_where.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: 'AssociatedConstantIntroducer', text: 'let'},
+// CHECK:STDOUT:           {kind: 'IdentifierNameNotBeforeParams', text: 'Y1'},
+// CHECK:STDOUT:           {kind: 'TypeTypeLiteral', text: 'type'},
+// CHECK:STDOUT:         {kind: 'AssociatedConstantNameAndType', text: ':!', subtree_size: 3},
+// CHECK:STDOUT:       {kind: 'AssociatedConstantDecl', text: ';', subtree_size: 5},
+// CHECK:STDOUT:     {kind: 'InterfaceDefinition', text: '}', subtree_size: 9},
+// 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: 'WhereOperand', text: 'where', subtree_size: 2},
+// CHECK:STDOUT:               {kind: 'IdentifierNameNotBeforeParams', text: 'Y1'},
+// CHECK:STDOUT:             {kind: 'DesignatorExpr', text: '.', subtree_size: 2},
+// CHECK:STDOUT:               {kind: 'TupleLiteralStart', text: '('},
+// CHECK:STDOUT:             {kind: 'TupleLiteral', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'RequirementEqual', text: '=', subtree_size: 5},
+// CHECK:STDOUT:         {kind: 'WhereExpr', text: 'where', subtree_size: 8},
+// CHECK:STDOUT:       {kind: 'RequireDecl', text: ';', subtree_size: 11},
+// CHECK:STDOUT:     {kind: 'InterfaceDefinition', text: '}', subtree_size: 15},
+// CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
+// CHECK:STDOUT:   ]
+// CHECK:STDOUT: - filename: other_impls.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: 'IdentifierNameBeforeParams', text: 'Z'},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:               {kind: 'IdentifierNameNotBeforeParams', text: 'T'},
+// CHECK:STDOUT:             {kind: 'CompileTimeBindingPatternStart', text: ':!', subtree_size: 2},
+// CHECK:STDOUT:             {kind: 'TypeTypeLiteral', text: 'type'},
+// CHECK:STDOUT:           {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 4},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 6},
+// CHECK:STDOUT:       {kind: 'InterfaceDefinitionStart', text: '{', subtree_size: 9},
+// CHECK:STDOUT:         {kind: 'RequireIntroducer', text: 'require'},
+// CHECK:STDOUT:           {kind: 'IdentifierNameExpr', text: 'T'},
+// CHECK:STDOUT:         {kind: 'RequireTypeImpls', text: 'impls', subtree_size: 2},
+// CHECK:STDOUT:         {kind: 'IdentifierNameExpr', text: 'Y'},
+// CHECK:STDOUT:       {kind: 'RequireDecl', text: ';', subtree_size: 5},
+// CHECK:STDOUT:     {kind: 'InterfaceDefinition', text: '}', subtree_size: 15},
+// CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
+// CHECK:STDOUT:   ]
+// CHECK:STDOUT: - filename: extend.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: 'ExtendModifier', text: 'extend'},
+// CHECK:STDOUT:         {kind: 'RequireDefaultSelfImpls', text: 'impls'},
+// CHECK:STDOUT:         {kind: 'IdentifierNameExpr', text: 'Y'},
+// CHECK:STDOUT:       {kind: 'RequireDecl', text: ';', subtree_size: 5},
+// CHECK:STDOUT:     {kind: 'InterfaceDefinition', text: '}', subtree_size: 9},
+// CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
+// CHECK:STDOUT:   ]
+// CHECK:STDOUT: - filename: fail_missing_impls.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: 'IdentifierNameExpr', text: 'Y'},
+// CHECK:STDOUT:       {kind: 'RequireDecl', text: ';', has_error: yes, subtree_size: 3},
+// CHECK:STDOUT:     {kind: 'InterfaceDefinition', text: '}', subtree_size: 7},
+// CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
+// CHECK:STDOUT:   ]
+// CHECK:STDOUT: - filename: fail_wrong_keyword.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: 'InvalidParse', text: 'as', has_error: yes},
+// CHECK:STDOUT:       {kind: 'RequireDecl', text: ';', has_error: yes, subtree_size: 3},
+// CHECK:STDOUT:     {kind: 'InterfaceDefinition', text: '}', subtree_size: 7},
+// CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
+// CHECK:STDOUT:   ]
+// CHECK:STDOUT: - filename: fail_as_in_self.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: 'SelfTypeNameExpr', text: 'Self'},
+// CHECK:STDOUT:       {kind: 'RequireDecl', text: ';', has_error: yes, subtree_size: 3},
+// CHECK:STDOUT:     {kind: 'InterfaceDefinition', text: '}', subtree_size: 7},
+// CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
+// CHECK:STDOUT:   ]

+ 2 - 2
toolchain/parse/testdata/packages/package/impl.carbon

@@ -42,7 +42,7 @@ impl package.Name as package.Interface {}
 // CHECK:STDOUT:     {kind: 'PackageDecl', text: ';', subtree_size: 4},
 // CHECK:STDOUT:         {kind: 'ImplIntroducer', text: 'impl'},
 // CHECK:STDOUT:           {kind: 'IntTypeLiteral', text: 'i32'},
-// CHECK:STDOUT:         {kind: 'TypeImplAs', text: 'as', subtree_size: 2},
+// CHECK:STDOUT:         {kind: 'ImplTypeAs', text: 'as', subtree_size: 2},
 // CHECK:STDOUT:         {kind: 'IdentifierNameExpr', text: 'Interface'},
 // CHECK:STDOUT:       {kind: 'ImplDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:     {kind: 'ImplDefinition', text: '}', subtree_size: 6},
@@ -59,7 +59,7 @@ impl package.Name as package.Interface {}
 // CHECK:STDOUT:             {kind: 'PackageExpr', text: 'package'},
 // CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: 'Name'},
 // CHECK:STDOUT:           {kind: 'MemberAccessExpr', text: '.', subtree_size: 3},
-// CHECK:STDOUT:         {kind: 'TypeImplAs', text: 'as', subtree_size: 4},
+// CHECK:STDOUT:         {kind: 'ImplTypeAs', text: 'as', subtree_size: 4},
 // CHECK:STDOUT:           {kind: 'PackageExpr', text: 'package'},
 // CHECK:STDOUT:           {kind: 'IdentifierNameNotBeforeParams', text: 'Interface'},
 // CHECK:STDOUT:         {kind: 'MemberAccessExpr', text: '.', subtree_size: 3},

+ 5 - 5
toolchain/parse/testdata/where_expr/impl_where.carbon

@@ -38,7 +38,7 @@ impl f64 as Interface where .T = (type where .Self impls type) {
 // CHECK:STDOUT:     {kind: 'InterfaceDefinition', text: '}', subtree_size: 9},
 // CHECK:STDOUT:         {kind: 'ImplIntroducer', text: 'impl'},
 // CHECK:STDOUT:           {kind: 'IntTypeLiteral', text: 'i32'},
-// CHECK:STDOUT:         {kind: 'TypeImplAs', text: 'as', subtree_size: 2},
+// CHECK:STDOUT:         {kind: 'ImplTypeAs', text: 'as', subtree_size: 2},
 // CHECK:STDOUT:             {kind: 'IdentifierNameExpr', text: 'Interface'},
 // CHECK:STDOUT:           {kind: 'WhereOperand', text: 'where', subtree_size: 2},
 // CHECK:STDOUT:               {kind: 'IdentifierNameNotBeforeParams', text: 'T'},
@@ -51,7 +51,7 @@ impl f64 as Interface where .T = (type where .Self impls type) {
 // CHECK:STDOUT:     {kind: 'ImplDefinition', text: '}', subtree_size: 13},
 // CHECK:STDOUT:       {kind: 'ImplIntroducer', text: 'impl'},
 // CHECK:STDOUT:         {kind: 'BoolTypeLiteral', text: 'bool'},
-// CHECK:STDOUT:       {kind: 'TypeImplAs', text: 'as', subtree_size: 2},
+// CHECK:STDOUT:       {kind: 'ImplTypeAs', text: 'as', subtree_size: 2},
 // CHECK:STDOUT:           {kind: 'IdentifierNameExpr', text: 'Interface'},
 // CHECK:STDOUT:         {kind: 'WhereOperand', text: 'where', subtree_size: 2},
 // CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: 'T'},
@@ -63,7 +63,7 @@ impl f64 as Interface where .T = (type where .Self impls type) {
 // CHECK:STDOUT:         {kind: 'ImplIntroducer', text: 'impl'},
 // CHECK:STDOUT:             {kind: 'TupleLiteralStart', text: '('},
 // CHECK:STDOUT:           {kind: 'TupleLiteral', text: ')', subtree_size: 2},
-// CHECK:STDOUT:         {kind: 'TypeImplAs', text: 'as', subtree_size: 3},
+// CHECK:STDOUT:         {kind: 'ImplTypeAs', text: 'as', subtree_size: 3},
 // CHECK:STDOUT:           {kind: 'ParenExprStart', text: '('},
 // CHECK:STDOUT:               {kind: 'IdentifierNameExpr', text: 'Interface'},
 // CHECK:STDOUT:             {kind: 'WhereOperand', text: 'where', subtree_size: 2},
@@ -79,7 +79,7 @@ impl f64 as Interface where .T = (type where .Self impls type) {
 // CHECK:STDOUT:       {kind: 'ImplIntroducer', text: 'impl'},
 // CHECK:STDOUT:           {kind: 'StructLiteralStart', text: '{'},
 // CHECK:STDOUT:         {kind: 'StructLiteral', text: '}', subtree_size: 2},
-// CHECK:STDOUT:       {kind: 'TypeImplAs', text: 'as', subtree_size: 3},
+// CHECK:STDOUT:       {kind: 'ImplTypeAs', text: 'as', subtree_size: 3},
 // CHECK:STDOUT:             {kind: 'ParenExprStart', text: '('},
 // CHECK:STDOUT:                 {kind: 'IdentifierNameExpr', text: 'Interface'},
 // CHECK:STDOUT:               {kind: 'WhereOperand', text: 'where', subtree_size: 2},
@@ -100,7 +100,7 @@ impl f64 as Interface where .T = (type where .Self impls type) {
 // CHECK:STDOUT:     {kind: 'ImplDecl', text: ';', subtree_size: 22},
 // CHECK:STDOUT:         {kind: 'ImplIntroducer', text: 'impl'},
 // CHECK:STDOUT:           {kind: 'FloatTypeLiteral', text: 'f64'},
-// CHECK:STDOUT:         {kind: 'TypeImplAs', text: 'as', subtree_size: 2},
+// CHECK:STDOUT:         {kind: 'ImplTypeAs', text: 'as', subtree_size: 2},
 // CHECK:STDOUT:             {kind: 'IdentifierNameExpr', text: 'Interface'},
 // CHECK:STDOUT:           {kind: 'WhereOperand', text: 'where', subtree_size: 2},
 // CHECK:STDOUT:               {kind: 'IdentifierNameNotBeforeParams', text: 'T'},

+ 37 - 3
toolchain/parse/typed_nodes.h

@@ -1446,6 +1446,40 @@ struct InterfaceDefinition {
   Lex::CloseCurlyBraceTokenIndex token;
 };
 
+// `require`...`impls` statements
+// ------------------------------
+
+// `require`
+using RequireIntroducer =
+    LeafNode<NodeKind::RequireIntroducer, Lex::RequireTokenIndex>;
+
+// `impls` with no type before it
+using RequireDefaultSelfImpls =
+    LeafNode<NodeKind::RequireDefaultSelfImpls, Lex::ImplsTokenIndex,
+             NodeCategory::RequireImpls>;
+
+// `<type> impls`.
+struct RequireTypeImpls {
+  static constexpr auto Kind = NodeKind::RequireTypeImpls.Define(
+      {.category = NodeCategory::RequireImpls, .child_count = 1});
+
+  AnyExprId type_expr;
+  Lex::ImplsTokenIndex token;
+};
+
+// `require T impls I where...`
+struct RequireDecl {
+  static constexpr auto Kind =
+      NodeKind::RequireDecl.Define({.category = NodeCategory::Decl,
+                                    .bracketed_by = RequireIntroducer::Kind});
+
+  RequireIntroducerId introducer;
+  llvm::SmallVector<AnyModifierId> modifiers;
+  AnyRequireImplsId impls;
+  AnyExprId facet_type;
+  Lex::SemiTokenIndex token;
+};
+
 // `impl`...`as` declarations and definitions
 // ------------------------------------------
 
@@ -1462,12 +1496,12 @@ struct ImplForall {
 };
 
 // `as` with no type before it
-using DefaultSelfImplAs = LeafNode<NodeKind::DefaultSelfImplAs,
+using ImplDefaultSelfAs = LeafNode<NodeKind::ImplDefaultSelfAs,
                                    Lex::AsTokenIndex, NodeCategory::ImplAs>;
 
 // `<type> as`
-struct TypeImplAs {
-  static constexpr auto Kind = NodeKind::TypeImplAs.Define(
+struct ImplTypeAs {
+  static constexpr auto Kind = NodeKind::ImplTypeAs.Define(
       {.category = NodeCategory::ImplAs, .child_count = 1});
 
   AnyExprId type_expr;