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");