From d11ee4b2b15abc76370fad318dcdaadc3e941a72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96zg=C3=BCr?= Date: Thu, 26 Feb 2026 22:21:19 +0300 Subject: [PATCH] Implement parsing observe declarations (#6674) This implements parsing of the [`observe`](https://docs.carbon-lang.dev/docs/design/generics/details.html#observing-a-type-implements-an-interface) declarations. - Added states and node kinds. - Added node categories. - Added a diagnostic for invalid keywords/operators. - Implemented parser state handlers. - Added structs to `typed_nodes.h`. - Added parser tests. --- toolchain/check/handle_observe.cpp | 29 +++++ toolchain/check/node_stack.h | 3 + toolchain/diagnostics/kind.def | 1 + toolchain/parse/BUILD | 1 + toolchain/parse/handle_decl_scope_loop.cpp | 3 + toolchain/parse/handle_observe.cpp | 72 ++++++++++++ toolchain/parse/handle_statement.cpp | 2 + toolchain/parse/node_category.h | 32 +++--- toolchain/parse/node_ids.h | 4 + toolchain/parse/node_kind.def | 5 + toolchain/parse/precedence.h | 2 +- toolchain/parse/state.def | 41 +++++++ .../observe/fail_invalid_operator.carbon | 24 ++++ .../testdata/observe/fail_missing_expr.carbon | 62 +++++++++++ .../parse/testdata/observe/observe.carbon | 103 ++++++++++++++++++ toolchain/parse/typed_nodes.h | 39 +++++++ 16 files changed, 407 insertions(+), 16 deletions(-) create mode 100644 toolchain/check/handle_observe.cpp create mode 100644 toolchain/parse/handle_observe.cpp create mode 100644 toolchain/parse/testdata/observe/fail_invalid_operator.carbon create mode 100644 toolchain/parse/testdata/observe/fail_missing_expr.carbon create mode 100644 toolchain/parse/testdata/observe/observe.carbon diff --git a/toolchain/check/handle_observe.cpp b/toolchain/check/handle_observe.cpp new file mode 100644 index 000000000000..c8e82b2c1164 --- /dev/null +++ b/toolchain/check/handle_observe.cpp @@ -0,0 +1,29 @@ +// 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::ObserveIntroducerId node_id) + -> bool { + return context.TODO(node_id, "ObserveIntroducerId"); +} + +auto HandleParseNode(Context& context, Parse::ObserveEqualEqualId node_id) + -> bool { + return context.TODO(node_id, "ObserveEqualEqualId"); +} + +auto HandleParseNode(Context& context, Parse::ObserveImplsId node_id) -> bool { + return context.TODO(node_id, "ObserveImplsId"); +} + +auto HandleParseNode(Context& context, Parse::ObserveDeclId node_id) -> bool { + return context.TODO(node_id, "ObserveDeclId"); +} + +} // namespace Carbon::Check diff --git a/toolchain/check/node_stack.h b/toolchain/check/node_stack.h index 1abba5c678de..92ff972e535c 100644 --- a/toolchain/check/node_stack.h +++ b/toolchain/check/node_stack.h @@ -467,6 +467,7 @@ class NodeStack { case Parse::NodeKind::LetInitializer: case Parse::NodeKind::LetIntroducer: case Parse::NodeKind::NamedConstraintIntroducer: + case Parse::NodeKind::ObserveIntroducer: case Parse::NodeKind::RefBindingName: case Parse::NodeKind::ReturnStatementStart: case Parse::NodeKind::StructLiteralStart: @@ -527,6 +528,8 @@ class NodeStack { case Parse::NodeKind::MatchIntroducer: case Parse::NodeKind::MatchStatementStart: case Parse::NodeKind::NamespaceStart: + case Parse::NodeKind::ObserveEqualEqual: + case Parse::NodeKind::ObserveImpls: case Parse::NodeKind::PackageIntroducer: case Parse::NodeKind::ParenExprStart: case Parse::NodeKind::PatternListComma: diff --git a/toolchain/diagnostics/kind.def b/toolchain/diagnostics/kind.def index 4d9774470582..8b115fc4b77c 100644 --- a/toolchain/diagnostics/kind.def +++ b/toolchain/diagnostics/kind.def @@ -115,6 +115,7 @@ CARBON_DIAGNOSTIC_KIND(ExpectedStructLiteralField) CARBON_DIAGNOSTIC_KIND(ExpectedVarAfterReturned) CARBON_DIAGNOSTIC_KIND(ExpectedChoiceDefinition) CARBON_DIAGNOSTIC_KIND(ExpectedChoiceAlternativeName) +CARBON_DIAGNOSTIC_KIND(ExpectedObserveOperator) CARBON_DIAGNOSTIC_KIND(NestedVar) CARBON_DIAGNOSTIC_KIND(NestedUnused) CARBON_DIAGNOSTIC_KIND(OperatorRequiresParentheses) diff --git a/toolchain/parse/BUILD b/toolchain/parse/BUILD index 81508f9e4a57..b80189e64d98 100644 --- a/toolchain/parse/BUILD +++ b/toolchain/parse/BUILD @@ -103,6 +103,7 @@ cc_library( ":context", ":dump", ":node_kind", + ":precedence", ":state", ":tree", "//common:check", diff --git a/toolchain/parse/handle_decl_scope_loop.cpp b/toolchain/parse/handle_decl_scope_loop.cpp index dab82e216b4c..168ddcfb6efb 100644 --- a/toolchain/parse/handle_decl_scope_loop.cpp +++ b/toolchain/parse/handle_decl_scope_loop.cpp @@ -8,6 +8,7 @@ #include "toolchain/parse/context.h" #include "toolchain/parse/handle.h" #include "toolchain/parse/node_kind.h" +#include "toolchain/parse/state.h" namespace Carbon::Parse { @@ -130,6 +131,8 @@ static constexpr auto DeclIntroducers = [] { StateKind::TypeAfterIntroducerAsInterface); set(Lex::TokenKind::Namespace, NodeKind::NamespaceStart, StateKind::Namespace); + set(Lex::TokenKind::Observe, NodeKind::ObserveIntroducer, + StateKind::ObserveAfterIntroducer); set(Lex::TokenKind::Require, NodeKind::RequireIntroducer, StateKind::RequireAfterIntroducer); set_contextual(Lex::TokenKind::Let, RegularContext, NodeKind::LetIntroducer, diff --git a/toolchain/parse/handle_observe.cpp b/toolchain/parse/handle_observe.cpp new file mode 100644 index 000000000000..e456fcc417d4 --- /dev/null +++ b/toolchain/parse/handle_observe.cpp @@ -0,0 +1,72 @@ +// 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/lex/token_kind.h" +#include "toolchain/parse/context.h" +#include "toolchain/parse/handle.h" +#include "toolchain/parse/node_kind.h" +#include "toolchain/parse/precedence.h" +#include "toolchain/parse/state.h" +#include "toolchain/parse/typed_nodes.h" + +namespace Carbon::Parse { +auto HandleObserveAfterIntroducer(Context& context) -> void { + auto state = context.PopState(); + + context.PushState(state, StateKind::ObserveOperator); + context.PushStateForExpr(PrecedenceGroup::ForRequirements()); +} + +auto HandleObserveOperator(Context& context) -> void { + auto state = context.PopState(); + + switch (context.PositionKind()) { + case Lex::TokenKind::EqualEqual: { + state.token = context.Consume(); + context.PushState(state, StateKind::ObserveFinishOperator); + context.PushStateForExpr(PrecedenceGroup::ForRequirements()); + return; + } + case Lex::TokenKind::Impls: { + state.token = context.Consume(); + context.PushState(state, StateKind::ObserveFinishOperator); + context.PushState(StateKind::Expr); + return; + } + default: { + if (!state.has_error) { + CARBON_DIAGNOSTIC(ExpectedObserveOperator, Error, + "observe should use `==` or `impls` operator"); + context.emitter().Emit(*context.position(), ExpectedObserveOperator); + } + context.RecoverFromDeclError(state, NodeKind::ObserveDecl, + /*skip_past_likely_end=*/true); + return; + } + } +} + +auto HandleObserveFinishOperator(Context& context) -> void { + auto state = context.PopState(); + auto token_kind = context.tokens().GetKind(state.token); + + if (token_kind == Lex::TokenKind::EqualEqual) { + context.AddNode(NodeKind::ObserveEqualEqual, state.token, state.has_error); + } else { + context.AddNode(NodeKind::ObserveImpls, state.token, state.has_error); + } + if (context.PositionIs(Lex::TokenKind::Semi)) { + context.PushState(state, StateKind::ObserveDecl); + } else { + context.PushState(state, StateKind::ObserveOperator); + } +} + +auto HandleObserveDecl(Context& context) -> void { + auto state = context.PopState(); + context.AddNodeExpectingDeclSemi(state, NodeKind::ObserveDecl, + Lex::TokenKind::Observe, + /*is_def_allowed=*/false); +} +} // namespace Carbon::Parse diff --git a/toolchain/parse/handle_statement.cpp b/toolchain/parse/handle_statement.cpp index 3a1a4d755319..2b4559e45302 100644 --- a/toolchain/parse/handle_statement.cpp +++ b/toolchain/parse/handle_statement.cpp @@ -4,6 +4,7 @@ #include +#include "toolchain/lex/token_kind.h" #include "toolchain/parse/context.h" #include "toolchain/parse/handle.h" @@ -64,6 +65,7 @@ auto HandleStatement(Context& context) -> void { case Lex::TokenKind::Let: case Lex::TokenKind::Library: case Lex::TokenKind::Namespace: + case Lex::TokenKind::Observe: // We intentionally don't handle Package here, because `package.` can be // used at the start of an expression, and it's not worth disambiguating it. case Lex::TokenKind::Var: { diff --git a/toolchain/parse/node_category.h b/toolchain/parse/node_category.h index 0cbc999a75db..10b0feb96c93 100644 --- a/toolchain/parse/node_category.h +++ b/toolchain/parse/node_category.h @@ -15,21 +15,23 @@ 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) \ - /* `impl as` or just `impl as` */ \ - X(ImplAs) \ - X(IntConst) \ - X(MemberExpr) \ - X(MemberName) \ - X(Modifier) \ - X(NonExprName) \ - X(PackageName) \ - X(Pattern) \ - /* `require impls` or just `require impls` */ \ - X(RequireImpls) \ - X(Requirement) \ +#define CARBON_NODE_CATEGORY(X) \ + X(Decl) \ + X(Expr) \ + /* `impl as` or just `impl as` */ \ + X(ImplAs) \ + X(IntConst) \ + X(MemberExpr) \ + X(MemberName) \ + X(Modifier) \ + X(NonExprName) \ + /* `observe == ` or `observe impls` */ \ + X(ObserveOperator) \ + X(PackageName) \ + X(Pattern) \ + /* `require impls` or just `require impls` */ \ + X(RequireImpls) \ + X(Requirement) \ X(Statement) // We expect this to grow, so are using a bigger size than needed. diff --git a/toolchain/parse/node_ids.h b/toolchain/parse/node_ids.h index 0d85827daf94..07a567c58863 100644 --- a/toolchain/parse/node_ids.h +++ b/toolchain/parse/node_ids.h @@ -7,6 +7,7 @@ #include "toolchain/base/index_base.h" #include "toolchain/lex/token_index.h" +#include "toolchain/parse/node_category.h" #include "toolchain/parse/node_kind.h" namespace Carbon::Parse { @@ -110,6 +111,9 @@ using AnyStatementId = NodeIdInCategory; using AnyRequireImplsId = NodeIdInCategory; using AnyRequirementId = NodeIdInCategory; +using AnyObserveOperatorId = NodeIdInCategory; +using AnyObserveOperandId = + NodeIdInCategory; using AnyNonExprNameId = NodeIdInCategory; using AnyPackageNameId = NodeIdInCategory; diff --git a/toolchain/parse/node_kind.def b/toolchain/parse/node_kind.def index 68ceb7018e8b..5b185de4492a 100644 --- a/toolchain/parse/node_kind.def +++ b/toolchain/parse/node_kind.def @@ -393,6 +393,11 @@ CARBON_PARSE_NODE_KIND(RequireTypeImpls) CARBON_PARSE_NODE_KIND(RequireDefaultSelfImpls) CARBON_PARSE_NODE_KIND(RequireDecl) +CARBON_PARSE_NODE_KIND(ObserveIntroducer) +CARBON_PARSE_NODE_KIND(ObserveEqualEqual) +CARBON_PARSE_NODE_KIND(ObserveImpls) +CARBON_PARSE_NODE_KIND(ObserveDecl) + CARBON_PARSE_NODE_KIND(NamedConstraintIntroducer) CARBON_PARSE_NODE_KIND(NamedConstraintDefinitionStart) CARBON_PARSE_NODE_KIND(NamedConstraintDefinition) diff --git a/toolchain/parse/precedence.h b/toolchain/parse/precedence.h index 13f9a7eb51d4..e3ffd362af6a 100644 --- a/toolchain/parse/precedence.h +++ b/toolchain/parse/precedence.h @@ -63,7 +63,7 @@ class PrecedenceGroup { static auto ForRequireImpls() -> PrecedenceGroup; // Get the precedence level at which to parse expressions in requirements - // after `where` or `require`. + // after `where`, `require`, or `observe`. static auto ForRequirements() -> PrecedenceGroup; // Look up the operator information of the given prefix operator token, or diff --git a/toolchain/parse/state.def b/toolchain/parse/state.def index 96496dccb182..3ce4f73761e8 100644 --- a/toolchain/parse/state.def +++ b/toolchain/parse/state.def @@ -396,6 +396,10 @@ CARBON_PARSE_STATE(DeclNameAndParamsAfterParams) // ^~~~~~~ // 1. RequireAfterIntroducer // +// observe ... +// ^~~~~~~ +// 1. ObserveAfterIntroducer +// // var ... (variant is Regular) // ^~~ // 1. VarAsRegular @@ -1549,6 +1553,43 @@ CARBON_PARSE_STATE(RequireBeforeImpls) // (state done) CARBON_PARSE_STATE(RequireDecl) +// Handles processing of an `observe` declaration after the introducer. +// +// observe TypeExpression ... +// ^ +// 1. Expr +// 2. ObserveOperator +CARBON_PARSE_STATE(ObserveAfterIntroducer) + +// Handles processing of an operator in an `observe` declaration. +// +// observe TypeExpression == TypeExpression ... +// ^~ +// 1. Expr +// 2. ObserveFinishOperator +// observe TypeExpression impls ... +// ^~~~~ +// 1. Expr +// 2. ObserveFinishOperator +CARBON_PARSE_STATE(ObserveOperator) + +// Finishes an operator in an `observe` declaration. +// +// observe TypeExpression == TypeExpression ... +// ^ +// 1. ObserveOperator +// observe TypeExpression == TypeExpression ; +// ^ +// 1. ObserveDecl +CARBON_PARSE_STATE(ObserveFinishOperator) + +// Handles processing of a completed `observe` declaration. +// +// observe ... ; +// ^ +// (state done) +CARBON_PARSE_STATE(ObserveDecl) + // Handles the start of a `var` or `returned var` in a non-class context. // // var ... (variant is Regular) diff --git a/toolchain/parse/testdata/observe/fail_invalid_operator.carbon b/toolchain/parse/testdata/observe/fail_invalid_operator.carbon new file mode 100644 index 000000000000..c9e61e974384 --- /dev/null +++ b/toolchain/parse/testdata/observe/fail_invalid_operator.carbon @@ -0,0 +1,24 @@ +// 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/observe/fail_invalid_operator.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/observe/fail_invalid_operator.carbon + +// CHECK:STDERR: fail_invalid_operator.carbon:[[@LINE+4]]:11: error: observe should use `==` or `impls` operator [ExpectedObserveOperator] +// CHECK:STDERR: observe T = U impls I; +// CHECK:STDERR: ^ +// CHECK:STDERR: +observe T = U impls I; + +// CHECK:STDOUT: - filename: fail_invalid_operator.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'ObserveIntroducer', text: 'observe'}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'T'}, +// CHECK:STDOUT: {kind: 'ObserveDecl', text: ';', has_error: yes, subtree_size: 3}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/observe/fail_missing_expr.carbon b/toolchain/parse/testdata/observe/fail_missing_expr.carbon new file mode 100644 index 000000000000..bd3b1f794962 --- /dev/null +++ b/toolchain/parse/testdata/observe/fail_missing_expr.carbon @@ -0,0 +1,62 @@ +// 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/observe/fail_missing_expr.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/observe/fail_missing_expr.carbon + +// --- fail_missing_expr_after_observe.carbon + +// CHECK:STDERR: fail_missing_expr_after_observe.carbon:[[@LINE+4]]:9: error: expected expression [ExpectedExpr] +// CHECK:STDERR: observe impls I; +// CHECK:STDERR: ^~~~~ +// CHECK:STDERR: +observe impls I; + +// --- fail_missing_expr_after_impls.carbon + +// CHECK:STDERR: fail_missing_expr_after_impls.carbon:[[@LINE+4]]:17: error: expected expression [ExpectedExpr] +// CHECK:STDERR: observe T impls ; +// CHECK:STDERR: ^ +// CHECK:STDERR: +observe T impls ; + +// --- fail_missing_impls.carbon + +// CHECK:STDERR: fail_missing_impls.carbon:[[@LINE+4]]:11: error: observe should use `==` or `impls` operator [ExpectedObserveOperator] +// CHECK:STDERR: observe T ; +// CHECK:STDERR: ^ +// CHECK:STDERR: +observe T ; + +// CHECK:STDOUT: - filename: fail_missing_expr_after_observe.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'ObserveIntroducer', text: 'observe'}, +// CHECK:STDOUT: {kind: 'InvalidParse', text: 'impls', has_error: yes}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'I'}, +// CHECK:STDOUT: {kind: 'ObserveImpls', text: 'impls', has_error: yes, subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ObserveDecl', text: ';', has_error: yes, subtree_size: 5}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: fail_missing_expr_after_impls.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'ObserveIntroducer', text: 'observe'}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'T'}, +// CHECK:STDOUT: {kind: 'InvalidParse', text: ';', has_error: yes}, +// CHECK:STDOUT: {kind: 'ObserveImpls', text: 'impls', has_error: yes, subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ObserveDecl', text: ';', has_error: yes, subtree_size: 5}, +// 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: 'ObserveIntroducer', text: 'observe'}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'T'}, +// CHECK:STDOUT: {kind: 'ObserveDecl', text: ';', has_error: yes, subtree_size: 3}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/observe/observe.carbon b/toolchain/parse/testdata/observe/observe.carbon new file mode 100644 index 000000000000..fd4afcc5ae50 --- /dev/null +++ b/toolchain/parse/testdata/observe/observe.carbon @@ -0,0 +1,103 @@ +// 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/observe/observe.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/observe/observe.carbon + +// --- base.carbon + +observe A == B == C; + +// --- impls.carbon + +observe T impls I; + +// --- equal_equal_impls.carbon + +observe T == U impls I; +observe A == B == C == D impls I; + +// --- statement.carbon + +fn F() { + observe A == B; +} + +interface I { + observe A == B; +} + +// CHECK:STDOUT: - filename: base.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'ObserveIntroducer', text: 'observe'}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'A'}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'B'}, +// CHECK:STDOUT: {kind: 'ObserveEqualEqual', text: '==', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'C'}, +// CHECK:STDOUT: {kind: 'ObserveEqualEqual', text: '==', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'ObserveDecl', text: ';', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: impls.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'ObserveIntroducer', text: 'observe'}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'T'}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'I'}, +// CHECK:STDOUT: {kind: 'ObserveImpls', text: 'impls', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ObserveDecl', text: ';', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: equal_equal_impls.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'ObserveIntroducer', text: 'observe'}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'T'}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'U'}, +// CHECK:STDOUT: {kind: 'ObserveEqualEqual', text: '==', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'I'}, +// CHECK:STDOUT: {kind: 'ObserveImpls', text: 'impls', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'ObserveDecl', text: ';', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'ObserveIntroducer', text: 'observe'}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'A'}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'B'}, +// CHECK:STDOUT: {kind: 'ObserveEqualEqual', text: '==', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'C'}, +// CHECK:STDOUT: {kind: 'ObserveEqualEqual', text: '==', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'D'}, +// CHECK:STDOUT: {kind: 'ObserveEqualEqual', text: '==', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'I'}, +// CHECK:STDOUT: {kind: 'ObserveImpls', text: 'impls', subtree_size: 9}, +// CHECK:STDOUT: {kind: 'ObserveDecl', text: ';', subtree_size: 11}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: statement.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierNameMaybeBeforeSignature', text: 'F'}, +// CHECK:STDOUT: {kind: 'ExplicitParamListStart', text: '('}, +// CHECK:STDOUT: {kind: 'ExplicitParamList', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'ObserveIntroducer', text: 'observe'}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'A'}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'B'}, +// CHECK:STDOUT: {kind: 'ObserveEqualEqual', text: '==', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ObserveDecl', text: ';', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 11}, +// CHECK:STDOUT: {kind: 'InterfaceIntroducer', text: 'interface'}, +// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeSignature', text: 'I'}, +// CHECK:STDOUT: {kind: 'InterfaceDefinitionStart', text: '{', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ObserveIntroducer', text: 'observe'}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'A'}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'B'}, +// CHECK:STDOUT: {kind: 'ObserveEqualEqual', text: '==', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ObserveDecl', text: ';', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'InterfaceDefinition', text: '}', subtree_size: 9}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parse/typed_nodes.h b/toolchain/parse/typed_nodes.h index 294b5339359e..beade4f0a3e8 100644 --- a/toolchain/parse/typed_nodes.h +++ b/toolchain/parse/typed_nodes.h @@ -1589,6 +1589,45 @@ struct RequireDecl { Lex::SemiTokenIndex token; }; +// `observe` declarations +// -------------------------------- + +// `observe` +using ObserveIntroducer = + LeafNode; + +// `==` +struct ObserveEqualEqual { + static constexpr auto Kind = NodeKind::ObserveEqualEqual.Define( + {.category = NodeCategory::ObserveOperator, .child_count = 2}); + + AnyObserveOperandId lhs; + Lex::EqualEqualTokenIndex token; + AnyExprId rhs; +}; + +// `impls` +struct ObserveImpls { + static constexpr auto Kind = NodeKind::ObserveImpls.Define( + {.category = NodeCategory::ObserveOperator, .child_count = 2}); + + AnyObserveOperandId lhs; + Lex::ImplsTokenIndex token; + AnyExprId rhs; +}; + +// `observe T == U impls I` +struct ObserveDecl { + static constexpr auto Kind = + NodeKind::ObserveDecl.Define({.category = NodeCategory::Decl, + .bracketed_by = ObserveIntroducer::Kind}); + + ObserveIntroducerId introducer; + llvm::SmallVector modifiers; + AnyObserveOperatorId op; + Lex::SemiTokenIndex token; +}; + // `impl`...`as` declarations and definitions // ------------------------------------------