From e613ad53235458131226da10b53e557159ff21f3 Mon Sep 17 00:00:00 2001 From: Jon Ross-Perkins Date: Mon, 13 Mar 2023 17:24:01 -0700 Subject: [PATCH] Reorganize `interface` parsing so that it's shared with `class` and `constraint` (#2666) We could similarly add others -- this is intended to make it easy to add more that parse essentially the same. The functionality expected is that types will use GetDeclarationContext in order to error on certain functionality in the declaration scope loop. e.g., with how constraints and interfaces currently don't allow definitions. I've only moved out `package` because it's only valid on the top line. It might still be good to parse it later, but with slightly different logic because it would always be an error, and the declaration context isn't quite the right framing for that. Also unifies some errors with `fn`. --- toolchain/diagnostics/diagnostic_kind.def | 8 +- toolchain/parser/parse_node_kind.def | 33 +++ toolchain/parser/parser.cpp | 215 +++++++++++------- toolchain/parser/parser.h | 14 ++ toolchain/parser/parser_state.def | 64 +++--- toolchain/parser/testdata/class/basic.carbon | 22 ++ .../declaration/fail_missing_name.carbon | 2 +- .../declaration/fail_only_fn_and_semi.carbon | 2 +- .../fail_repeated_fn_and_semi.carbon | 2 +- ...skip_indented_newline_until_outdent.carbon | 2 +- ...ail_skip_indented_newline_with_semi.carbon | 2 +- ..._skip_indented_newline_without_semi.carbon | 2 +- .../fail_skip_to_newline_without_semi.carbon | 2 +- ...hout_name_and_many_tokens_in_params.carbon | 2 +- .../interface/fail_missing_name.carbon | 2 +- .../interface/fail_missing_open_curly.carbon | 4 +- .../generics/named_constraint/basic.carbon | 22 ++ .../fail_no_impl_allowed.carbon | 33 +++ .../semantics_parse_tree_handler.cpp | 49 ++++ 19 files changed, 352 insertions(+), 130 deletions(-) create mode 100644 toolchain/parser/testdata/class/basic.carbon create mode 100644 toolchain/parser/testdata/generics/named_constraint/basic.carbon create mode 100644 toolchain/parser/testdata/generics/named_constraint/fail_no_impl_allowed.carbon diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index d99ed45d0d8c..9dfda228fa1c 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/toolchain/diagnostics/diagnostic_kind.def @@ -53,8 +53,6 @@ CARBON_DIAGNOSTIC_KIND(BinaryOperatorRequiresWhitespace) CARBON_DIAGNOSTIC_KIND(ExpectedCloseParen) CARBON_DIAGNOSTIC_KIND(ExpectedCodeBlock) CARBON_DIAGNOSTIC_KIND(ExpectedExpression) -CARBON_DIAGNOSTIC_KIND(ExpectedFunctionBodyOrSemi) -CARBON_DIAGNOSTIC_KIND(ExpectedFunctionName) CARBON_DIAGNOSTIC_KIND(ExpectedFunctionParams) CARBON_DIAGNOSTIC_KIND(ExpectedIdentifierAfterDot) CARBON_DIAGNOSTIC_KIND(ExpectedParameterName) @@ -81,9 +79,9 @@ CARBON_DIAGNOSTIC_KIND(ExpectedSemiToEndPackageDirective) CARBON_DIAGNOSTIC_KIND(ExpectedIn) CARBON_DIAGNOSTIC_KIND(ExpectedInNotColon) -// Interface-specific diagnostics. -CARBON_DIAGNOSTIC_KIND(ExpectedInterfaceName) -CARBON_DIAGNOSTIC_KIND(ExpectedInterfaceOpenCurlyBrace) +// Declaration diagnostics. +CARBON_DIAGNOSTIC_KIND(ExpectedDeclarationName) +CARBON_DIAGNOSTIC_KIND(ExpectedDeclarationSemiOrDefinition) CARBON_DIAGNOSTIC_KIND(MethodImplNotAllowed) // Class and interface diagnostics diff --git a/toolchain/parser/parse_node_kind.def b/toolchain/parser/parse_node_kind.def index 561c78272c5c..4b17b9ef0f71 100644 --- a/toolchain/parser/parse_node_kind.def +++ b/toolchain/parser/parse_node_kind.def @@ -282,6 +282,21 @@ CARBON_PARSE_NODE_KIND_BRACKET(StructLiteral, CARBON_PARSE_NODE_KIND_BRACKET(StructTypeLiteral, StructLiteralOrStructTypeLiteralStart) +// `class`: +// ClassIntroducer +// DeclaredName +// ClassDefinitionStart +// _external_: declarations +// ClassDefinition +// +// The above is the structure for a definition; for a declaration, +// ClassDefinitionStart and later nodes are removed and replaced by +// ClassDeclaration. +CARBON_PARSE_NODE_KIND_CHILD_COUNT(ClassIntroducer, 0) +CARBON_PARSE_NODE_KIND_BRACKET(ClassDefinitionStart, ClassIntroducer) +CARBON_PARSE_NODE_KIND_BRACKET(ClassDefinition, ClassDefinitionStart) +CARBON_PARSE_NODE_KIND_BRACKET(ClassDeclaration, ClassIntroducer) + // `interface`: // InterfaceIntroducer // DeclaredName @@ -297,6 +312,24 @@ CARBON_PARSE_NODE_KIND_BRACKET(InterfaceDefinitionStart, InterfaceIntroducer) CARBON_PARSE_NODE_KIND_BRACKET(InterfaceDefinition, InterfaceDefinitionStart) CARBON_PARSE_NODE_KIND_BRACKET(InterfaceDeclaration, InterfaceIntroducer) +// `constraint`: +// NamedConstraintIntroducer +// DeclaredName +// NamedConstraintDefinitionStart +// _external_: declarations +// NamedConstraintDefinition +// +// The above is the structure for a definition; for a declaration, +// NamedConstraintDefinitionStart and later nodes are removed and replaced by +// NamedConstraintDeclaration. +CARBON_PARSE_NODE_KIND_CHILD_COUNT(NamedConstraintIntroducer, 0) +CARBON_PARSE_NODE_KIND_BRACKET(NamedConstraintDefinitionStart, + NamedConstraintIntroducer) +CARBON_PARSE_NODE_KIND_BRACKET(NamedConstraintDefinition, + NamedConstraintDefinitionStart) +CARBON_PARSE_NODE_KIND_BRACKET(NamedConstraintDeclaration, + NamedConstraintIntroducer) + // A pattern binding for `self` deduced parameter: // `self` // _external_: type expression diff --git a/toolchain/parser/parser.cpp b/toolchain/parser/parser.cpp index 3d13c3821dd6..d48c95741307 100644 --- a/toolchain/parser/parser.cpp +++ b/toolchain/parser/parser.cpp @@ -28,6 +28,13 @@ CARBON_DIAGNOSTIC(ExpectedParenAfter, Error, "Expected `(` after `{0}`.", CARBON_DIAGNOSTIC(ExpectedSemiAfterExpression, Error, "Expected `;` after expression."); +CARBON_DIAGNOSTIC(ExpectedDeclarationName, Error, + "`{0}` introducer should be followed by a name.", TokenKind); +CARBON_DIAGNOSTIC(ExpectedDeclarationSemiOrDefinition, Error, + "`{0}` should either end with a `;` for a declaration or " + "have a `{{ ... }` block for a definition.", + TokenKind); + // A relative location for characters in errors. enum class RelativeLocation : int8_t { Around, @@ -433,7 +440,14 @@ auto Parser::Parse() -> void { // Traces state_stack_. This runs even in opt because it's low overhead. PrettyStackTraceParseState pretty_stack(this); - PushState(ParserState::DeclarationLoop); + PushState(ParserState::DeclarationScopeLoop); + + // The package should always be the first token, if it's present. Any other + // use is invalid. + if (PositionIs(TokenKind::Package)) { + PushState(ParserState::Package); + } + while (!state_stack_.empty()) { switch (state_stack_.back().state) { #define CARBON_PARSER_STATE(Name) \ @@ -448,18 +462,27 @@ auto Parser::Parse() -> void { } auto Parser::GetDeclarationContext() -> DeclarationContext { - for (auto entry : llvm::reverse(state_stack_)) { - switch (entry.state) { - case ParserState::InterfaceDefinitionLoop: - return DeclarationContext::Interface; - case ParserState::DeclarationLoop: - return DeclarationContext::File; - default: - // Continue checking. - break; + // i == 0 is the file-level DeclarationScopeLoop. Additionally, i == 1 can be + // skipped because it will never be a DeclarationScopeLoop. + for (int i = state_stack_.size() - 1; i > 1; --i) { + // The declaration context is always the state _above_ a + // DeclarationScopeLoop. + if (state_stack_[i].state == ParserState::DeclarationScopeLoop) { + switch (state_stack_[i - 1].state) { + case ParserState::TypeDefinitionFinishAsClass: + return DeclarationContext::Class; + case ParserState::TypeDefinitionFinishAsInterface: + return DeclarationContext::Interface; + case ParserState::TypeDefinitionFinishAsNamedConstraint: + return DeclarationContext::NamedConstraint; + default: + llvm_unreachable("Missing handling for a declaration scope"); + } } } - llvm_unreachable("Should always be able to find DeclarationLoop"); + CARBON_CHECK(!state_stack_.empty() && + state_stack_[0].state == ParserState::DeclarationScopeLoop); + return DeclarationContext::File; } auto Parser::HandleDeclarationError(StateStackEntry state, @@ -743,20 +766,30 @@ auto Parser::HandleCodeBlockFinishState() -> void { } } -auto Parser::HandleDeclarationLoopState() -> void { - // This maintains the current state unless we're at the end of the file. +auto Parser::HandleDeclarationScopeLoopState() -> void { + // This maintains the current state unless we're at the end of the scope. switch (PositionKind()) { + case TokenKind::CloseCurlyBrace: case TokenKind::EndOfFile: { + // This is the end of the scope, so the loop state ends. PopAndDiscardState(); break; } + case TokenKind::Class: { + PushState(ParserState::TypeIntroducerAsClass); + break; + } + case TokenKind::Constraint: { + PushState(ParserState::TypeIntroducerAsNamedConstraint); + break; + } case TokenKind::Fn: { PushState(ParserState::FunctionIntroducer); break; } - case TokenKind::Package: { - PushState(ParserState::Package); + case TokenKind::Interface: { + PushState(ParserState::TypeIntroducerAsInterface); break; } case TokenKind::Semi: { @@ -767,10 +800,6 @@ auto Parser::HandleDeclarationLoopState() -> void { PushState(ParserState::VarAsSemicolon); break; } - case TokenKind::Interface: { - PushState(ParserState::InterfaceIntroducer); - break; - } default: { HandleUnrecognizedDeclaration(); break; @@ -1057,9 +1086,7 @@ auto Parser::HandleFunctionIntroducerState() -> void { if (!ConsumeAndAddLeafNodeIf(TokenKind::Identifier, ParseNodeKind::DeclaredName)) { - CARBON_DIAGNOSTIC(ExpectedFunctionName, Error, - "Expected function name after `fn` keyword."); - emitter_->Emit(*position_, ExpectedFunctionName); + emitter_->Emit(*position_, ExpectedDeclarationName, TokenKind::Fn); // TODO: We could change the lexer to allow us to synthesize certain // kinds of tokens and try to "recover" here, but unclear that this is // really useful. @@ -1170,7 +1197,9 @@ auto Parser::HandleFunctionSignatureFinishState() -> void { break; } case TokenKind::OpenCurlyBrace: { - if (GetDeclarationContext() == DeclarationContext::Interface) { + if (auto context = GetDeclarationContext(); + context == DeclarationContext::Interface || + context == DeclarationContext::NamedConstraint) { CARBON_DIAGNOSTIC( MethodImplNotAllowed, Error, "Method implementations are not allowed in interfaces."); @@ -1190,10 +1219,8 @@ auto Parser::HandleFunctionSignatureFinishState() -> void { break; } default: { - CARBON_DIAGNOSTIC( - ExpectedFunctionBodyOrSemi, Error, - "Expected function definition or `;` after function declaration."); - emitter_->Emit(*position_, ExpectedFunctionBodyOrSemi); + emitter_->Emit(*position_, ExpectedDeclarationSemiOrDefinition, + TokenKind::Fn); // Only need to skip if we've not already found a new line. bool skip_past_likely_end = tokens_->GetLine(*position_) == tokens_->GetLine(state.token); @@ -1210,64 +1237,6 @@ auto Parser::HandleFunctionDefinitionFinishState() -> void { state.has_error); } -auto Parser::HandleInterfaceIntroducerState() -> void { - auto state = PopState(); - - AddLeafNode(ParseNodeKind::InterfaceIntroducer, Consume()); - - if (!ConsumeAndAddLeafNodeIf(TokenKind::Identifier, - ParseNodeKind::DeclaredName)) { - CARBON_DIAGNOSTIC(ExpectedInterfaceName, Error, - "Expected interface name after `interface` keyword."); - emitter_->Emit(*position_, ExpectedInterfaceName); - HandleDeclarationError(state, ParseNodeKind::InterfaceDeclaration, - /*skip_past_likely_end=*/true); - return; - } - - if (auto semi = ConsumeIf(TokenKind::Semi)) { - AddNode(ParseNodeKind::InterfaceDeclaration, *semi, state.subtree_start, - state.has_error); - return; - } - - if (!PositionIs(TokenKind::OpenCurlyBrace)) { - CARBON_DIAGNOSTIC(ExpectedInterfaceOpenCurlyBrace, Error, - "Expected `{{` to start interface definition."); - emitter_->Emit(*position_, ExpectedInterfaceOpenCurlyBrace); - HandleDeclarationError(state, ParseNodeKind::InterfaceDeclaration, - /*skip_past_likely_end=*/true); - return; - } - - state.state = ParserState::InterfaceDefinitionLoop; - PushState(state); - AddNode(ParseNodeKind::InterfaceDefinitionStart, Consume(), - state.subtree_start, state.has_error); -} - -auto Parser::HandleInterfaceDefinitionLoopState() -> void { - // This maintains the current state unless we're at the end of the interface - // definition. - - switch (PositionKind()) { - case TokenKind::CloseCurlyBrace: { - auto state = PopState(); - AddNode(ParseNodeKind::InterfaceDefinition, Consume(), - state.subtree_start, state.has_error); - break; - } - case TokenKind::Fn: { - PushState(ParserState::FunctionIntroducer); - break; - } - default: { - HandleUnrecognizedDeclaration(); - break; - } - } -} - auto Parser::HandlePackageState() -> void { auto state = PopState(); @@ -1762,6 +1731,82 @@ auto Parser::HandleStatementWhileBlockFinishState() -> void { state.has_error); } +auto Parser::HandleTypeIntroducer(ParseNodeKind introducer_kind, + ParseNodeKind declaration_kind, + ParseNodeKind definition_start_kind, + ParserState definition_finish_state) -> void { + auto state = PopState(); + + AddLeafNode(introducer_kind, Consume()); + + if (!ConsumeAndAddLeafNodeIf(TokenKind::Identifier, + ParseNodeKind::DeclaredName)) { + emitter_->Emit(*position_, ExpectedDeclarationName, + tokens_->GetKind(state.token)); + HandleDeclarationError(state, declaration_kind, + /*skip_past_likely_end=*/true); + return; + } + + if (auto semi = ConsumeIf(TokenKind::Semi)) { + AddNode(declaration_kind, *semi, state.subtree_start, state.has_error); + return; + } + + if (!PositionIs(TokenKind::OpenCurlyBrace)) { + emitter_->Emit(*position_, ExpectedDeclarationSemiOrDefinition, + tokens_->GetKind(state.token)); + HandleDeclarationError(state, declaration_kind, + /*skip_past_likely_end=*/true); + return; + } + + state.state = definition_finish_state; + PushState(state); + PushState(ParserState::DeclarationScopeLoop); + AddNode(definition_start_kind, Consume(), state.subtree_start, + state.has_error); +} + +auto Parser::HandleTypeIntroducerAsClassState() -> void { + HandleTypeIntroducer(ParseNodeKind::ClassIntroducer, + ParseNodeKind::ClassDeclaration, + ParseNodeKind::ClassDefinitionStart, + ParserState::TypeDefinitionFinishAsClass); +} + +auto Parser::HandleTypeIntroducerAsInterfaceState() -> void { + HandleTypeIntroducer(ParseNodeKind::InterfaceIntroducer, + ParseNodeKind::InterfaceDeclaration, + ParseNodeKind::InterfaceDefinitionStart, + ParserState::TypeDefinitionFinishAsInterface); +} + +auto Parser::HandleTypeIntroducerAsNamedConstraintState() -> void { + HandleTypeIntroducer(ParseNodeKind::NamedConstraintIntroducer, + ParseNodeKind::NamedConstraintDeclaration, + ParseNodeKind::NamedConstraintDefinitionStart, + ParserState::TypeDefinitionFinishAsNamedConstraint); +} + +auto Parser::HandleTypeDefinitionFinish(ParseNodeKind definition_kind) -> void { + auto state = PopState(); + + AddNode(definition_kind, Consume(), state.subtree_start, state.has_error); +} + +auto Parser::HandleTypeDefinitionFinishAsClassState() -> void { + HandleTypeDefinitionFinish(ParseNodeKind::ClassDefinition); +} + +auto Parser::HandleTypeDefinitionFinishAsInterfaceState() -> void { + HandleTypeDefinitionFinish(ParseNodeKind::InterfaceDefinition); +} + +auto Parser::HandleTypeDefinitionFinishAsNamedConstraintState() -> void { + HandleTypeDefinitionFinish(ParseNodeKind::NamedConstraintDefinition); +} + auto Parser::HandleVar(ParserState finish_state) -> void { PopAndDiscardState(); diff --git a/toolchain/parser/parser.h b/toolchain/parser/parser.h index 680db42dbd2c..ef8b064fd6b0 100644 --- a/toolchain/parser/parser.h +++ b/toolchain/parser/parser.h @@ -49,7 +49,9 @@ class Parser { // Supported return values for GetDeclarationContext. enum class DeclarationContext { File, // Top-level context. + Class, Interface, + NamedConstraint, }; // Helper class for tracing state_stack_ on crashes. @@ -260,6 +262,9 @@ class Parser { // This is expected to be called in cases which are close to a context. // Although it looks like it could be O(n) for state_stack_'s depth, valid // parses should only need to look down a couple steps. + // + // This currently assumes it's being called from within the declaration's + // DeclarationScopeLoop. auto GetDeclarationContext() -> DeclarationContext; // Handles error recovery in a declaration, particularly before any possible @@ -316,6 +321,15 @@ class Parser { // Handles the `;` after a keyword statement. auto HandleStatementKeywordFinish(ParseNodeKind node_kind) -> void; + // Handles processing of a type's ` {`. + auto HandleTypeIntroducer(ParseNodeKind introducer_kind, + ParseNodeKind declaration_kind, + ParseNodeKind definition_start_kind, + ParserState definition_finish_state) -> void; + + // Handles parsing after the declaration scope of a type. + auto HandleTypeDefinitionFinish(ParseNodeKind definition_kind) -> void; + // Handles VarAs(Semicolon|For). auto HandleVar(ParserState finish_state) -> void; diff --git a/toolchain/parser/parser_state.def b/toolchain/parser/parser_state.def index 3675db8e2d2f..aac8a73f4560 100644 --- a/toolchain/parser/parser_state.def +++ b/toolchain/parser/parser_state.def @@ -127,23 +127,26 @@ CARBON_PARSER_STATE(CodeBlockFinish) // // If `EndOfFile`: // (state done) +// If `Class`: +// 1. TypeIntroducerAsClass +// 2. DeclarationScopeLoop +// If `Constraint`: +// 1. TypeIntroducerAsNamedConstraint +// 2. DeclarationScopeLoop // If `Fn`: // 1. FunctionIntroducer -// 2. DeclarationLoop -// If `Package`: -// 1. Package -// 2. DeclarationLoop +// 2. DeclarationScopeLoop +// If `Interface`: +// 1. TypeIntroducerAsInterface +// 2. DeclarationScopeLoop // If `Semi`: -// 1. DeclarationLoop +// 1. DeclarationScopeLoop // If `Var`: // 1. Var -// 2. DeclarationLoop -// If `interface`: -// 1. InterfaceIntroducer -// 2. DeclarationLoop +// 2. DeclarationScopeLoop // Else: -// 1. DeclarationLoop -CARBON_PARSER_STATE(DeclarationLoop) +// 1. DeclarationScopeLoop +CARBON_PARSER_STATE(DeclarationScopeLoop) // Starts deduced parameter processing. // @@ -329,24 +332,6 @@ CARBON_PARSER_STATE(FunctionSignatureFinish) // (state done) CARBON_PARSER_STATE(FunctionDefinitionFinish) -// Handles parsing the body of an interface. -// -// If `CloseCurlyBrace`: -// (state done) -// Else: -// 1. InterfaceDefinitionLoop -CARBON_PARSER_STATE(InterfaceDefinitionLoop) - -// Handles processing of a intefaces's `interface {`. -// -// If `Semi`: -// (state done) -// If `OpenCurlyBrace`: -// 1. InterfaceDefinitionLoop -// Else: -// (state done) -CARBON_PARSER_STATE(InterfaceIntroducer) - // Handles `package`. // // Always: @@ -575,6 +560,27 @@ CARBON_PARSER_STATE(StatementWhileConditionFinish) // (state done) CARBON_PARSER_STATE(StatementWhileBlockFinish) +// Handles parsing after the declaration scope of a type. +// +// Always: +// (state done) +CARBON_PARSER_STATE(TypeDefinitionFinishAsClass) +CARBON_PARSER_STATE(TypeDefinitionFinishAsInterface) +CARBON_PARSER_STATE(TypeDefinitionFinishAsNamedConstraint) + +// Handles processing of a type's ` [;{]`. +// +// If `Semi`: +// (state done) +// If `OpenCurlyBrace`: +// 1. DeclarationScopeLoop +// 2. TypeDefinitionFinishAs(Class|Interface|NamedConstraint) +// Else: +// (state done) +CARBON_PARSER_STATE(TypeIntroducerAsClass) +CARBON_PARSER_STATE(TypeIntroducerAsInterface) +CARBON_PARSER_STATE(TypeIntroducerAsNamedConstraint) + // Handles the start of a `var`. // // Always: diff --git a/toolchain/parser/testdata/class/basic.carbon b/toolchain/parser/testdata/class/basic.carbon new file mode 100644 index 000000000000..e1c1723b7e0a --- /dev/null +++ b/toolchain/parser/testdata/class/basic.carbon @@ -0,0 +1,22 @@ +// 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 +// RUN: %{carbon-run-parser} +// CHECK:STDOUT: [ +// CHECK:STDOUT: {kind: 'ClassIntroducer', text: 'class'}, +// CHECK:STDOUT: {kind: 'DeclaredName', text: 'Foo'}, +// CHECK:STDOUT: {kind: 'ClassDefinitionStart', text: '{', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'DeclaredName', text: 'Baz'}, +// CHECK:STDOUT: {kind: 'ParameterListStart', text: '('}, +// CHECK:STDOUT: {kind: 'ParameterList', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDeclaration', text: ';', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'ClassDefinition', text: '}', subtree_size: 9}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] + +class Foo { + fn Baz(); +} diff --git a/toolchain/parser/testdata/function/declaration/fail_missing_name.carbon b/toolchain/parser/testdata/function/declaration/fail_missing_name.carbon index 7e45b1583cdb..63b4acb8f3c6 100644 --- a/toolchain/parser/testdata/function/declaration/fail_missing_name.carbon +++ b/toolchain/parser/testdata/function/declaration/fail_missing_name.carbon @@ -10,5 +10,5 @@ // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] -// CHECK:STDERR: {{.*}}/toolchain/parser/testdata/function/declaration/fail_missing_name.carbon:[[@LINE+1]]:4: Expected function name after `fn` keyword. +// CHECK:STDERR: {{.*}}/toolchain/parser/testdata/function/declaration/fail_missing_name.carbon:[[@LINE+1]]:4: `fn` introducer should be followed by a name. fn (); diff --git a/toolchain/parser/testdata/function/declaration/fail_only_fn_and_semi.carbon b/toolchain/parser/testdata/function/declaration/fail_only_fn_and_semi.carbon index 3c420ef2684f..8fef102a185e 100644 --- a/toolchain/parser/testdata/function/declaration/fail_only_fn_and_semi.carbon +++ b/toolchain/parser/testdata/function/declaration/fail_only_fn_and_semi.carbon @@ -10,5 +10,5 @@ // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] -// CHECK:STDERR: {{.*}}/toolchain/parser/testdata/function/declaration/fail_only_fn_and_semi.carbon:[[@LINE+1]]:3: Expected function name after `fn` keyword. +// CHECK:STDERR: {{.*}}/toolchain/parser/testdata/function/declaration/fail_only_fn_and_semi.carbon:[[@LINE+1]]:3: `fn` introducer should be followed by a name. fn; diff --git a/toolchain/parser/testdata/function/declaration/fail_repeated_fn_and_semi.carbon b/toolchain/parser/testdata/function/declaration/fail_repeated_fn_and_semi.carbon index 739cb527f1b7..980c19bcfa28 100644 --- a/toolchain/parser/testdata/function/declaration/fail_repeated_fn_and_semi.carbon +++ b/toolchain/parser/testdata/function/declaration/fail_repeated_fn_and_semi.carbon @@ -10,5 +10,5 @@ // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] -// CHECK:STDERR: {{.*}}/toolchain/parser/testdata/function/declaration/fail_repeated_fn_and_semi.carbon:[[@LINE+1]]:4: Expected function name after `fn` keyword. +// CHECK:STDERR: {{.*}}/toolchain/parser/testdata/function/declaration/fail_repeated_fn_and_semi.carbon:[[@LINE+1]]:4: `fn` introducer should be followed by a name. fn fn; diff --git a/toolchain/parser/testdata/function/declaration/fail_skip_indented_newline_until_outdent.carbon b/toolchain/parser/testdata/function/declaration/fail_skip_indented_newline_until_outdent.carbon index 8d996fec0dc1..8daaf1f63d18 100644 --- a/toolchain/parser/testdata/function/declaration/fail_skip_indented_newline_until_outdent.carbon +++ b/toolchain/parser/testdata/function/declaration/fail_skip_indented_newline_until_outdent.carbon @@ -15,7 +15,7 @@ // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] - // CHECK:STDERR: {{.*}}/toolchain/parser/testdata/function/declaration/fail_skip_indented_newline_until_outdent.carbon:[[@LINE+1]]:6: Expected function name after `fn` keyword. + // CHECK:STDERR: {{.*}}/toolchain/parser/testdata/function/declaration/fail_skip_indented_newline_until_outdent.carbon:[[@LINE+1]]:6: `fn` introducer should be followed by a name. fn (x, y, z) diff --git a/toolchain/parser/testdata/function/declaration/fail_skip_indented_newline_with_semi.carbon b/toolchain/parser/testdata/function/declaration/fail_skip_indented_newline_with_semi.carbon index c4e344202ca1..dd60e4e80588 100644 --- a/toolchain/parser/testdata/function/declaration/fail_skip_indented_newline_with_semi.carbon +++ b/toolchain/parser/testdata/function/declaration/fail_skip_indented_newline_with_semi.carbon @@ -15,7 +15,7 @@ // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] -// CHECK:STDERR: {{.*}}/toolchain/parser/testdata/function/declaration/fail_skip_indented_newline_with_semi.carbon:[[@LINE+1]]:4: Expected function name after `fn` keyword. +// CHECK:STDERR: {{.*}}/toolchain/parser/testdata/function/declaration/fail_skip_indented_newline_with_semi.carbon:[[@LINE+1]]:4: `fn` introducer should be followed by a name. fn (x, y, z); diff --git a/toolchain/parser/testdata/function/declaration/fail_skip_indented_newline_without_semi.carbon b/toolchain/parser/testdata/function/declaration/fail_skip_indented_newline_without_semi.carbon index 2a3ac2fd464f..8acf7cb3a310 100644 --- a/toolchain/parser/testdata/function/declaration/fail_skip_indented_newline_without_semi.carbon +++ b/toolchain/parser/testdata/function/declaration/fail_skip_indented_newline_without_semi.carbon @@ -15,7 +15,7 @@ // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] -// CHECK:STDERR: {{.*}}/toolchain/parser/testdata/function/declaration/fail_skip_indented_newline_without_semi.carbon:[[@LINE+1]]:4: Expected function name after `fn` keyword. +// CHECK:STDERR: {{.*}}/toolchain/parser/testdata/function/declaration/fail_skip_indented_newline_without_semi.carbon:[[@LINE+1]]:4: `fn` introducer should be followed by a name. fn (x, y, z) diff --git a/toolchain/parser/testdata/function/declaration/fail_skip_to_newline_without_semi.carbon b/toolchain/parser/testdata/function/declaration/fail_skip_to_newline_without_semi.carbon index ce55b6ae3935..f0695b501abe 100644 --- a/toolchain/parser/testdata/function/declaration/fail_skip_to_newline_without_semi.carbon +++ b/toolchain/parser/testdata/function/declaration/fail_skip_to_newline_without_semi.carbon @@ -15,6 +15,6 @@ // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] -// CHECK:STDERR: {{.*}}/toolchain/parser/testdata/function/declaration/fail_skip_to_newline_without_semi.carbon:[[@LINE+1]]:4: Expected function name after `fn` keyword. +// CHECK:STDERR: {{.*}}/toolchain/parser/testdata/function/declaration/fail_skip_to_newline_without_semi.carbon:[[@LINE+1]]:4: `fn` introducer should be followed by a name. fn () fn F(); diff --git a/toolchain/parser/testdata/function/declaration/fail_without_name_and_many_tokens_in_params.carbon b/toolchain/parser/testdata/function/declaration/fail_without_name_and_many_tokens_in_params.carbon index 2081857387b9..cf3792815103 100644 --- a/toolchain/parser/testdata/function/declaration/fail_without_name_and_many_tokens_in_params.carbon +++ b/toolchain/parser/testdata/function/declaration/fail_without_name_and_many_tokens_in_params.carbon @@ -10,5 +10,5 @@ // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] -// CHECK:STDERR: {{.*}}/toolchain/parser/testdata/function/declaration/fail_without_name_and_many_tokens_in_params.carbon:[[@LINE+1]]:4: Expected function name after `fn` keyword. +// CHECK:STDERR: {{.*}}/toolchain/parser/testdata/function/declaration/fail_without_name_and_many_tokens_in_params.carbon:[[@LINE+1]]:4: `fn` introducer should be followed by a name. fn (a tokens c d e f g h i j k l m n o p q r s t u v w x y z); diff --git a/toolchain/parser/testdata/generics/interface/fail_missing_name.carbon b/toolchain/parser/testdata/generics/interface/fail_missing_name.carbon index 70f181872064..f1d680181b4e 100644 --- a/toolchain/parser/testdata/generics/interface/fail_missing_name.carbon +++ b/toolchain/parser/testdata/generics/interface/fail_missing_name.carbon @@ -10,6 +10,6 @@ // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] -// CHECK:STDERR: {{.*}}/toolchain/parser/testdata/generics/interface/fail_missing_name.carbon:[[@LINE+1]]:11: Expected interface name after `interface` keyword. +// CHECK:STDERR: {{.*}}/toolchain/parser/testdata/generics/interface/fail_missing_name.carbon:[[@LINE+1]]:11: `interface` introducer should be followed by a name. interface { } diff --git a/toolchain/parser/testdata/generics/interface/fail_missing_open_curly.carbon b/toolchain/parser/testdata/generics/interface/fail_missing_open_curly.carbon index 90e287659cda..5014248967d3 100644 --- a/toolchain/parser/testdata/generics/interface/fail_missing_open_curly.carbon +++ b/toolchain/parser/testdata/generics/interface/fail_missing_open_curly.carbon @@ -14,8 +14,8 @@ // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] -// CHECK:STDERR: {{.*}}/toolchain/parser/testdata/generics/interface/fail_missing_open_curly.carbon:[[@LINE+1]]:15: Expected `{` to start interface definition. +// CHECK:STDERR: {{.*}}/toolchain/parser/testdata/generics/interface/fail_missing_open_curly.carbon:[[@LINE+1]]:15: `interface` should either end with a `;` for a declaration or have a `{ ... }` block for a definition. interface Bar Baz {} -// CHECK:STDERR: {{.*}}/toolchain/parser/testdata/generics/interface/fail_missing_open_curly.carbon:[[@LINE+1]]:14: Expected `{` to start interface definition. +// CHECK:STDERR: {{.*}}/toolchain/parser/testdata/generics/interface/fail_missing_open_curly.carbon:[[@LINE+1]]:14: `interface` should either end with a `;` for a declaration or have a `{ ... }` block for a definition. interface Foo diff --git a/toolchain/parser/testdata/generics/named_constraint/basic.carbon b/toolchain/parser/testdata/generics/named_constraint/basic.carbon new file mode 100644 index 000000000000..86db57d0b356 --- /dev/null +++ b/toolchain/parser/testdata/generics/named_constraint/basic.carbon @@ -0,0 +1,22 @@ +// 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 +// RUN: %{carbon-run-parser} +// CHECK:STDOUT: [ +// CHECK:STDOUT: {kind: 'NamedConstraintIntroducer', text: 'constraint'}, +// CHECK:STDOUT: {kind: 'DeclaredName', text: 'Foo'}, +// CHECK:STDOUT: {kind: 'NamedConstraintDefinitionStart', text: '{', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'DeclaredName', text: 'Baz'}, +// CHECK:STDOUT: {kind: 'ParameterListStart', text: '('}, +// CHECK:STDOUT: {kind: 'ParameterList', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDeclaration', text: ';', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'NamedConstraintDefinition', text: '}', subtree_size: 9}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] + +constraint Foo { + fn Baz(); +} diff --git a/toolchain/parser/testdata/generics/named_constraint/fail_no_impl_allowed.carbon b/toolchain/parser/testdata/generics/named_constraint/fail_no_impl_allowed.carbon new file mode 100644 index 000000000000..8abd470c7947 --- /dev/null +++ b/toolchain/parser/testdata/generics/named_constraint/fail_no_impl_allowed.carbon @@ -0,0 +1,33 @@ +// 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 +// RUN: %{not} %{carbon-run-parser} +// CHECK:STDOUT: [ +// CHECK:STDOUT: {kind: 'NamedConstraintIntroducer', text: 'constraint'}, +// CHECK:STDOUT: {kind: 'DeclaredName', text: 'Foo'}, +// CHECK:STDOUT: {kind: 'NamedConstraintDefinitionStart', text: '{', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'DeclaredName', text: 'Add'}, +// CHECK:STDOUT: {kind: 'DeducedParameterListStart', text: '['}, +// CHECK:STDOUT: {kind: 'SelfDeducedParameter', text: 'self'}, +// CHECK:STDOUT: {kind: 'SelfType', text: 'Self'}, +// CHECK:STDOUT: {kind: 'PatternBinding', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'DeducedParameterList', text: ']', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'ParameterListStart', text: '('}, +// CHECK:STDOUT: {kind: 'DeclaredName', text: 'b'}, +// CHECK:STDOUT: {kind: 'SelfType', text: 'Self'}, +// CHECK:STDOUT: {kind: 'PatternBinding', text: ':', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ParameterList', text: ')', subtree_size: 5}, +// CHECK:STDOUT: {kind: 'SelfType', text: 'Self'}, +// CHECK:STDOUT: {kind: 'ReturnType', text: '->', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDeclaration', text: 'fn', has_error: yes, subtree_size: 15}, +// CHECK:STDOUT: {kind: 'NamedConstraintDefinition', text: '}', subtree_size: 19}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] + +constraint Foo { + // CHECK:STDERR: {{.*}}/toolchain/parser/testdata/generics/named_constraint/fail_no_impl_allowed.carbon:[[@LINE+1]]:39: Method implementations are not allowed in interfaces. + fn Add[self: Self](b: Self) -> Self {} +} diff --git a/toolchain/semantics/semantics_parse_tree_handler.cpp b/toolchain/semantics/semantics_parse_tree_handler.cpp index 96ef44e91f45..5ad228a31600 100644 --- a/toolchain/semantics/semantics_parse_tree_handler.cpp +++ b/toolchain/semantics/semantics_parse_tree_handler.cpp @@ -362,6 +362,30 @@ auto SemanticsParseTreeHandler::HandleCallExpressionStart( return true; } +auto SemanticsParseTreeHandler::HandleClassDeclaration( + ParseTree::Node parse_node) -> bool { + emitter_->Emit(parse_node, SemanticsTodo, "HandleClassDeclaration"); + return false; +} + +auto SemanticsParseTreeHandler::HandleClassDefinition( + ParseTree::Node parse_node) -> bool { + emitter_->Emit(parse_node, SemanticsTodo, "HandleClassDefinition"); + return false; +} + +auto SemanticsParseTreeHandler::HandleClassDefinitionStart( + ParseTree::Node parse_node) -> bool { + emitter_->Emit(parse_node, SemanticsTodo, "HandleClassDefinitionStart"); + return false; +} + +auto SemanticsParseTreeHandler::HandleClassIntroducer( + ParseTree::Node parse_node) -> bool { + emitter_->Emit(parse_node, SemanticsTodo, "HandleClassIntroducer"); + return false; +} + auto SemanticsParseTreeHandler::HandleCodeBlock(ParseTree::Node parse_node) -> bool { emitter_->Emit(parse_node, SemanticsTodo, "HandleCodeBlock"); @@ -691,6 +715,31 @@ auto SemanticsParseTreeHandler::HandleNameReference(ParseTree::Node parse_node) return true; } +auto SemanticsParseTreeHandler::HandleNamedConstraintDeclaration( + ParseTree::Node parse_node) -> bool { + emitter_->Emit(parse_node, SemanticsTodo, "HandleNamedConstraintDeclaration"); + return false; +} + +auto SemanticsParseTreeHandler::HandleNamedConstraintDefinition( + ParseTree::Node parse_node) -> bool { + emitter_->Emit(parse_node, SemanticsTodo, "HandleNamedConstraintDefinition"); + return false; +} + +auto SemanticsParseTreeHandler::HandleNamedConstraintDefinitionStart( + ParseTree::Node parse_node) -> bool { + emitter_->Emit(parse_node, SemanticsTodo, + "HandleNamedConstraintDefinitionStart"); + return false; +} + +auto SemanticsParseTreeHandler::HandleNamedConstraintIntroducer( + ParseTree::Node parse_node) -> bool { + emitter_->Emit(parse_node, SemanticsTodo, "HandleNamedConstraintIntroducer"); + return false; +} + auto SemanticsParseTreeHandler::HandlePackageApi(ParseTree::Node parse_node) -> bool { emitter_->Emit(parse_node, SemanticsTodo, "HandlePackageApi");