From c5b7ff212b94e758a40491b4433d34827ce07342 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Tue, 9 Apr 2024 15:27:33 +0200 Subject: [PATCH] Parsing support for `adapt` declarations. (#3871) --- toolchain/check/handle_class.cpp | 12 ++ toolchain/parse/handle_adapt.cpp | 32 ++++ toolchain/parse/handle_decl_scope_loop.cpp | 7 + toolchain/parse/node_kind.def | 9 ++ toolchain/parse/state.def | 17 +- toolchain/parse/testdata/class/adapt.carbon | 171 ++++++++++++++++++++ toolchain/parse/typed_nodes.h | 14 ++ 7 files changed, 261 insertions(+), 1 deletion(-) create mode 100644 toolchain/parse/handle_adapt.cpp create mode 100644 toolchain/parse/testdata/class/adapt.carbon diff --git a/toolchain/check/handle_class.cpp b/toolchain/check/handle_class.cpp index 47fba7a4e2e4..c686106f620c 100644 --- a/toolchain/check/handle_class.cpp +++ b/toolchain/check/handle_class.cpp @@ -172,6 +172,18 @@ auto HandleClassDefinitionStart(Context& context, return true; } +auto HandleAdaptIntroducer(Context& /*context*/, + Parse::AdaptIntroducerId /*node_id*/) -> bool { + return true; +} + +auto HandleAdaptDecl(Context& context, Parse::AdaptDeclId /*node_id*/) -> bool { + auto adapted_expr_id = context.node_stack().PopExpr(); + (void)adapted_expr_id; + // TODO: Process the `adapt` declaration. + return true; +} + auto HandleBaseIntroducer(Context& context, Parse::BaseIntroducerId /*node_id*/) -> bool { context.decl_state_stack().Push(DeclState::Base); diff --git a/toolchain/parse/handle_adapt.cpp b/toolchain/parse/handle_adapt.cpp new file mode 100644 index 000000000000..9385c5760fef --- /dev/null +++ b/toolchain/parse/handle_adapt.cpp @@ -0,0 +1,32 @@ +// 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/parse/context.h" + +namespace Carbon::Parse { + +// Handles processing of a complete `adapt T` declaration. +auto HandleAdaptDecl(Context& context) -> void { + // TODO: This is identical to HandleBaseDecl other than the `NodeKind`, + // and very similar to `HandleNamespaceFinish` and `HandleAliasFinish`. + // We should factor out this common work. + auto state = context.PopState(); + + auto semi = context.ConsumeIf(Lex::TokenKind::Semi); + if (!semi && !state.has_error) { + context.EmitExpectedDeclSemi(context.tokens().GetKind(state.token)); + state.has_error = true; + } + + if (state.has_error) { + context.RecoverFromDeclError(state, NodeKind::AdaptDecl, + /*skip_past_likely_end=*/true); + return; + } + + context.AddNode(NodeKind::AdaptDecl, *semi, state.subtree_start, + state.has_error); +} + +} // namespace Carbon::Parse diff --git a/toolchain/parse/handle_decl_scope_loop.cpp b/toolchain/parse/handle_decl_scope_loop.cpp index bd51952c8ec6..1d549de557ae 100644 --- a/toolchain/parse/handle_decl_scope_loop.cpp +++ b/toolchain/parse/handle_decl_scope_loop.cpp @@ -110,6 +110,12 @@ static auto HandleBaseAsDecl(Context& context, Context::StateStackEntry state) static auto TryHandleAsDecl(Context& context, Context::StateStackEntry state, bool saw_modifier) -> bool { switch (context.PositionKind()) { + case Lex::TokenKind::Adapt: { + ApplyIntroducer(context, state, NodeKind::AdaptIntroducer, + State::AdaptDecl); + context.PushState(State::Expr); + return true; + } case Lex::TokenKind::Alias: { ApplyIntroducer(context, state, NodeKind::AliasIntroducer, State::Alias); return true; @@ -199,6 +205,7 @@ static auto ResolveAmbiguousTokenAsDeclaration(Context& context, // also modifiers (such as `base`). Other introducer tokens need to be // added by hand. switch (context.PositionKind(Lookahead::NextToken)) { + case Lex::TokenKind::Adapt: case Lex::TokenKind::Alias: case Lex::TokenKind::Class: case Lex::TokenKind::Constraint: diff --git a/toolchain/parse/node_kind.def b/toolchain/parse/node_kind.def index 5e35a0191f65..71562902b68a 100644 --- a/toolchain/parse/node_kind.def +++ b/toolchain/parse/node_kind.def @@ -664,6 +664,15 @@ CARBON_PARSE_NODE_KIND_BRACKET(ClassDefinition, ClassDefinitionStart, CARBON_PARSE_NODE_KIND_BRACKET(ClassDecl, ClassIntroducer, CARBON_IF_VALID(Semi)) +// `adapt`: +// AdaptIntroducer +// _repeated_ _external_: modifier +// _external_: expression +// AdaptDecl +CARBON_PARSE_NODE_KIND_CHILD_COUNT(AdaptIntroducer, 0, Adapt) +CARBON_PARSE_NODE_KIND_BRACKET(AdaptDecl, AdaptIntroducer, + CARBON_IF_VALID(Semi)) + // `base`: // BaseIntroducer // _repeated_ _external_: modifier diff --git a/toolchain/parse/state.def b/toolchain/parse/state.def index 0cbd789691bf..036054e57af7 100644 --- a/toolchain/parse/state.def +++ b/toolchain/parse/state.def @@ -307,6 +307,12 @@ CARBON_PARSE_STATE(DeclNameAndParamsAfterImplicit) // ^~~~~~~ // 1. DeclScopeLoop // +// adapt ... +// ^~~~~ +// 1. Expr +// 2. AdaptDecl +// 3. DeclScopeLoop +// // alias ... // ^~~~~ // 1. Alias @@ -1191,12 +1197,21 @@ CARBON_PARSE_STATE_VARIANTS3(TypeAfterIntroducer, Class, Interface, CARBON_PARSE_STATE_VARIANTS4(DeclOrDefinition, Class, Impl, Interface, NamedConstraint) +// Handles processing of a completed `adapt T` declaration. +// +// adapt T ; +// ^ +// adapt T ??? ; +// ^~~~~ +// (state done) +CARBON_PARSE_STATE(AdaptDecl) + // Handles processing of a completed `base: B` declaration. // // base: B ; // ^ // base: B ??? ; -// ^ +// ^~~~~ // (state done) CARBON_PARSE_STATE(BaseDecl) diff --git a/toolchain/parse/testdata/class/adapt.carbon b/toolchain/parse/testdata/class/adapt.carbon new file mode 100644 index 000000000000..2fcc5c694404 --- /dev/null +++ b/toolchain/parse/testdata/class/adapt.carbon @@ -0,0 +1,171 @@ +// 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 + +// --- adapt.carbon + +class A { + adapt (); +} + +class B { + adapt A; +} + +class C { + adapt i32; +} + +class D { + extend adapt {.a: i32, .b: i32}; +} + +class E { + adapt GetType(42); +} + +// This is syntactically OK but semantically invalid. +adapt i32; + +// --- fail_bad_syntax.carbon + +class A { + // CHECK:STDERR: fail_bad_syntax.carbon:[[@LINE+4]]:11: ERROR: `adapt` declarations must end with a `;`. + // CHECK:STDERR: adapt 1 trailing_token; + // CHECK:STDERR: ^~~~~~~~~~~~~~ + // CHECK:STDERR: + adapt 1 trailing_token; +} + +class B { + // CHECK:STDERR: fail_bad_syntax.carbon:[[@LINE+4]]:8: ERROR: Expected expression. + // CHECK:STDERR: adapt; + // CHECK:STDERR: ^ + // CHECK:STDERR: + adapt; +} + +class C { + adapt +// CHECK:STDERR: fail_bad_syntax.carbon:[[@LINE+4]]:1: ERROR: Expected expression. +// CHECK:STDERR: } +// CHECK:STDERR: ^ +// CHECK:STDERR: +} + +class D { + // CHECK:STDERR: fail_bad_syntax.carbon:[[@LINE+4]]:9: ERROR: Expected expression. + // CHECK:STDERR: adapt class; + // CHECK:STDERR: ^~~~~ + // CHECK:STDERR: + adapt class; +} + +fn F() { + // CHECK:STDERR: fail_bad_syntax.carbon:[[@LINE+3]]:3: ERROR: Expected expression. + // CHECK:STDERR: adapt i32; + // CHECK:STDERR: ^~~~~ + adapt i32; +} + +// CHECK:STDOUT: - filename: adapt.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'ClassIntroducer', text: 'class'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'A'}, +// CHECK:STDOUT: {kind: 'ClassDefinitionStart', text: '{', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'AdaptIntroducer', text: 'adapt'}, +// CHECK:STDOUT: {kind: 'TupleLiteralStart', text: '('}, +// CHECK:STDOUT: {kind: 'TupleLiteral', text: ')', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'AdaptDecl', text: ';', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'ClassDefinition', text: '}', subtree_size: 8}, +// CHECK:STDOUT: {kind: 'ClassIntroducer', text: 'class'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'B'}, +// CHECK:STDOUT: {kind: 'ClassDefinitionStart', text: '{', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'AdaptIntroducer', text: 'adapt'}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'A'}, +// CHECK:STDOUT: {kind: 'AdaptDecl', text: ';', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ClassDefinition', text: '}', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'ClassIntroducer', text: 'class'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'C'}, +// CHECK:STDOUT: {kind: 'ClassDefinitionStart', text: '{', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'AdaptIntroducer', text: 'adapt'}, +// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, +// CHECK:STDOUT: {kind: 'AdaptDecl', text: ';', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ClassDefinition', text: '}', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'ClassIntroducer', text: 'class'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'D'}, +// CHECK:STDOUT: {kind: 'ClassDefinitionStart', text: '{', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'AdaptIntroducer', text: 'adapt'}, +// CHECK:STDOUT: {kind: 'ExtendModifier', text: 'extend'}, +// CHECK:STDOUT: {kind: 'StructTypeLiteralStart', text: '{'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'a'}, +// CHECK:STDOUT: {kind: 'StructFieldDesignator', text: '.', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, +// CHECK:STDOUT: {kind: 'StructTypeField', text: ':', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'StructComma', text: ','}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'b'}, +// CHECK:STDOUT: {kind: 'StructFieldDesignator', text: '.', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, +// CHECK:STDOUT: {kind: 'StructTypeField', text: ':', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'StructTypeLiteral', text: '}', subtree_size: 11}, +// CHECK:STDOUT: {kind: 'AdaptDecl', text: ';', subtree_size: 14}, +// CHECK:STDOUT: {kind: 'ClassDefinition', text: '}', subtree_size: 18}, +// CHECK:STDOUT: {kind: 'ClassIntroducer', text: 'class'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'E'}, +// CHECK:STDOUT: {kind: 'ClassDefinitionStart', text: '{', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'AdaptIntroducer', text: 'adapt'}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'GetType'}, +// CHECK:STDOUT: {kind: 'CallExprStart', text: '(', subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IntLiteral', text: '42'}, +// CHECK:STDOUT: {kind: 'CallExpr', text: ')', subtree_size: 4}, +// CHECK:STDOUT: {kind: 'AdaptDecl', text: ';', subtree_size: 6}, +// CHECK:STDOUT: {kind: 'ClassDefinition', text: '}', subtree_size: 10}, +// CHECK:STDOUT: {kind: 'AdaptIntroducer', text: 'adapt'}, +// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'}, +// CHECK:STDOUT: {kind: 'AdaptDecl', text: ';', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: fail_bad_syntax.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'ClassIntroducer', text: 'class'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'A'}, +// CHECK:STDOUT: {kind: 'ClassDefinitionStart', text: '{', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'AdaptIntroducer', text: 'adapt'}, +// CHECK:STDOUT: {kind: 'IntLiteral', text: '1'}, +// CHECK:STDOUT: {kind: 'AdaptDecl', text: ';', has_error: yes, subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ClassDefinition', text: '}', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'ClassIntroducer', text: 'class'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'B'}, +// CHECK:STDOUT: {kind: 'ClassDefinitionStart', text: '{', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'AdaptIntroducer', text: 'adapt'}, +// CHECK:STDOUT: {kind: 'InvalidParse', text: ';', has_error: yes}, +// CHECK:STDOUT: {kind: 'AdaptDecl', text: ';', has_error: yes, subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ClassDefinition', text: '}', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'ClassIntroducer', text: 'class'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'C'}, +// CHECK:STDOUT: {kind: 'ClassDefinitionStart', text: '{', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'AdaptIntroducer', text: 'adapt'}, +// CHECK:STDOUT: {kind: 'InvalidParse', text: '}', has_error: yes}, +// CHECK:STDOUT: {kind: 'AdaptDecl', text: 'adapt', has_error: yes, subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ClassDefinition', text: '}', subtree_size: 7}, +// CHECK:STDOUT: {kind: 'ClassIntroducer', text: 'class'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'D'}, +// CHECK:STDOUT: {kind: 'ClassDefinitionStart', text: '{', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'AdaptIntroducer', text: 'adapt'}, +// CHECK:STDOUT: {kind: 'InvalidParse', text: 'class', has_error: yes}, +// CHECK:STDOUT: {kind: 'AdaptDecl', text: ';', has_error: yes, subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ClassDefinition', text: '}', subtree_size: 7}, +// 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: 'InvalidParse', text: 'adapt', has_error: yes}, +// CHECK:STDOUT: {kind: 'ExprStatement', text: ';', has_error: yes, subtree_size: 2}, +// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 8}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] diff --git a/toolchain/parse/typed_nodes.h b/toolchain/parse/typed_nodes.h index df2b3c130a7d..431f93971841 100644 --- a/toolchain/parse/typed_nodes.h +++ b/toolchain/parse/typed_nodes.h @@ -904,6 +904,20 @@ struct ClassDefinition { llvm::SmallVector members; }; +// Adapter declaration +// ------------------- + +// `adapt` +using AdaptIntroducer = LeafNode; +// `adapt SomeType;` +struct AdaptDecl { + static constexpr auto Kind = NodeKind::AdaptDecl.Define(NodeCategory::Decl); + + AdaptIntroducerId introducer; + llvm::SmallVector modifiers; + AnyExprId adapted_type; +}; + // Base class declaration // ----------------------