Skip to content

Commit

Permalink
Add parsing for 'fn destroy'
Browse files Browse the repository at this point in the history
  • Loading branch information
jonmeow committed Feb 28, 2025
1 parent 6d6987d commit 67dc09f
Show file tree
Hide file tree
Showing 13 changed files with 211 additions and 45 deletions.
10 changes: 10 additions & 0 deletions toolchain/check/handle_name.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,16 @@ auto HandleParseNode(Context& context, Parse::IdentifierNameExprId node_id)
return true;
}

auto HandleParseNode(Context& context,
Parse::KeywordNameNotBeforeParamsId node_id) -> bool {
return context.TODO(node_id, "KeywordNameNotBeforeParamsId");
}

auto HandleParseNode(Context& context, Parse::KeywordNameBeforeParamsId node_id)
-> bool {
return context.TODO(node_id, "KeywordNameBeforeParamsId");
}

auto HandleParseNode(Context& context, Parse::BaseNameId node_id) -> bool {
context.node_stack().Push(node_id, SemIR::NameId::Base);
return true;
Expand Down
3 changes: 1 addition & 2 deletions toolchain/check/name_component.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,7 @@ auto PopNameComponent(Context& context, SemIR::InstId return_slot_pattern_id)
}

auto [name_loc_id, name_id] =
context.node_stack()
.PopWithNodeId<Parse::NodeCategory::NonExprIdentifierName>();
context.node_stack().PopWithNodeId<Parse::NodeCategory::NonExprName>();

return {
.name_loc_id = name_loc_id,
Expand Down
6 changes: 3 additions & 3 deletions toolchain/check/node_stack.h
Original file line number Diff line number Diff line change
Expand Up @@ -377,9 +377,9 @@ class NodeStack {
Id::KindFor<SemIR::InstId>());
set_id_if_category_is(Parse::NodeCategory::Expr,
Id::KindFor<SemIR::InstId>());
set_id_if_category_is(Parse::NodeCategory::MemberName |
Parse::NodeCategory::NonExprIdentifierName,
Id::KindFor<SemIR::NameId>());
set_id_if_category_is(
Parse::NodeCategory::MemberName | Parse::NodeCategory::NonExprName,
Id::KindFor<SemIR::NameId>());
set_id_if_category_is(Parse::NodeCategory::ImplAs,
Id::KindFor<SemIR::InstId>());
set_id_if_category_is(Parse::NodeCategory::Decl |
Expand Down
103 changes: 103 additions & 0 deletions toolchain/check/testdata/class/no_prelude/destroy.carbon
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// AUTOUPDATE
// TIP: To test this file alone, run:
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/no_prelude/destroy.carbon
// TIP: To dump output, run:
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/no_prelude/destroy.carbon

// --- fail_todo_basic.carbon

class C {
// CHECK:STDERR: fail_todo_basic.carbon:[[@LINE+4]]:6: error: semantics TODO: `KeywordNameBeforeParamsId` [SemanticsTodo]
// CHECK:STDERR: fn destroy[self: Self]();
// CHECK:STDERR: ^~~~~~~
// CHECK:STDERR:
fn destroy[self: Self]();
}

// --- fail_todo_addr.carbon
// CHECK:STDERR: fail_todo_addr.carbon: error: `Main//default` previously provided by `fail_todo_basic.carbon` [DuplicateMainApi]
// CHECK:STDERR:

class C {
// CHECK:STDERR: fail_todo_addr.carbon:[[@LINE+4]]:6: error: semantics TODO: `KeywordNameBeforeParamsId` [SemanticsTodo]
// CHECK:STDERR: fn destroy[addr self: Self*]();
// CHECK:STDERR: ^~~~~~~
// CHECK:STDERR:
fn destroy[addr self: Self*]();
}

// --- fail_todo_class_function.carbon
// CHECK:STDERR: fail_todo_class_function.carbon: error: `Main//default` previously provided by `fail_todo_basic.carbon` [DuplicateMainApi]
// CHECK:STDERR:

class C {
// CHECK:STDERR: fail_todo_class_function.carbon:[[@LINE+4]]:6: error: semantics TODO: `KeywordNameBeforeParamsId` [SemanticsTodo]
// CHECK:STDERR: fn destroy();
// CHECK:STDERR: ^~~~~~~
// CHECK:STDERR:
fn destroy();
}

// --- fail_todo_wrong_scope.carbon
// CHECK:STDERR: fail_todo_wrong_scope.carbon: error: `Main//default` previously provided by `fail_todo_basic.carbon` [DuplicateMainApi]
// CHECK:STDERR:

// CHECK:STDERR: fail_todo_wrong_scope.carbon:[[@LINE+4]]:4: error: semantics TODO: `KeywordNameBeforeParamsId` [SemanticsTodo]
// CHECK:STDERR: fn destroy();
// CHECK:STDERR: ^~~~~~~
// CHECK:STDERR:
fn destroy();

// CHECK:STDOUT: --- fail_todo_basic.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %C: type = class_type @C [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {}
// CHECK:STDOUT:
// CHECK:STDOUT: class @C {
// CHECK:STDOUT: complete_type_witness = invalid
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = constants.%C
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- fail_todo_addr.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %C: type = class_type @C [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {}
// CHECK:STDOUT:
// CHECK:STDOUT: class @C {
// CHECK:STDOUT: complete_type_witness = invalid
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = constants.%C
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- fail_todo_class_function.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %C: type = class_type @C [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {}
// CHECK:STDOUT:
// CHECK:STDOUT: class @C {
// CHECK:STDOUT: complete_type_witness = invalid
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = constants.%C
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- fail_todo_wrong_scope.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: file {}
// CHECK:STDOUT:
2 changes: 1 addition & 1 deletion toolchain/lex/token_kind.def
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ CARBON_KEYWORD_TOKEN(Const, "const")
CARBON_KEYWORD_TOKEN(Continue, "continue")
CARBON_KEYWORD_TOKEN(Core, "Core")
CARBON_KEYWORD_TOKEN(Default, "default")
CARBON_KEYWORD_TOKEN(Destructor, "destructor")
CARBON_KEYWORD_TOKEN(Destroy, "destroy")
CARBON_KEYWORD_TOKEN(Else, "else")
CARBON_KEYWORD_TOKEN(Extend, "extend")
CARBON_KEYWORD_TOKEN(Extern, "extern")
Expand Down
1 change: 1 addition & 0 deletions toolchain/parse/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ cc_library(
"//toolchain/base:shared_value_stores",
"//toolchain/diagnostics:diagnostic_emitter",
"//toolchain/diagnostics:format_providers",
"//toolchain/lex:token_index",
"//toolchain/lex:token_kind",
"//toolchain/lex:tokenized_buffer",
],
Expand Down
79 changes: 48 additions & 31 deletions toolchain/parse/handle_decl_name_and_params.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,67 +2,84 @@
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include "toolchain/lex/token_index.h"
#include "toolchain/parse/context.h"
#include "toolchain/parse/handle.h"

namespace Carbon::Parse {

auto HandleDeclNameAndParams(Context& context) -> void {
auto state = context.PopState();

auto identifier = context.ConsumeIf(Lex::TokenKind::Identifier);
if (!identifier) {
Lex::TokenIndex token = *context.position();
if (context.tokens().GetKind(token) == Lex::TokenKind::FileEnd) {
// The end of file is an unhelpful diagnostic location. Instead, use the
// introducer token.
token = state.token;
}
if (state.token == *context.position()) {
CARBON_DIAGNOSTIC(ExpectedDeclNameAfterPeriod, Error,
"`.` should be followed by a name");
context.emitter().Emit(token, ExpectedDeclNameAfterPeriod);
} else {
CARBON_DIAGNOSTIC(ExpectedDeclName, Error,
"`{0}` introducer should be followed by a name",
Lex::TokenKind);
context.emitter().Emit(token, ExpectedDeclName,
context.tokens().GetKind(state.token));
}
context.ReturnErrorOnState();
context.AddInvalidParse(*context.position());
return;
}

// Adds a leaf node for the name, and updates the state stack for parameter
// handling.
static auto HandleName(Context& context, Context::StateStackEntry state,
Lex::TokenIndex name_token,
NodeKind not_before_params_kind,
NodeKind before_params_kind) -> void {
switch (context.PositionKind()) {
case Lex::TokenKind::Period:
context.AddLeafNode(NodeKind::IdentifierNameNotBeforeParams, *identifier);
context.AddLeafNode(not_before_params_kind, name_token);
context.AddNode(NodeKind::NameQualifierWithoutParams,
context.ConsumeChecked(Lex::TokenKind::Period),
state.has_error);
context.PushState(State::DeclNameAndParams);
break;

case Lex::TokenKind::OpenSquareBracket:
context.AddLeafNode(NodeKind::IdentifierNameBeforeParams, *identifier);
context.AddLeafNode(before_params_kind, name_token);
state.state = State::DeclNameAndParamsAfterImplicit;
context.PushState(state);
context.PushState(State::PatternListAsImplicit);
break;

case Lex::TokenKind::OpenParen:
context.AddLeafNode(NodeKind::IdentifierNameBeforeParams, *identifier);
context.AddLeafNode(before_params_kind, name_token);
state.state = State::DeclNameAndParamsAfterParams;
context.PushState(state);
context.PushState(State::PatternListAsExplicit);
break;

default:
context.AddLeafNode(NodeKind::IdentifierNameNotBeforeParams, *identifier);
context.AddLeafNode(not_before_params_kind, name_token);
break;
}
}

auto HandleDeclNameAndParams(Context& context) -> void {
auto state = context.PopState();

if (auto identifier = context.ConsumeIf(Lex::TokenKind::Identifier)) {
HandleName(context, state, *identifier,
NodeKind::IdentifierNameNotBeforeParams,
NodeKind::IdentifierNameBeforeParams);
return;
}

if (auto keyword = context.ConsumeIf(Lex::TokenKind::Destroy)) {
HandleName(context, state, *keyword, NodeKind::KeywordNameNotBeforeParams,
NodeKind::KeywordNameBeforeParams);
return;
}

Lex::TokenIndex token = *context.position();
if (context.tokens().GetKind(token) == Lex::TokenKind::FileEnd) {
// The end of file is an unhelpful diagnostic location. Instead, use the
// introducer token.
token = state.token;
}
if (state.token == *context.position()) {
CARBON_DIAGNOSTIC(ExpectedDeclNameAfterPeriod, Error,
"`.` should be followed by a name");
context.emitter().Emit(token, ExpectedDeclNameAfterPeriod);
} else {
CARBON_DIAGNOSTIC(ExpectedDeclName, Error,
"`{0}` introducer should be followed by a name",
Lex::TokenKind);
context.emitter().Emit(token, ExpectedDeclName,
context.tokens().GetKind(state.token));
}
context.ReturnErrorOnState();
context.AddInvalidParse(*context.position());
}

auto HandleDeclNameAndParamsAfterImplicit(Context& context) -> void {
auto state = context.PopState();

Expand Down
2 changes: 1 addition & 1 deletion toolchain/parse/node_category.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();
X(MemberExpr) \
X(MemberName) \
X(Modifier) \
X(NonExprIdentifierName) \
X(NonExprName) \
X(PackageName) \
X(Pattern) \
X(Requirement) \
Expand Down
3 changes: 1 addition & 2 deletions toolchain/parse/node_ids.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,7 @@ using AnyPatternId = NodeIdInCategory<NodeCategory::Pattern>;
using AnyStatementId =
NodeIdInCategory<NodeCategory::Statement | NodeCategory::Decl>;
using AnyRequirementId = NodeIdInCategory<NodeCategory::Requirement>;
using AnyNonExprIdentifierNameId =
NodeIdInCategory<NodeCategory::NonExprIdentifierName>;
using AnyNonExprNameId = NodeIdInCategory<NodeCategory::NonExprName>;
using AnyPackageNameId = NodeIdInCategory<NodeCategory::PackageName>;

// NodeId with kind that matches one of the `T::Kind`s.
Expand Down
3 changes: 3 additions & 0 deletions toolchain/parse/node_kind.def
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ CARBON_PARSE_NODE_KIND(EmptyDecl)
CARBON_PARSE_NODE_KIND(IdentifierNameNotBeforeParams)
CARBON_PARSE_NODE_KIND(IdentifierNameBeforeParams)

CARBON_PARSE_NODE_KIND(KeywordNameNotBeforeParams)
CARBON_PARSE_NODE_KIND(KeywordNameBeforeParams)

CARBON_PARSE_NODE_KIND(IdentifierNameExpr)

CARBON_PARSE_NODE_KIND(SelfValueName)
Expand Down
28 changes: 28 additions & 0 deletions toolchain/parse/testdata/function/declaration.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ fn foo(a: i32, b: i32);

fn foo() -> u32;

// --- keyword.carbon

fn destroy() {}

// --- keyword_no_params.carbon

fn destroy {}

// --- impl_fn.carbon

impl fn F();
Expand Down Expand Up @@ -256,6 +264,26 @@ 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);
// CHECK:STDOUT: {kind: 'FunctionDecl', text: ';', subtree_size: 7},
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
// CHECK:STDOUT: ]
// CHECK:STDOUT: - filename: keyword.carbon
// CHECK:STDOUT: parse_tree: [
// CHECK:STDOUT: {kind: 'FileStart', text: ''},
// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'},
// CHECK:STDOUT: {kind: 'KeywordNameBeforeParams', text: 'destroy'},
// CHECK:STDOUT: {kind: 'ExplicitParamListStart', text: '('},
// CHECK:STDOUT: {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 6},
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
// CHECK:STDOUT: ]
// CHECK:STDOUT: - filename: keyword_no_params.carbon
// CHECK:STDOUT: parse_tree: [
// CHECK:STDOUT: {kind: 'FileStart', text: ''},
// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'},
// CHECK:STDOUT: {kind: 'KeywordNameNotBeforeParams', text: 'destroy'},
// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 3},
// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 4},
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
// CHECK:STDOUT: ]
// CHECK:STDOUT: - filename: impl_fn.carbon
// CHECK:STDOUT: parse_tree: [
// CHECK:STDOUT: {kind: 'FileStart', text: ''},
Expand Down
14 changes: 10 additions & 4 deletions toolchain/parse/typed_nodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,13 +131,19 @@ using EmptyDecl =
// to be followed by parameters.
using IdentifierNameBeforeParams =
LeafNode<NodeKind::IdentifierNameBeforeParams, Lex::IdentifierTokenIndex,
NodeCategory::MemberName | NodeCategory::NonExprIdentifierName>;
NodeCategory::MemberName | NodeCategory::NonExprName>;
using KeywordNameBeforeParams =
LeafNode<NodeKind::KeywordNameBeforeParams, Lex::TokenIndex,
NodeCategory::MemberName | NodeCategory::NonExprName>;

// A name in a non-expression context, such as a declaration, that is known
// to not be followed by parameters.
using IdentifierNameNotBeforeParams =
LeafNode<NodeKind::IdentifierNameNotBeforeParams, Lex::IdentifierTokenIndex,
NodeCategory::MemberName | NodeCategory::NonExprIdentifierName>;
NodeCategory::MemberName | NodeCategory::NonExprName>;
using KeywordNameNotBeforeParams =
LeafNode<NodeKind::KeywordNameNotBeforeParams, Lex::TokenIndex,
NodeCategory::MemberName | NodeCategory::NonExprName>;

// A name in an expression context.
using IdentifierNameExpr =
Expand Down Expand Up @@ -188,7 +194,7 @@ struct DeclName {
llvm::SmallVector<
NodeIdOneOf<NameQualifierWithParams, NameQualifierWithoutParams>>
qualifiers;
AnyNonExprIdentifierNameId name;
AnyNonExprNameId name;
std::optional<ImplicitParamListId> implicit_params;
std::optional<ExplicitParamListId> params;
};
Expand Down Expand Up @@ -1157,7 +1163,7 @@ struct ChoiceDefinition {

ChoiceDefinitionStartId signature;
struct Alternative {
AnyNonExprIdentifierNameId name;
AnyNonExprNameId name;
std::optional<ExplicitParamListId> parameters;
};
CommaSeparatedList<Alternative, ChoiceAlternativeListCommaId> alternatives;
Expand Down
2 changes: 1 addition & 1 deletion toolchain/parse/typed_nodes_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ Optional [^:]*: found
Optional [^:]*: begin
NodeIdForKind error: wrong kind IdentifierNameBeforeParams, expected ImplicitParamList
Optional [^:]*: missing
NodeIdInCategory NonExprIdentifierName: kind IdentifierNameBeforeParams consumed
NodeIdInCategory NonExprName: kind IdentifierNameBeforeParams consumed
Vector: begin
NodeIdOneOf NameQualifierWithParams or NameQualifierWithoutParams: NameQualifierWithoutParams consumed
NodeIdOneOf error: wrong kind AbstractModifier, expected NameQualifierWithParams or NameQualifierWithoutParams
Expand Down

0 comments on commit 67dc09f

Please sign in to comment.