[parse] Implement initial parsing support for Lambda expressions (#6583)

This adds the necessary parser infrastructure to recognize and parse
lambda expressions in Carbon.

Key changes:
- Added  and  Parse Node Kinds.
- Updated  to use  to accommodate the growing number of node kinds.
- Implemented parser states and handlers for lambda syntax ( or ).
- Added  structure to .
- Added diagnostics for missing lambda bodies.
- Added a stub in  phase to defer semantic analysis using .
- Added parser tests for lambdas.
This commit is contained in:
Burak Emir
2026-01-14 16:58:15 +00:00
committed by GitHub
parent 83651bb9ee
commit 80639a02f0
19 changed files with 496 additions and 15 deletions
+5
View File
@@ -688,4 +688,9 @@ auto HandleParseNode(Context& context,
return true;
}
auto HandleParseNode(Context& context, Parse::FunctionTerseDefinitionId node_id)
-> bool {
return context.TODO(node_id, "HandleFunctionTerseDefinition");
}
} // namespace Carbon::Check
+24
View File
@@ -0,0 +1,24 @@
// 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/check/context.h"
#include "toolchain/check/handle.h"
namespace Carbon::Check {
auto HandleParseNode(Context& context, Parse::LambdaIntroducerId node_id)
-> bool {
return context.TODO(node_id, "HandleLambdaIntroducer");
}
auto HandleParseNode(Context& context, Parse::LambdaId node_id) -> bool {
return context.TODO(node_id, "HandleLambda");
}
auto HandleParseNode(Context& context, Parse::TerseBodyArrowId node_id)
-> bool {
return context.TODO(node_id, "HandleTerseBodyArrow");
}
} // namespace Carbon::Check
+2
View File
@@ -462,6 +462,7 @@ class NodeStack {
case Parse::NodeKind::ImplicitParamListStart:
case Parse::NodeKind::ImplIntroducer:
case Parse::NodeKind::InterfaceIntroducer:
case Parse::NodeKind::LambdaIntroducer:
case Parse::NodeKind::LetInitializer:
case Parse::NodeKind::LetIntroducer:
case Parse::NodeKind::NamedConstraintIntroducer:
@@ -534,6 +535,7 @@ class NodeStack {
case Parse::NodeKind::StructLiteralComma:
case Parse::NodeKind::StructFieldDesignator:
case Parse::NodeKind::StructTypeLiteralComma:
case Parse::NodeKind::TerseBodyArrow:
case Parse::NodeKind::TupleLiteralComma:
case Parse::NodeKind::WhileCondition:
return Id::Kind::Invalid;
@@ -53,14 +53,14 @@ fn A[] -> ();
// TODO: We don't have parsing support for this yet.
library "[[@TEST_NAME]]";
// CHECK:STDERR: fail_todo_arrow_body.carbon:[[@LINE+8]]:6: error: `fn` declarations must either end with a `;` or have a `{ ... }` block for a definition [ExpectedDeclSemiOrDefinition]
// CHECK:STDERR: fail_todo_arrow_body.carbon:[[@LINE+8]]:1: error: semantics TODO: `function with positional parameters` [SemanticsTodo]
// CHECK:STDERR: fn A => 0;
// CHECK:STDERR: ^~~~~~~
// CHECK:STDERR:
// CHECK:STDERR: fail_todo_arrow_body.carbon:[[@LINE+4]]:6: error: semantics TODO: `HandleTerseBodyArrow` [SemanticsTodo]
// CHECK:STDERR: fn A => 0;
// CHECK:STDERR: ^~
// CHECK:STDERR:
// CHECK:STDERR: fail_todo_arrow_body.carbon:[[@LINE+4]]:1: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo]
// CHECK:STDERR: fn A => 0;
// CHECK:STDERR: ^~~~~~~~~~
// CHECK:STDERR:
fn A => 0;
// --- fail_invalid_file_generic_regression_test.carbon
@@ -176,6 +176,11 @@ fn A {
// CHECK:STDOUT:
// CHECK:STDOUT: --- fail_todo_arrow_body.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @A;
// CHECK:STDOUT:
// CHECK:STDOUT: --- fail_invalid_file_generic_regression_test.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
@@ -95,6 +95,8 @@ CARBON_DIAGNOSTIC_KIND(BinaryOperatorRequiresWhitespace)
CARBON_DIAGNOSTIC_KIND(ExpectedArrayComma)
CARBON_DIAGNOSTIC_KIND(ExpectedCloseSymbol)
CARBON_DIAGNOSTIC_KIND(ExpectedCodeBlock)
CARBON_DIAGNOSTIC_KIND(ExpectedLambdaBody)
CARBON_DIAGNOSTIC_KIND(ExpectedLambdaBodyAfterReturnType)
CARBON_DIAGNOSTIC_KIND(ExpectedExpr)
CARBON_DIAGNOSTIC_KIND(ExpectedIdentifierAfterPeriodOrArrow)
CARBON_DIAGNOSTIC_KIND(ExpectedIdentifierOrSelfAfterPeriod)
+2 -1
View File
@@ -155,7 +155,8 @@ CARBON_DECL_INTRODUCER_TOKEN(Choice, "choice")
CARBON_DECL_INTRODUCER_TOKEN(Class, "class")
CARBON_DECL_INTRODUCER_TOKEN(Constraint, "constraint")
CARBON_DECL_INTRODUCER_TOKEN(Export, "export")
CARBON_DECL_INTRODUCER_TOKEN(Fn, "fn")
CARBON_TOKEN_WITH_VIRTUAL_NODE(
CARBON_DECL_INTRODUCER_TOKEN(Fn, "fn"))
CARBON_DECL_INTRODUCER_TOKEN(Impl, "impl")
CARBON_DECL_INTRODUCER_TOKEN(Import, "import")
CARBON_DECL_INTRODUCER_TOKEN(Interface, "interface")
+13
View File
@@ -470,6 +470,19 @@ auto Context::AddFunctionDefinition(Lex::TokenIndex token, bool has_error)
}
}
auto Context::AddFunctionTerseDefinition(Lex::TokenIndex token, bool has_error)
-> void {
auto definition_id =
AddNode<NodeKind::FunctionTerseDefinition>(token, has_error);
if (ParsingInDeferredDefinitionScope(*this)) {
auto definition_index = deferred_definition_stack_.pop_back_val();
auto& definition = tree_->deferred_definitions_.Get(definition_index);
definition.definition_id = definition_id;
definition.next_definition_index =
DeferredDefinitionIndex(tree_->deferred_definitions().size());
}
}
auto Context::PrintForStackDump(llvm::raw_ostream& output) const -> void {
output << "Parser stack:\n";
for (auto [i, entry] : llvm::enumerate(state_stack_)) {
+4
View File
@@ -406,6 +406,10 @@ class Context {
// Adds a function definition node, and ends tracking a deferred definition if
// necessary.
auto AddFunctionDefinition(Lex::TokenIndex token, bool has_error) -> void;
// Adds a function terse definition node, and ends tracking a deferred
// definition if necessary.
auto AddFunctionTerseDefinition(Lex::TokenIndex token, bool has_error)
-> void;
// Prints information for a stack dump.
auto PrintForStackDump(llvm::raw_ostream& output) const -> void;
+5
View File
@@ -79,6 +79,11 @@ auto HandleExprInPostfix(Context& context) -> void {
context.PushState(state);
break;
}
case Lex::TokenKind::Fn: {
context.PushState(state);
context.PushState(StateKind::LambdaIntroducer);
break;
}
case Lex::TokenKind::False: {
context.AddLeafNode(NodeKind::BoolLiteralFalse, context.Consume());
context.PushState(state);
+19
View File
@@ -51,6 +51,13 @@ auto HandleFunctionSignatureFinish(Context& context) -> void {
context.PushState(StateKind::StatementScopeLoop);
break;
}
case Lex::TokenKind::EqualGreater: {
context.AddFunctionDefinitionStart(context.Consume(), state.has_error);
context.AddLeafNode(NodeKind::TerseBodyArrow, *(context.position() - 1));
context.PushState(state, StateKind::FunctionTerseBodyFinish);
context.PushStateForExpr(PrecedenceGroup::ForTopLevelExpr());
break;
}
case Lex::TokenKind::Equal: {
context.AddNode(NodeKind::BuiltinFunctionDefinitionStart,
context.Consume(), state.has_error);
@@ -95,4 +102,16 @@ auto HandleFunctionDefinitionFinish(Context& context) -> void {
context.AddFunctionDefinition(context.Consume(), state.has_error);
}
auto HandleFunctionTerseBodyFinish(Context& context) -> void {
auto state = context.PopState();
auto semi = context.ConsumeIf(Lex::TokenKind::Semi);
if (!semi && !state.has_error) {
context.DiagnoseExpectedDeclSemi(Lex::TokenKind::Fn);
state.has_error = true;
}
context.AddFunctionTerseDefinition(semi ? *semi : *(context.position() - 1),
state.has_error);
}
} // namespace Carbon::Parse
+100
View File
@@ -0,0 +1,100 @@
// 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"
#include "toolchain/parse/handle.h"
namespace Carbon::Parse {
auto HandleLambdaIntroducer(Context& context) -> void {
auto state = context.PopState();
context.AddLeafNode(NodeKind::LambdaIntroducer, context.Consume());
context.PushState(state, StateKind::LambdaAfterIntroducer);
}
auto HandleLambdaAfterIntroducer(Context& context) -> void {
auto state = context.PopState();
if (context.PositionIs(Lex::TokenKind::OpenSquareBracket)) {
context.PushState(state, StateKind::LambdaAfterImplicitParams);
context.PushState(StateKind::PatternListAsImplicit);
} else if (context.PositionIs(Lex::TokenKind::OpenParen)) {
context.PushState(state, StateKind::LambdaAfterParams);
context.PushState(StateKind::PatternListAsExplicit);
} else {
// No implicit or explicit params.
context.PushState(state, StateKind::LambdaAfterParams);
}
}
auto HandleLambdaAfterImplicitParams(Context& context) -> void {
auto state = context.PopState();
if (context.PositionIs(Lex::TokenKind::OpenParen)) {
context.PushState(state, StateKind::LambdaAfterParams);
context.PushState(StateKind::PatternListAsExplicit);
} else {
// No explicit params after implicit params.
context.PushState(state, StateKind::LambdaAfterParams);
}
}
auto HandleLambdaAfterParams(Context& context) -> void {
auto state = context.PopState();
if (context.PositionIs(Lex::TokenKind::MinusGreater)) {
// Has return type.
context.PushState(state, StateKind::LambdaBody);
context.PushState(StateKind::FunctionReturnTypeFinish);
context.ConsumeAndDiscard();
context.PushStateForExpr(PrecedenceGroup::ForType());
} else if (context.PositionIs(Lex::TokenKind::EqualGreater)) {
// Terse body `=> expr`
context.AddLeafNode(NodeKind::TerseBodyArrow, context.Consume());
context.PushState(state, StateKind::LambdaBodyFinish);
context.PushStateForExpr(PrecedenceGroup::ForTopLevelExpr());
} else if (context.PositionIs(Lex::TokenKind::OpenCurlyBrace)) {
// Block body `{ ... }`
context.PushState(state, StateKind::LambdaBodyFinish);
context.PushState(StateKind::CodeBlock);
} else {
CARBON_DIAGNOSTIC(ExpectedLambdaBody, Error,
"expected `->`, `=>`, or `{{`");
context.emitter().Emit(*context.position(), ExpectedLambdaBody);
state.has_error = true;
context.ReturnErrorOnState();
}
}
auto HandleLambdaBody(Context& context) -> void {
auto state = context.PopState();
// We arrive here after parsing return type.
// So we look for `=>` or `{`.
if (context.PositionIs(Lex::TokenKind::EqualGreater)) {
// Terse body `=> expr`
context.AddLeafNode(NodeKind::TerseBodyArrow, context.Consume());
context.PushState(state, StateKind::LambdaBodyFinish);
context.PushStateForExpr(PrecedenceGroup::ForTopLevelExpr());
} else if (context.PositionIs(Lex::TokenKind::OpenCurlyBrace)) {
// Block body `{ ... }`
context.PushState(state, StateKind::LambdaBodyFinish);
context.PushState(StateKind::CodeBlock);
} else {
CARBON_DIAGNOSTIC(ExpectedLambdaBodyAfterReturnType, Error,
"expected `=>` or `{{` after return type");
context.emitter().Emit(*context.position(),
ExpectedLambdaBodyAfterReturnType);
state.has_error = true;
context.ReturnErrorOnState();
}
}
auto HandleLambdaBodyFinish(Context& context) -> void {
auto state = context.PopState();
context.AddNode(NodeKind::Lambda, state.token, state.has_error);
}
} // namespace Carbon::Parse
+3
View File
@@ -160,6 +160,9 @@ using AnyClassDeclId =
ChoiceDefinitionStartId>;
using AnyFunctionDeclId = NodeIdOneOf<FunctionDeclId, FunctionDefinitionStartId,
BuiltinFunctionDefinitionStartId>;
using AnyFunctionDefinitionId =
NodeIdOneOf<FunctionDefinitionId, FunctionTerseDefinitionId,
BuiltinFunctionDefinitionId>;
using AnyImplDeclId = NodeIdOneOf<ImplDeclId, ImplDefinitionStartId>;
using AnyInterfaceDeclId =
NodeIdOneOf<InterfaceDeclId, InterfaceDefinitionStartId>;
+4
View File
@@ -143,10 +143,14 @@ CARBON_PARSE_NODE_KIND(CodeBlockStart)
CARBON_PARSE_NODE_KIND(CodeBlock)
CARBON_PARSE_NODE_KIND(FunctionIntroducer)
CARBON_PARSE_NODE_KIND(LambdaIntroducer)
CARBON_PARSE_NODE_KIND(ReturnType)
CARBON_PARSE_NODE_KIND(FunctionDefinitionStart)
CARBON_PARSE_NODE_KIND(FunctionDefinition)
CARBON_PARSE_NODE_KIND(FunctionTerseDefinition)
CARBON_PARSE_NODE_KIND(FunctionDecl)
CARBON_PARSE_NODE_KIND(Lambda)
CARBON_PARSE_NODE_KIND(TerseBodyArrow)
CARBON_PARSE_NODE_KIND(BuiltinFunctionDefinitionStart)
CARBON_PARSE_NODE_KIND(BuiltinName)
CARBON_PARSE_NODE_KIND(BuiltinFunctionDefinition)
+3 -3
View File
@@ -14,7 +14,7 @@
namespace Carbon::Parse {
CARBON_DEFINE_RAW_ENUM_CLASS(NodeKind, uint8_t) {
CARBON_DEFINE_RAW_ENUM_CLASS(NodeKind, uint16_t) {
#define CARBON_PARSE_NODE_KIND(Name) CARBON_RAW_ENUM_ENUMERATOR(Name)
#include "toolchain/parse/node_kind.def"
};
@@ -91,8 +91,8 @@ static_assert(
"a `constexpr` context despite being declared as merely `const`. We use it "
"in a static assert here to ensure that.");
// We expect the parse node kind to fit compactly into 8 bits.
static_assert(sizeof(NodeKind) == 1, "Kind objects include padding!");
// We expect the parse node kind to fit compactly into 16 bits.
static_assert(sizeof(NodeKind) == 2, "Kind objects include padding!");
// Optional arguments that can be supplied when defining a node kind. At least
// one of `bracketed_by` and `child_count` is required.
+84
View File
@@ -678,6 +678,83 @@ CARBON_PARSE_STATE(ExprStatementFinish)
// 2. FunctionAfterParams
CARBON_PARSE_STATE(FunctionIntroducer)
// Handles a lambda's introducer.
//
// fn ...
// ^
// 1. LambdaIntroducer
// 2. LambdaAfterIntroducer
CARBON_PARSE_STATE(LambdaIntroducer)
// Handles processing of a lambda after the introducer.
//
// fn [ ... ] ...
// ^
// 1. PatternListAsImplicit
// 2. LambdaAfterImplicitParams
//
// fn ( ... ) ...
// ^
// 1. PatternListAsExplicit
// 2. LambdaAfterParams
//
// fn ...
// ^
// 1. LambdaAfterParams
CARBON_PARSE_STATE(LambdaAfterIntroducer)
// Handles processing of a lambda after implicit parameters.
//
// fn [ ... ] ( ... ) ...
// ^
// 1. PatternListAsExplicit
// 2. LambdaAfterParams
//
// fn [ ... ] ...
// ^
// 1. LambdaAfterParams
CARBON_PARSE_STATE(LambdaAfterImplicitParams)
// Handles processing of a lambda after optional parameters.
//
// fn ... -> ...
// ^
// 1. FunctionReturnTypeFinish (Reused)
// 2. LambdaBody
//
// fn ... => ...
// ^
// 1. Expr
// 2. LambdaBodyFinish
//
// fn ... { ... }
// ^
// 1. StatementScopeLoop
// 2. LambdaBodyFinish
CARBON_PARSE_STATE(LambdaAfterParams)
// Handles processing of a lambda body.
//
// fn ... -> ... { ... }
// ^
// 1. StatementScopeLoop
// 2. LambdaBodyFinish
//
// fn ... -> ... => ...
// ^
// 1. Expr
// 2. LambdaBodyFinish
CARBON_PARSE_STATE(LambdaBody)
// Finishes a lambda expression.
//
// fn ... }
// ^
// fn ... => expr
// ^
// (state done)
CARBON_PARSE_STATE(LambdaBodyFinish)
// Handles processing of a function's syntax after `)`, primarily the
// possibility a `->` return type is there. Always enqueues signature finish
// handling.
@@ -726,6 +803,13 @@ CARBON_PARSE_STATE(FunctionSignatureFinish)
// (state done)
CARBON_PARSE_STATE(FunctionDefinitionFinish)
// Finishes a function with a terse body.
//
// fn ... => expr ;
// ^
// (state done)
CARBON_PARSE_STATE(FunctionTerseBodyFinish)
// Handles `export <name>`.
//
// export Name;
+43
View File
@@ -0,0 +1,43 @@
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// AUTOUPDATE
// TIP: To test this file alone, run:
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/parse/testdata/function/terse.carbon
// TIP: To dump output, run:
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/function/terse.carbon
fn F(x: i32) => x;
fn G(x: i32) -> i32 => x;
// CHECK:STDOUT: - filename: terse.carbon
// CHECK:STDOUT: parse_tree: [
// CHECK:STDOUT: {kind: 'FileStart', text: ''},
// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'},
// CHECK:STDOUT: {kind: 'IdentifierNameBeforeParams', text: 'F'},
// CHECK:STDOUT: {kind: 'ExplicitParamListStart', text: '('},
// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'x'},
// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'},
// CHECK:STDOUT: {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
// CHECK:STDOUT: {kind: 'ExplicitParamList', text: ')', subtree_size: 5},
// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '=>', subtree_size: 8},
// CHECK:STDOUT: {kind: 'TerseBodyArrow', text: '=>'},
// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'x'},
// CHECK:STDOUT: {kind: 'FunctionTerseDefinition', text: ';', subtree_size: 11},
// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'},
// CHECK:STDOUT: {kind: 'IdentifierNameBeforeParams', text: 'G'},
// CHECK:STDOUT: {kind: 'ExplicitParamListStart', text: '('},
// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'x'},
// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'},
// CHECK:STDOUT: {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
// CHECK:STDOUT: {kind: 'ExplicitParamList', text: ')', subtree_size: 5},
// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'},
// CHECK:STDOUT: {kind: 'ReturnType', text: '->', subtree_size: 2},
// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '=>', subtree_size: 10},
// CHECK:STDOUT: {kind: 'TerseBodyArrow', text: '=>'},
// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'x'},
// CHECK:STDOUT: {kind: 'FunctionTerseDefinition', text: ';', subtree_size: 13},
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
// CHECK:STDOUT: ]
+135
View File
@@ -0,0 +1,135 @@
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// AUTOUPDATE
// TIP: To test this file alone, run:
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/parse/testdata/lambda/lambda.carbon
// TIP: To dump output, run:
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/parse/testdata/lambda/lambda.carbon
// --- basic.carbon
var a: auto = fn [T:! type] (x: T) -> T { return x; };
var b: auto = fn (x: i32) => x;
var c: auto = fn { };
// --- fail_expected_body.carbon
fn F() {
// CHECK:STDERR: fail_expected_body.carbon:[[@LINE+4]]:20: error: expected `->`, `=>`, or `{` [ExpectedLambdaBody]
// CHECK:STDERR: var x: auto = fn ;
// CHECK:STDERR: ^
// CHECK:STDERR:
var x: auto = fn ;
}
// --- fail_expected_body_after_return_type.carbon
fn G() {
// CHECK:STDERR: fail_expected_body_after_return_type.carbon:[[@LINE+4]]:27: error: expected `=>` or `{` after return type [ExpectedLambdaBodyAfterReturnType]
// CHECK:STDERR: var x: auto = fn -> i32 ;
// CHECK:STDERR: ^
// CHECK:STDERR:
var x: auto = fn -> i32 ;
}
// CHECK:STDOUT: - filename: basic.carbon
// CHECK:STDOUT: parse_tree: [
// CHECK:STDOUT: {kind: 'FileStart', text: ''},
// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'},
// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'a'},
// CHECK:STDOUT: {kind: 'AutoTypeLiteral', text: 'auto'},
// CHECK:STDOUT: {kind: 'VarBindingPattern', text: ':', subtree_size: 3},
// CHECK:STDOUT: {kind: 'VariablePattern', text: 'var', subtree_size: 4},
// CHECK:STDOUT: {kind: 'VariableInitializer', text: '='},
// CHECK:STDOUT: {kind: 'LambdaIntroducer', text: 'fn'},
// CHECK:STDOUT: {kind: 'ImplicitParamListStart', text: '['},
// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'T'},
// CHECK:STDOUT: {kind: 'CompileTimeBindingPatternStart', text: ':!', subtree_size: 2},
// CHECK:STDOUT: {kind: 'TypeTypeLiteral', text: 'type'},
// CHECK:STDOUT: {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 4},
// CHECK:STDOUT: {kind: 'ImplicitParamList', text: ']', subtree_size: 6},
// CHECK:STDOUT: {kind: 'ExplicitParamListStart', text: '('},
// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'x'},
// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'T'},
// CHECK:STDOUT: {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
// CHECK:STDOUT: {kind: 'ExplicitParamList', text: ')', subtree_size: 5},
// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'T'},
// CHECK:STDOUT: {kind: 'ReturnType', text: '->', subtree_size: 2},
// CHECK:STDOUT: {kind: 'CodeBlockStart', text: '{'},
// CHECK:STDOUT: {kind: 'ReturnStatementStart', text: 'return'},
// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'x'},
// CHECK:STDOUT: {kind: 'ReturnStatement', text: ';', subtree_size: 3},
// CHECK:STDOUT: {kind: 'CodeBlock', text: '}', subtree_size: 5},
// CHECK:STDOUT: {kind: 'Lambda', text: 'fn', subtree_size: 20},
// CHECK:STDOUT: {kind: 'VariableDecl', text: ';', subtree_size: 27},
// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'},
// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'b'},
// CHECK:STDOUT: {kind: 'AutoTypeLiteral', text: 'auto'},
// CHECK:STDOUT: {kind: 'VarBindingPattern', text: ':', subtree_size: 3},
// CHECK:STDOUT: {kind: 'VariablePattern', text: 'var', subtree_size: 4},
// CHECK:STDOUT: {kind: 'VariableInitializer', text: '='},
// CHECK:STDOUT: {kind: 'LambdaIntroducer', text: 'fn'},
// CHECK:STDOUT: {kind: 'ExplicitParamListStart', text: '('},
// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'x'},
// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'},
// CHECK:STDOUT: {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
// CHECK:STDOUT: {kind: 'ExplicitParamList', text: ')', subtree_size: 5},
// CHECK:STDOUT: {kind: 'TerseBodyArrow', text: '=>'},
// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'x'},
// CHECK:STDOUT: {kind: 'Lambda', text: 'fn', subtree_size: 9},
// CHECK:STDOUT: {kind: 'VariableDecl', text: ';', subtree_size: 16},
// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'},
// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'c'},
// CHECK:STDOUT: {kind: 'AutoTypeLiteral', text: 'auto'},
// CHECK:STDOUT: {kind: 'VarBindingPattern', text: ':', subtree_size: 3},
// CHECK:STDOUT: {kind: 'VariablePattern', text: 'var', subtree_size: 4},
// CHECK:STDOUT: {kind: 'VariableInitializer', text: '='},
// CHECK:STDOUT: {kind: 'LambdaIntroducer', text: 'fn'},
// CHECK:STDOUT: {kind: 'CodeBlockStart', text: '{'},
// CHECK:STDOUT: {kind: 'CodeBlock', text: '}', subtree_size: 2},
// CHECK:STDOUT: {kind: 'Lambda', text: 'fn', subtree_size: 4},
// CHECK:STDOUT: {kind: 'VariableDecl', text: ';', subtree_size: 11},
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
// CHECK:STDOUT: ]
// CHECK:STDOUT: - filename: fail_expected_body.carbon
// CHECK:STDOUT: parse_tree: [
// CHECK:STDOUT: {kind: 'FileStart', text: ''},
// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'},
// CHECK:STDOUT: {kind: 'IdentifierNameBeforeParams', text: 'F'},
// CHECK:STDOUT: {kind: 'ExplicitParamListStart', text: '('},
// CHECK:STDOUT: {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'},
// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'x'},
// CHECK:STDOUT: {kind: 'AutoTypeLiteral', text: 'auto'},
// CHECK:STDOUT: {kind: 'VarBindingPattern', text: ':', subtree_size: 3},
// CHECK:STDOUT: {kind: 'VariablePattern', text: 'var', subtree_size: 4},
// CHECK:STDOUT: {kind: 'VariableInitializer', text: '='},
// CHECK:STDOUT: {kind: 'LambdaIntroducer', text: 'fn'},
// CHECK:STDOUT: {kind: 'VariableDecl', text: ';', has_error: yes, subtree_size: 8},
// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 14},
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
// CHECK:STDOUT: ]
// CHECK:STDOUT: - filename: fail_expected_body_after_return_type.carbon
// CHECK:STDOUT: parse_tree: [
// CHECK:STDOUT: {kind: 'FileStart', text: ''},
// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'},
// CHECK:STDOUT: {kind: 'IdentifierNameBeforeParams', text: 'G'},
// CHECK:STDOUT: {kind: 'ExplicitParamListStart', text: '('},
// CHECK:STDOUT: {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
// CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
// CHECK:STDOUT: {kind: 'VariableIntroducer', text: 'var'},
// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeParams', text: 'x'},
// CHECK:STDOUT: {kind: 'AutoTypeLiteral', text: 'auto'},
// CHECK:STDOUT: {kind: 'VarBindingPattern', text: ':', subtree_size: 3},
// CHECK:STDOUT: {kind: 'VariablePattern', text: 'var', subtree_size: 4},
// CHECK:STDOUT: {kind: 'VariableInitializer', text: '='},
// CHECK:STDOUT: {kind: 'LambdaIntroducer', text: 'fn'},
// CHECK:STDOUT: {kind: 'IntTypeLiteral', text: 'i32'},
// CHECK:STDOUT: {kind: 'ReturnType', text: '->', subtree_size: 2},
// CHECK:STDOUT: {kind: 'VariableDecl', text: ';', has_error: yes, subtree_size: 10},
// CHECK:STDOUT: {kind: 'FunctionDefinition', text: '}', subtree_size: 16},
// CHECK:STDOUT: {kind: 'FileEnd', text: ''},
// CHECK:STDOUT: ]
+3 -4
View File
@@ -40,7 +40,7 @@ struct DeferredDefinition {
// The node that starts the function definition.
FunctionDefinitionStartId start_id;
// The function definition node.
FunctionDefinitionId definition_id = NodeId::None;
AnyFunctionDefinitionId definition_id = NodeId::None;
// The index of the next method that is not nested within this one.
DeferredDefinitionIndex next_definition_index = DeferredDefinitionIndex::None;
};
@@ -211,9 +211,8 @@ class Tree : public Printable<Tree> {
}
private:
// The kind of this node. Note that this is only a single byte.
// The kind of this node.
NodeKind kind_;
static_assert(sizeof(kind_) == 1, "TokenKind must pack to 8 bits");
// Whether this node is or contains a parse error.
//
@@ -234,7 +233,7 @@ class Tree : public Printable<Tree> {
unsigned token_index_ : Lex::TokenIndex::Bits;
};
static_assert(sizeof(NodeImpl) == 4,
static_assert(sizeof(NodeImpl) == 8,
"Unexpected size of node implementation!");
// Sets the kind of a node. This is intended to allow putting the tree into a
+35 -2
View File
@@ -469,8 +469,8 @@ struct FunctionSignature {
using FunctionDecl = FunctionSignature<NodeKind::FunctionDecl,
Lex::SemiTokenIndex, NodeCategory::Decl>;
using FunctionDefinitionStart =
FunctionSignature<NodeKind::FunctionDefinitionStart,
Lex::OpenCurlyBraceTokenIndex, NodeCategory::None>;
FunctionSignature<NodeKind::FunctionDefinitionStart, Lex::TokenIndex,
NodeCategory::None>;
// A function definition: `fn F() -> i32 { ... }`.
struct FunctionDefinition {
@@ -483,6 +483,18 @@ struct FunctionDefinition {
Lex::CloseCurlyBraceTokenIndex token;
};
// A terse function definition: `fn F() -> i32 => expr;`.
struct FunctionTerseDefinition {
static constexpr auto Kind = NodeKind::FunctionTerseDefinition.Define(
{.category = NodeCategory::Decl,
.bracketed_by = FunctionDefinitionStart::Kind});
FunctionDefinitionStartId signature;
TerseBodyArrowId arrow;
AnyExprId body;
Lex::SemiTokenIndex token;
};
using BuiltinFunctionDefinitionStart =
FunctionSignature<NodeKind::BuiltinFunctionDefinitionStart,
Lex::EqualTokenIndex, NodeCategory::None>;
@@ -651,6 +663,9 @@ struct VariablePattern {
using CodeBlockStart =
LeafNode<NodeKind::CodeBlockStart, Lex::OpenCurlyBraceTokenIndex>;
using TerseBodyArrow =
LeafNode<NodeKind::TerseBodyArrow, Lex::EqualGreaterTokenIndex>;
// A code block: `{ statement; statement; ... }`.
struct CodeBlock {
static constexpr auto Kind =
@@ -661,6 +676,24 @@ struct CodeBlock {
Lex::CloseCurlyBraceTokenIndex token;
};
using LambdaIntroducer =
LeafNode<NodeKind::LambdaIntroducer, Lex::FnTokenIndex>;
struct Lambda {
static constexpr auto Kind = NodeKind::Lambda.Define(
{.category = NodeCategory::Expr, .bracketed_by = LambdaIntroducer::Kind});
LambdaIntroducerId introducer;
std::optional<ImplicitParamListId> implicit_params;
std::optional<ExplicitParamListId> explicit_params;
std::optional<ReturnTypeId> return_type;
std::optional<TerseBodyArrowId> arrow;
NodeId body;
// Use a generic token index because the token might be `}` or part of an
// expression.
Lex::TokenIndex token;
};
// An expression statement: `F(x);`.
struct ExprStatement {
static constexpr auto Kind = NodeKind::ExprStatement.Define(