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.
This commit is contained in:
Özgür
2026-02-26 19:21:19 +00:00
committed by GitHub
parent 34651f429f
commit d11ee4b2b1
16 changed files with 407 additions and 16 deletions
+29
View File
@@ -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
+3
View File
@@ -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:
+1
View File
@@ -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)
+1
View File
@@ -103,6 +103,7 @@ cc_library(
":context",
":dump",
":node_kind",
":precedence",
":state",
":tree",
"//common:check",
@@ -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,
+72
View File
@@ -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
+2
View File
@@ -4,6 +4,7 @@
#include <optional>
#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: {
+17 -15
View File
@@ -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 <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) \
#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) \
/* `observe <type> == <type>` or `observe <type> impls` */ \
X(ObserveOperator) \
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.
+4
View File
@@ -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<NodeCategory::Statement | NodeCategory::Decl>;
using AnyRequireImplsId = NodeIdInCategory<NodeCategory::RequireImpls>;
using AnyRequirementId = NodeIdInCategory<NodeCategory::Requirement>;
using AnyObserveOperatorId = NodeIdInCategory<NodeCategory::ObserveOperator>;
using AnyObserveOperandId =
NodeIdInCategory<NodeCategory::Expr | NodeCategory::ObserveOperator>;
using AnyNonExprNameId = NodeIdInCategory<NodeCategory::NonExprName>;
using AnyPackageNameId = NodeIdInCategory<NodeCategory::PackageName>;
+5
View File
@@ -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)
+1 -1
View File
@@ -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
+41
View File
@@ -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)
@@ -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: ]
@@ -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: ]
+103
View File
@@ -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: ]
+39
View File
@@ -1589,6 +1589,45 @@ struct RequireDecl {
Lex::SemiTokenIndex token;
};
// `observe` declarations
// --------------------------------
// `observe`
using ObserveIntroducer =
LeafNode<NodeKind::ObserveIntroducer, Lex::ObserveTokenIndex>;
// `==`
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<AnyModifierId> modifiers;
AnyObserveOperatorId op;
Lex::SemiTokenIndex token;
};
// `impl`...`as` declarations and definitions
// ------------------------------------------