diff --git a/toolchain/check/handle_where.cpp b/toolchain/check/handle_where.cpp index 8aa19ba57ff9..a5dc86ad640b 100644 --- a/toolchain/check/handle_where.cpp +++ b/toolchain/check/handle_where.cpp @@ -17,4 +17,27 @@ auto HandleParseNode(Context& context, Parse::DesignatorExprId node_id) return context.TODO(node_id, "HandleDesignatorExpr"); } +auto HandleParseNode(Context& context, Parse::WhereOperandId node_id) -> bool { + return context.TODO(node_id, "HandleWhereOperand"); +} + +auto HandleParseNode(Context& context, Parse::RequirementEqualId node_id) + -> bool { + return context.TODO(node_id, "HandleRequirementEqual"); +} + +auto HandleParseNode(Context& context, Parse::RequirementEqualEqualId node_id) + -> bool { + return context.TODO(node_id, "HandleRequirementEqualEqual"); +} + +auto HandleParseNode(Context& context, Parse::RequirementImplsId node_id) + -> bool { + return context.TODO(node_id, "HandleRequirementImpls"); +} + +auto HandleParseNode(Context& context, Parse::WhereExprId node_id) -> bool { + return context.TODO(node_id, "HandleWhereExpr"); +} + } // namespace Carbon::Check diff --git a/toolchain/check/node_stack.h b/toolchain/check/node_stack.h index 6fccf072fe68..427f75a979dc 100644 --- a/toolchain/check/node_stack.h +++ b/toolchain/check/node_stack.h @@ -621,6 +621,9 @@ class NodeStack { case Parse::NodeKind::PrivateModifier: case Parse::NodeKind::ProtectedModifier: case Parse::NodeKind::RealLiteral: + case Parse::NodeKind::RequirementEqual: + case Parse::NodeKind::RequirementEqualEqual: + case Parse::NodeKind::RequirementImpls: case Parse::NodeKind::ReturnStatement: case Parse::NodeKind::SelfTypeName: case Parse::NodeKind::SelfTypeNameExpr: @@ -641,6 +644,8 @@ class NodeStack { case Parse::NodeKind::UnsignedIntTypeLiteral: case Parse::NodeKind::VariableDecl: case Parse::NodeKind::VirtualModifier: + case Parse::NodeKind::WhereExpr: + case Parse::NodeKind::WhereOperand: case Parse::NodeKind::WhileStatement: return Id::Kind::Invalid; } diff --git a/toolchain/check/testdata/impl/fail_todo_impl_assoc_const.carbon b/toolchain/check/testdata/impl/fail_todo_impl_assoc_const.carbon index ee715c5ce431..0f424e53edb3 100644 --- a/toolchain/check/testdata/impl/fail_todo_impl_assoc_const.carbon +++ b/toolchain/check/testdata/impl/fail_todo_impl_assoc_const.carbon @@ -10,9 +10,9 @@ interface I { let T:! type; } -// CHECK:STDERR: fail_todo_impl_assoc_const.carbon:[[@LINE+3]]:16: ERROR: `impl` declarations must either end with a `;` or have a `{ ... }` block for a definition. +// CHECK:STDERR: fail_todo_impl_assoc_const.carbon:[[@LINE+3]]:14: ERROR: Semantics TODO: `HandleWhereOperand`. // CHECK:STDERR: impl bool as I where .T = bool {} -// CHECK:STDERR: ^~~~~ +// CHECK:STDERR: ^~~~~~~ impl bool as I where .T = bool {} // CHECK:STDOUT: --- fail_todo_impl_assoc_const.carbon @@ -27,35 +27,7 @@ impl bool as I where .T = bool {} // CHECK:STDOUT: %Bool: %Bool.type = struct_value () [template] // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: imports { -// CHECK:STDOUT: %Core: = namespace file.%Core.import, [template] { -// CHECK:STDOUT: .Bool = %import_ref -// CHECK:STDOUT: import Core//prelude -// CHECK:STDOUT: import Core//prelude/operators -// CHECK:STDOUT: import Core//prelude/types -// CHECK:STDOUT: import Core//prelude/operators/arithmetic -// CHECK:STDOUT: import Core//prelude/operators/as -// CHECK:STDOUT: import Core//prelude/operators/bitwise -// CHECK:STDOUT: import Core//prelude/operators/comparison -// CHECK:STDOUT: import Core//prelude/types/bool -// CHECK:STDOUT: } -// CHECK:STDOUT: %import_ref: %Bool.type = import_ref Core//prelude/types/bool, inst+2, loaded [template = constants.%Bool] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [template] { -// CHECK:STDOUT: .Core = imports.%Core -// CHECK:STDOUT: .I = %I.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %Core.import = import Core -// CHECK:STDOUT: %I.decl: type = interface_decl @I [template = constants.%.1] {} -// CHECK:STDOUT: impl_decl @impl { -// CHECK:STDOUT: %bool.make_type: init type = call constants.%Bool() [template = bool] -// CHECK:STDOUT: %.loc16_6.1: type = value_of_initializer %bool.make_type [template = bool] -// CHECK:STDOUT: %.loc16_6.2: type = converted %bool.make_type, %.loc16_6.1 [template = bool] -// CHECK:STDOUT: %I.ref: type = name_ref I, %I.decl [template = constants.%.1] -// CHECK:STDOUT: } -// CHECK:STDOUT: } +// CHECK:STDOUT: file {} // CHECK:STDOUT: // CHECK:STDOUT: interface @I { // CHECK:STDOUT: %Self: %.1 = bind_symbolic_name Self 0 [symbolic = constants.%Self] @@ -68,7 +40,5 @@ impl bool as I where .T = bool {} // CHECK:STDOUT: witness = (%T) // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: impl @impl: bool as %.1; -// CHECK:STDOUT: // CHECK:STDOUT: fn @Bool() -> type = "bool.make_type"; // CHECK:STDOUT: diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index c5f2487bcfe3..8ef113a39243 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/toolchain/diagnostics/diagnostic_kind.def @@ -127,6 +127,9 @@ CARBON_DIAGNOSTIC_KIND(ImplExpectedAs) // Alias diagnostics. CARBON_DIAGNOSTIC_KIND(ExpectedAliasInitializer) +// Where requirement diagnostics. +CARBON_DIAGNOSTIC_KIND(ExpectedRequirementOperator) + // ============================================================================ // Semantics diagnostics // ============================================================================ diff --git a/toolchain/lex/token_kind.def b/toolchain/lex/token_kind.def index 6205cfe8f5ea..78c26a75cba7 100644 --- a/toolchain/lex/token_kind.def +++ b/toolchain/lex/token_kind.def @@ -212,7 +212,8 @@ CARBON_KEYWORD_TOKEN(Type, "type") // Underscore is tokenized as a keyword because it's part of identifiers. CARBON_KEYWORD_TOKEN(Underscore, "_") CARBON_KEYWORD_TOKEN(Virtual, "virtual") -CARBON_KEYWORD_TOKEN(Where, "where") +CARBON_TOKEN_WITH_VIRTUAL_NODE( + CARBON_KEYWORD_TOKEN(Where, "where")) CARBON_KEYWORD_TOKEN(While, "while") // clang-format on diff --git a/toolchain/parse/handle_expr.cpp b/toolchain/parse/handle_expr.cpp index 87ef6cbc003d..7e7068e90377 100644 --- a/toolchain/parse/handle_expr.cpp +++ b/toolchain/parse/handle_expr.cpp @@ -323,6 +323,15 @@ auto HandleExprLoop(Context& context) -> void { state.state = State::ExprLoopForShortCircuitOperatorAsOr; break; + // `where` also needs a virtual parse tree node, and parses its right + // argument in a mode where it can handle requirement operators like + // `impls` and `=`. + case Lex::TokenKind::Where: + context.AddNode(NodeKind::WhereOperand, state.token, state.has_error); + context.PushState(state, State::WhereFinish); + context.PushState(State::RequirementBegin); + return; + default: state.state = State::ExprLoopForInfixOperator; break; diff --git a/toolchain/parse/handle_requirement.cpp b/toolchain/parse/handle_requirement.cpp new file mode 100644 index 000000000000..56428d1f3b6d --- /dev/null +++ b/toolchain/parse/handle_requirement.cpp @@ -0,0 +1,83 @@ +// 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" + +namespace Carbon::Parse { + +auto HandleRequirementBegin(Context& context) -> void { + context.PopAndDiscardState(); + context.PushState(State::RequirementOperator); + context.PushStateForExpr(PrecedenceGroup::ForRequirements()); +} + +auto HandleRequirementOperator(Context& context) -> void { + auto state = context.PopState(); + + switch (context.PositionKind()) { + case Lex::TokenKind::Impls: { + break; + } + case Lex::TokenKind::Equal: { + break; + } + case Lex::TokenKind::EqualEqual: { + break; + } + default: { + if (!state.has_error) { + CARBON_DIAGNOSTIC( + ExpectedRequirementOperator, Error, + "Requirement should use `impls`, `=`, or `==` operator."); + context.emitter().Emit(*context.position(), + ExpectedRequirementOperator); + } + context.ReturnErrorOnState(); + return; + } + } + state.token = context.Consume(); + context.PushState(state, State::RequirementOperatorFinish); + context.PushStateForExpr(PrecedenceGroup::ForRequirements()); +} + +auto HandleRequirementOperatorFinish(Context& context) -> void { + auto state = context.PopState(); + + switch (auto token_kind = context.tokens().GetKind(state.token)) { + case Lex::TokenKind::Impls: { + context.AddNode(NodeKind::RequirementImpls, state.token, state.has_error); + break; + } + case Lex::TokenKind::Equal: { + context.AddNode(NodeKind::RequirementEqual, state.token, state.has_error); + break; + } + case Lex::TokenKind::EqualEqual: { + context.AddNode(NodeKind::RequirementEqualEqual, state.token, + state.has_error); + break; + } + default: + // RequirementOperatorFinish state is only pushed in + // HandleRequirementOperator on one of the three requirement operator + // tokens. + CARBON_FATAL() << "Unexpected token kind for requirement operator: " + << token_kind; + return; + } + // TODO: Handle `and` token. +} + +auto HandleWhereFinish(Context& context) -> void { + auto state = context.PopState(); + if (state.has_error) { + context.ReturnErrorOnState(); + } + context.AddNode(NodeKind::WhereExpr, state.token, state.has_error); +} + +} // namespace Carbon::Parse diff --git a/toolchain/parse/node_ids.h b/toolchain/parse/node_ids.h index 5835a1a61fae..ee7206560ff1 100644 --- a/toolchain/parse/node_ids.h +++ b/toolchain/parse/node_ids.h @@ -77,6 +77,7 @@ using AnyMemberAccessId = using AnyModifierId = NodeIdInCategory; using AnyPatternId = NodeIdInCategory; using AnyStatementId = NodeIdInCategory; +using AnyRequirementId = NodeIdInCategory; // NodeId with kind that matches one of the `T::Kind`s. template diff --git a/toolchain/parse/node_kind.def b/toolchain/parse/node_kind.def index e310312cd470..aa8c25586a1b 100644 --- a/toolchain/parse/node_kind.def +++ b/toolchain/parse/node_kind.def @@ -276,6 +276,11 @@ CARBON_PARSE_NODE_KIND(ShortCircuitOperatorOr) CARBON_PARSE_NODE_KIND(SelfTypeName) CARBON_PARSE_NODE_KIND(DesignatorExpr) +CARBON_PARSE_NODE_KIND(RequirementEqual) +CARBON_PARSE_NODE_KIND(RequirementEqualEqual) +CARBON_PARSE_NODE_KIND(RequirementImpls) +CARBON_PARSE_NODE_KIND(WhereExpr) +CARBON_PARSE_NODE_KIND(WhereOperand) CARBON_PARSE_NODE_KIND(IfExprIf) CARBON_PARSE_NODE_KIND(IfExprThen) diff --git a/toolchain/parse/node_kind.h b/toolchain/parse/node_kind.h index d2d291ec2aea..0630def4344e 100644 --- a/toolchain/parse/node_kind.h +++ b/toolchain/parse/node_kind.h @@ -34,9 +34,10 @@ class NodeCategory : public Printable { Pattern = 1 << 6, Statement = 1 << 7, IntConst = 1 << 8, + Requirement = 1 << 9, None = 0, - LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/IntConst) + LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/Requirement) }; // Support implicit conversion so that the difference with the member enum is diff --git a/toolchain/parse/precedence.cpp b/toolchain/parse/precedence.cpp index 81904eeae85c..ba280b5d42aa 100644 --- a/toolchain/parse/precedence.cpp +++ b/toolchain/parse/precedence.cpp @@ -29,6 +29,8 @@ enum PrecedenceLevel : int8_t { // Type formation. TypePrefix, TypePostfix, + // `where` keyword. + Where, // Casts. As, // Logical. @@ -60,9 +62,9 @@ struct OperatorPriorityTable { MarkHigherThan({Multiplicative}, {Additive}); MarkHigherThan( {Additive, Modulo, BitwiseAnd, BitwiseOr, BitwiseXor, BitShift}, - {Relational}); + {Relational, Where}); MarkHigherThan({Relational, LogicalPrefix}, {LogicalAnd, LogicalOr}); - MarkHigherThan({As, LogicalAnd, LogicalOr}, {If}); + MarkHigherThan({As, LogicalAnd, LogicalOr, Where}, {If}); MarkHigherThan({If}, {Assignment}); MarkHigherThan({Assignment, IncrementDecrement}, {Lowest}); @@ -194,6 +196,10 @@ auto PrecedenceGroup::ForImplAs() -> PrecedenceGroup { return PrecedenceGroup(As); } +auto PrecedenceGroup::ForRequirements() -> PrecedenceGroup { + return PrecedenceGroup(Where); +} + auto PrecedenceGroup::ForLeading(Lex::TokenKind kind) -> std::optional { switch (kind) { @@ -289,6 +295,10 @@ auto PrecedenceGroup::ForTrailing(Lex::TokenKind kind, bool infix) case Lex::TokenKind::As: return Trailing{.level = As, .is_binary = true}; + // Requirement operator. + case Lex::TokenKind::Where: + return Trailing{.level = Where, .is_binary = true}; + // Prefix-only operators. case Lex::TokenKind::Const: case Lex::TokenKind::MinusMinus: diff --git a/toolchain/parse/precedence.h b/toolchain/parse/precedence.h index 3c2242b62f15..d418a0fad898 100644 --- a/toolchain/parse/precedence.h +++ b/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 expressions in requirements + // after `where` or `require`. + static auto ForRequirements() -> PrecedenceGroup; + // Look up the operator information of the given prefix operator token, or // return std::nullopt if the given token is not a prefix operator. static auto ForLeading(Lex::TokenKind kind) -> std::optional; diff --git a/toolchain/parse/state.def b/toolchain/parse/state.def index 27a33c2e8819..40c65ef01014 100644 --- a/toolchain/parse/state.def +++ b/toolchain/parse/state.def @@ -17,7 +17,7 @@ // // The comments before each state describe the portion of the grammar that the // state is implementing, by giving an example of each kind of token sequence -// that this state handles. In this example, `...` indicates a sequence of +// that this state handles. In these examples, `...` indicates a sequence of // tokens handled by some other state, and `???` indicates a sequence of invalid // tokens. A trailing `??? ;` indicates an attempt to skip to the end of the // declaration, which may or may not actually find a `;` token. @@ -614,6 +614,46 @@ CARBON_PARSE_STATE(IfExprFinishElse) // (state done) CARBON_PARSE_STATE(IfExprFinish) +// Handles the beginning of a requirement expression after a `where` operator in +// an expression. +// TODO: Also a `require` declaration? +// +// expr where ... +// ^ +// 1. Expr +// 2. RequirementOperator +CARBON_PARSE_STATE(RequirementBegin) + +// Handles a requirement operator in a `where` expression. +// +// expr where expr impls ... +// ^~~~ +// expr where expr = ... +// ^ +// expr where expr == ... +// ^~ +// 1. Expr +// 2. RequirementOperatorFinish +CARBON_PARSE_STATE(RequirementOperator) + +// Finishes a requirement operator in a `where` expression. +// +// expr where expr impls expr +// ^ +// expr where expr = expr +// ^ +// expr where expr == expr +// ^ +// (state done) +CARBON_PARSE_STATE(RequirementOperatorFinish) + +// Finishes an `where` expression. +// +// expr where requirement +// ^ +// (state done) +CARBON_PARSE_STATE(WhereFinish) + // Handles the `;` for an expression statement, which is different from most // keyword statements. // diff --git a/toolchain/parse/testdata/operators/fail_precedence_where.carbon b/toolchain/parse/testdata/operators/fail_precedence_where.carbon new file mode 100644 index 000000000000..10aaba3a3640 --- /dev/null +++ b/toolchain/parse/testdata/operators/fail_precedence_where.carbon @@ -0,0 +1,271 @@ +// 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/operators/fail_precedence_where.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/operators/fail_precedence_where.carbon + +// --- fail_and_where.carbon + +fn AndWhere() { + // CHECK:STDERR: fail_and_where.carbon:[[@LINE+4]]:14: ERROR: Parentheses are required to disambiguate operator precedence. + // CHECK:STDERR: true and T where .A = B; + // CHECK:STDERR: ^~~~~ + // CHECK:STDERR: + true and T where .A = B; +} + +// --- fail_where_or.carbon + +fn WhereOr() { + // CHECK:STDERR: fail_where_or.carbon:[[@LINE+4]]:22: ERROR: Expected `;` after expression statement. + // CHECK:STDERR: U where .C impls D or false; + // CHECK:STDERR: ^~ + // CHECK:STDERR: + U where .C impls D or false; +} + +// --- fail_equals_where.carbon + +fn EqualsWhere() { + // CHECK:STDERR: fail_equals_where.carbon:[[@LINE+4]]:10: ERROR: Parentheses are required to disambiguate operator precedence. + // CHECK:STDERR: e == V where .G == f32; + // CHECK:STDERR: ^~~~~ + // CHECK:STDERR: + e == V where .G == f32; +} + +// --- fail_where_neq.carbon + +fn WhereNEq() { + // CHECK:STDERR: fail_where_neq.carbon:[[@LINE+4]]:21: ERROR: Expected `;` after expression statement. + // CHECK:STDERR: W where .H = bool != i; + // CHECK:STDERR: ^~ + // CHECK:STDERR: + W where .H = bool != i; +} + +// --- fail_less_requirement.carbon + +fn LessRequirement() { + // CHECK:STDERR: fail_less_requirement.carbon:[[@LINE+4]]:14: ERROR: Requirement should use `impls`, `=`, or `==` operator. + // CHECK:STDERR: X where .J < .K; + // CHECK:STDERR: ^ + // CHECK:STDERR: + X where .J < .K; +} + +// --- fail_as_where.carbon + +fn AsWhere() { + // CHECK:STDERR: fail_as_where.carbon:[[@LINE+4]]:10: ERROR: Parentheses are required to disambiguate operator precedence. + // CHECK:STDERR: Y as Z where .L impls M; + // CHECK:STDERR: ^~~~~ + // CHECK:STDERR: + Y as Z where .L impls M; +} + +// --- fail_where_as.carbon + +fn WhereAs() { + // CHECK:STDERR: fail_where_as.carbon:[[@LINE+4]]:21: ERROR: Expected `;` after expression statement. + // CHECK:STDERR: S where .N == i32 as type; + // CHECK:STDERR: ^~ + // CHECK:STDERR: + S where .N == i32 as type; +} + +// --- fail_as_in_argument.carbon + +fn AsInArgument() { + // CHECK:STDERR: fail_as_in_argument.carbon:[[@LINE+4]]:14: ERROR: Requirement should use `impls`, `=`, or `==` operator. + // CHECK:STDERR: R where .O as type impls P; + // CHECK:STDERR: ^~ + // CHECK:STDERR: + R where .O as type impls P; +} + +// --- fail_greater_in_argument.carbon + +fn GreaterInArgument() { + // CHECK:STDERR: fail_greater_in_argument.carbon:[[@LINE+3]]:15: ERROR: Requirement should use `impls`, `=`, or `==` operator. + // CHECK:STDERR: Q where .AA > .BB impls CC; + // CHECK:STDERR: ^ + Q where .AA > .BB impls CC; +} + +// CHECK:STDOUT: - filename: fail_and_where.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'AndWhere'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'BoolLiteralTrue', text: 'true'}, +// CHECK:STDOUT: {kind: 'ShortCircuitOperandAnd', text: 'and', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'T'}, +// CHECK:STDOUT: {kind: 'ShortCircuitOperatorAnd', text: 'and', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'WhereOperand', text: 'where', has_error: yes, subtree_size: 5}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'A'}, +// CHECK:STDOUT: {kind: 'DesignatorExpr', text: '.', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'B'}, +// CHECK:STDOUT: {kind: 'RequirementEqual', text: '=', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'WhereExpr', text: 'where', has_error: yes, subtree_size: 10}, +// CHECK:STDOUT: {kind: 'ExprStatement', text: ';', has_error: yes, subtree_size: 11}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 17}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: fail_where_or.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'WhereOr'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'U'}, +// CHECK:STDOUT: {kind: 'WhereOperand', text: 'where', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'C'}, +// CHECK:STDOUT: {kind: 'DesignatorExpr', text: '.', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'D'}, +// CHECK:STDOUT: {kind: 'RequirementImpls', text: 'impls', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'WhereExpr', text: 'where', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'ExprStatement', text: ';', has_error: yes, subtree_size: 8}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 14}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: fail_equals_where.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'EqualsWhere'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'e'}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'V'}, +// CHECK:STDOUT: {kind: 'InfixOperatorEqualEqual', text: '==', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'WhereOperand', text: 'where', has_error: yes, subtree_size: 4}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'G'}, +// CHECK:STDOUT: {kind: 'DesignatorExpr', text: '.', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FloatTypeLiteral', text: 'f32'}, +// CHECK:STDOUT: {kind: 'RequirementEqualEqual', text: '==', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'WhereExpr', text: 'where', has_error: yes, subtree_size: 9}, +// CHECK:STDOUT: {kind: 'ExprStatement', text: ';', has_error: yes, subtree_size: 10}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 16}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: fail_where_neq.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'WhereNEq'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'W'}, +// CHECK:STDOUT: {kind: 'WhereOperand', text: 'where', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'H'}, +// CHECK:STDOUT: {kind: 'DesignatorExpr', text: '.', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'BoolTypeLiteral', text: 'bool'}, +// CHECK:STDOUT: {kind: 'RequirementEqual', text: '=', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'WhereExpr', text: 'where', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'ExprStatement', text: ';', has_error: yes, subtree_size: 8}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 14}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: fail_less_requirement.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'LessRequirement'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'X'}, +// CHECK:STDOUT: {kind: 'WhereOperand', text: 'where', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'J'}, +// CHECK:STDOUT: {kind: 'DesignatorExpr', text: '.', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'WhereExpr', text: 'where', has_error: yes, subtree_size: 5}, +// CHECK:STDOUT: {kind: 'ExprStatement', text: ';', has_error: yes, subtree_size: 6}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 12}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: fail_as_where.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'AsWhere'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'Y'}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'Z'}, +// CHECK:STDOUT: {kind: 'InfixOperatorAs', text: 'as', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'WhereOperand', text: 'where', has_error: yes, subtree_size: 4}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'L'}, +// CHECK:STDOUT: {kind: 'DesignatorExpr', text: '.', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'M'}, +// CHECK:STDOUT: {kind: 'RequirementImpls', text: 'impls', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'WhereExpr', text: 'where', has_error: yes, subtree_size: 9}, +// CHECK:STDOUT: {kind: 'ExprStatement', text: ';', has_error: yes, subtree_size: 10}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 16}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: fail_where_as.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'WhereAs'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'S'}, +// CHECK:STDOUT: {kind: 'WhereOperand', text: 'where', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'N'}, +// CHECK:STDOUT: {kind: 'DesignatorExpr', text: '.', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, +// CHECK:STDOUT: {kind: 'RequirementEqualEqual', text: '==', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'WhereExpr', text: 'where', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'ExprStatement', text: ';', has_error: yes, subtree_size: 8}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 14}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: fail_as_in_argument.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'AsInArgument'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'R'}, +// CHECK:STDOUT: {kind: 'WhereOperand', text: 'where', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'O'}, +// CHECK:STDOUT: {kind: 'DesignatorExpr', text: '.', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'WhereExpr', text: 'where', has_error: yes, subtree_size: 5}, +// CHECK:STDOUT: {kind: 'ExprStatement', text: ';', has_error: yes, subtree_size: 6}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 12}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: fail_greater_in_argument.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'GreaterInArgument'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'Q'}, +// CHECK:STDOUT: {kind: 'WhereOperand', text: 'where', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'AA'}, +// CHECK:STDOUT: {kind: 'DesignatorExpr', text: '.', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'WhereExpr', text: 'where', has_error: yes, subtree_size: 5}, +// CHECK:STDOUT: {kind: 'ExprStatement', text: ';', has_error: yes, subtree_size: 6}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 12}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/operators/precedence_where.carbon b/toolchain/parse/testdata/operators/precedence_where.carbon new file mode 100644 index 000000000000..6c160b3f6d05 --- /dev/null +++ b/toolchain/parse/testdata/operators/precedence_where.carbon @@ -0,0 +1,95 @@ +// 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/operators/precedence_where.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/operators/precedence_where.carbon + +fn PostfixStar(T:! I* where .A* == .B*); + +fn Ampersand(U:! J & K where C & D impls E & F); + +fn BinaryPlus(V:! L + M where G & H impls N & O); + +fn UnaryMinus(W:! -P where -Q impls -R); + +// CHECK:STDOUT: - filename: precedence_where.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'PostfixStar'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'T'}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'I'}, +// CHECK:STDOUT: {kind: 'PostfixOperatorStar', text: '*', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'WhereOperand', text: 'where', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'A'}, +// CHECK:STDOUT: {kind: 'DesignatorExpr', text: '.', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'PostfixOperatorStar', text: '*', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'B'}, +// CHECK:STDOUT: {kind: 'DesignatorExpr', text: '.', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'PostfixOperatorStar', text: '*', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'RequirementEqualEqual', text: '==', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'WhereExpr', text: 'where', subtree_size: 11}, +// CHECK:STDOUT: {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 13}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 15}, +// CHECK:STDOUT: {kind: 'FunctionDecl', text: ';', subtree_size: 18}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'Ampersand'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'U'}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'J'}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'K'}, +// CHECK:STDOUT: {kind: 'InfixOperatorAmp', text: '&', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'WhereOperand', text: 'where', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'C'}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'D'}, +// CHECK:STDOUT: {kind: 'InfixOperatorAmp', text: '&', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'E'}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'F'}, +// CHECK:STDOUT: {kind: 'InfixOperatorAmp', text: '&', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'RequirementImpls', text: 'impls', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'WhereExpr', text: 'where', subtree_size: 12}, +// CHECK:STDOUT: {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 14}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 16}, +// CHECK:STDOUT: {kind: 'FunctionDecl', text: ';', subtree_size: 19}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'BinaryPlus'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'V'}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'L'}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'M'}, +// CHECK:STDOUT: {kind: 'InfixOperatorPlus', text: '+', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'WhereOperand', text: 'where', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'G'}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'H'}, +// CHECK:STDOUT: {kind: 'InfixOperatorAmp', text: '&', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'N'}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'O'}, +// CHECK:STDOUT: {kind: 'InfixOperatorAmp', text: '&', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'RequirementImpls', text: 'impls', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'WhereExpr', text: 'where', subtree_size: 12}, +// CHECK:STDOUT: {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 14}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 16}, +// CHECK:STDOUT: {kind: 'FunctionDecl', text: ';', subtree_size: 19}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'UnaryMinus'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'W'}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'P'}, +// CHECK:STDOUT: {kind: 'PrefixOperatorMinus', text: '-', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'WhereOperand', text: 'where', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'Q'}, +// CHECK:STDOUT: {kind: 'PrefixOperatorMinus', text: '-', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'R'}, +// CHECK:STDOUT: {kind: 'PrefixOperatorMinus', text: '-', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'RequirementImpls', text: 'impls', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'WhereExpr', text: 'where', subtree_size: 9}, +// CHECK:STDOUT: {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 11}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 13}, +// CHECK:STDOUT: {kind: 'FunctionDecl', text: ';', subtree_size: 16}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/where_expr/basic.carbon b/toolchain/parse/testdata/where_expr/basic.carbon new file mode 100644 index 000000000000..01a66df2126a --- /dev/null +++ b/toolchain/parse/testdata/where_expr/basic.carbon @@ -0,0 +1,86 @@ +// 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/where_expr/basic.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/where_expr/basic.carbon + +fn Foo(T: type where .Self == i32); + +interface I { + let T:! type where .Foo impls Bar; +} + +let T:! type where .U = .V; + +fn F() { + let U:! type where .W == .X; +} + +// CHECK:STDOUT: - filename: basic.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'Foo'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'T'}, +// CHECK:STDOUT: {kind: 'TypeTypeLiteral', text: 'type'}, +// CHECK:STDOUT: {kind: 'WhereOperand', text: 'where', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'SelfTypeName', text: 'Self'}, +// CHECK:STDOUT: {kind: 'DesignatorExpr', text: '.', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, +// CHECK:STDOUT: {kind: 'RequirementEqualEqual', text: '==', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'WhereExpr', text: 'where', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'BindingPattern', text: ':', subtree_size: 9}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 11}, +// CHECK:STDOUT: {kind: 'FunctionDecl', text: ';', subtree_size: 14}, +// CHECK:STDOUT: {kind: 'InterfaceIntroducer', text: 'interface'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'I'}, +// CHECK:STDOUT: {kind: 'InterfaceDefinitionStart', text: '{', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'LetIntroducer', text: 'let'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'T'}, +// CHECK:STDOUT: {kind: 'TypeTypeLiteral', text: 'type'}, +// CHECK:STDOUT: {kind: 'WhereOperand', text: 'where', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'Foo'}, +// CHECK:STDOUT: {kind: 'DesignatorExpr', text: '.', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'Bar'}, +// CHECK:STDOUT: {kind: 'RequirementImpls', text: 'impls', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'WhereExpr', text: 'where', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 9}, +// CHECK:STDOUT: {kind: 'LetDecl', text: ';', subtree_size: 11}, +// CHECK:STDOUT: {kind: 'InterfaceDefinition', text: '}', subtree_size: 15}, +// CHECK:STDOUT: {kind: 'LetIntroducer', text: 'let'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'T'}, +// CHECK:STDOUT: {kind: 'TypeTypeLiteral', text: 'type'}, +// CHECK:STDOUT: {kind: 'WhereOperand', text: 'where', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'U'}, +// CHECK:STDOUT: {kind: 'DesignatorExpr', text: '.', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'V'}, +// CHECK:STDOUT: {kind: 'DesignatorExpr', text: '.', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'RequirementEqual', text: '=', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'WhereExpr', text: 'where', subtree_size: 8}, +// CHECK:STDOUT: {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 10}, +// CHECK:STDOUT: {kind: 'LetDecl', text: ';', subtree_size: 12}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'F'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'LetIntroducer', text: 'let'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'U'}, +// CHECK:STDOUT: {kind: 'TypeTypeLiteral', text: 'type'}, +// CHECK:STDOUT: {kind: 'WhereOperand', text: 'where', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'W'}, +// CHECK:STDOUT: {kind: 'DesignatorExpr', text: '.', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'X'}, +// CHECK:STDOUT: {kind: 'DesignatorExpr', text: '.', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'RequirementEqualEqual', text: '==', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'WhereExpr', text: 'where', subtree_size: 8}, +// CHECK:STDOUT: {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 10}, +// CHECK:STDOUT: {kind: 'LetDecl', text: ';', subtree_size: 12}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 18}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/where_expr/fail_todo_where_and.carbon b/toolchain/parse/testdata/where_expr/fail_todo_where_and.carbon new file mode 100644 index 000000000000..f67ac6a98a5b --- /dev/null +++ b/toolchain/parse/testdata/where_expr/fail_todo_where_and.carbon @@ -0,0 +1,36 @@ +// 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/where_expr/fail_todo_where_and.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/where_expr/fail_todo_where_and.carbon + +// TODO: Support `and` to allow multiple requirements in `where` expressions. + +// CHECK:STDERR: fail_todo_where_and.carbon:[[@LINE+3]]:31: ERROR: Expected `,` or `)`. +// CHECK:STDERR: fn Foo(T: type where .U = i32 and .V == .W and .X impls I); +// CHECK:STDERR: ^~~ +fn Foo(T: type where .U = i32 and .V == .W and .X impls I); + +// CHECK:STDOUT: - filename: fail_todo_where_and.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'Foo'}, +// CHECK:STDOUT: {kind: 'TuplePatternStart', text: '('}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'T'}, +// CHECK:STDOUT: {kind: 'TypeTypeLiteral', text: 'type'}, +// CHECK:STDOUT: {kind: 'WhereOperand', text: 'where', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'U'}, +// CHECK:STDOUT: {kind: 'DesignatorExpr', text: '.', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, +// CHECK:STDOUT: {kind: 'RequirementEqual', text: '=', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'WhereExpr', text: 'where', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'BindingPattern', text: ':', subtree_size: 9}, +// CHECK:STDOUT: {kind: 'TuplePattern', text: ')', has_error: yes, subtree_size: 11}, +// CHECK:STDOUT: {kind: 'FunctionDecl', text: ';', subtree_size: 14}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parse/testdata/where_expr/impl_where.carbon b/toolchain/parse/testdata/where_expr/impl_where.carbon new file mode 100644 index 000000000000..bf52cc93284a --- /dev/null +++ b/toolchain/parse/testdata/where_expr/impl_where.carbon @@ -0,0 +1,57 @@ +// 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/where_expr/impl_where.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/where_expr/impl_where.carbon + +interface Interface { + let T:! type; +} + +impl i32 as Interface where .T = () { +} + +impl bool as Interface where .T = i32; + +// CHECK:STDOUT: - filename: impl_where.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'InterfaceIntroducer', text: 'interface'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'Interface'}, +// CHECK:STDOUT: {kind: 'InterfaceDefinitionStart', text: '{', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'LetIntroducer', text: 'let'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'T'}, +// CHECK:STDOUT: {kind: 'TypeTypeLiteral', text: 'type'}, +// CHECK:STDOUT: {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'LetDecl', text: ';', subtree_size: 5}, +// 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: 'IdentifierNameExpr', text: 'Interface'}, +// CHECK:STDOUT: {kind: 'WhereOperand', text: 'where', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'T'}, +// 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: 'ImplDefinitionStart', text: '{', subtree_size: 12}, +// 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: 'IdentifierNameExpr', text: 'Interface'}, +// CHECK:STDOUT: {kind: 'WhereOperand', text: 'where', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'T'}, +// CHECK:STDOUT: {kind: 'DesignatorExpr', text: '.', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, +// CHECK:STDOUT: {kind: 'RequirementEqual', text: '=', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'WhereExpr', text: 'where', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'ImplDecl', text: ';', subtree_size: 11}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parse/typed_nodes.h b/toolchain/parse/typed_nodes.h index cab0eca359df..3ca9d931e1d8 100644 --- a/toolchain/parse/typed_nodes.h +++ b/toolchain/parse/typed_nodes.h @@ -1016,6 +1016,8 @@ struct IfExprElse { AnyExprId else_result; }; +// A `where` expression (TODO: `require` and `observe` declarations) + // The `Self` in a context where it is treated as a name rather than an // expression, such as `.Self`. using SelfTypeName = @@ -1032,6 +1034,50 @@ struct DesignatorExpr { NodeIdOneOf name; }; +struct RequirementEqual { + static constexpr auto Kind = NodeKind::RequirementEqual.Define( + {.category = NodeCategory::Requirement, .child_count = 2}); + // TODO: Enforce that the lhs is always a DesignatorExpr. + AnyExprId lhs; + Lex::EqualTokenIndex token; + AnyExprId rhs; +}; + +struct RequirementEqualEqual { + static constexpr auto Kind = NodeKind::RequirementEqualEqual.Define( + {.category = NodeCategory::Requirement, .child_count = 2}); + AnyExprId lhs; + Lex::EqualEqualTokenIndex token; + AnyExprId rhs; +}; + +struct RequirementImpls { + static constexpr auto Kind = NodeKind::RequirementImpls.Define( + {.category = NodeCategory::Requirement, .child_count = 2}); + AnyExprId lhs; + Lex::ImplsTokenIndex token; + AnyExprId rhs; +}; + +struct WhereOperand { + static constexpr auto Kind = + NodeKind::WhereOperand.Define({.child_count = 1}); + AnyExprId type; + // This is a virtual token. The `where` token is owned by the + // WhereExpr node. + Lex::WhereTokenIndex token; +}; + +struct WhereExpr { + static constexpr auto Kind = + NodeKind::WhereExpr.Define({.category = NodeCategory::Expr, + .bracketed_by = WhereOperand::Kind, + .child_count = 2}); + WhereOperandId introducer; + Lex::WhereTokenIndex token; + AnyRequirementId requirements; +}; + // Choice nodes // ------------