Skip to content

Commit

Permalink
Parse support for tuple patterns in var and let (#3448)
Browse files Browse the repository at this point in the history
Co-authored-by: Richard Smith <[email protected]>
  • Loading branch information
geoffromer and zygoloid authored Dec 8, 2023
1 parent 8ace1dc commit 39750b9
Show file tree
Hide file tree
Showing 27 changed files with 181 additions and 104 deletions.
1 change: 1 addition & 0 deletions toolchain/check/handle_binding_pattern.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ auto HandleBindingPattern(Context& context, Parse::NodeId parse_node) -> bool {
}

auto HandleTemplate(Context& context, Parse::NodeId parse_node) -> bool {
// TODO: diagnose if this occurs in a `var` context.
return context.TODO(parse_node, "HandleTemplate");
}

Expand Down
3 changes: 3 additions & 0 deletions toolchain/check/handle_let.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ namespace Carbon::Check {

auto HandleLetDecl(Context& context, Parse::NodeId parse_node) -> bool {
auto value_id = context.node_stack().PopExpr();
if (context.node_stack().PeekIs<Parse::NodeKind::ParamList>()) {
return context.TODO(parse_node, "tuple pattern in let");
}
SemIR::InstId pattern_id =
context.node_stack().Pop<Parse::NodeKind::BindingPattern>();
context.node_stack()
Expand Down
14 changes: 11 additions & 3 deletions toolchain/check/handle_variable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,23 @@ auto HandleVariableInitializer(Context& context, Parse::NodeId parse_node)
auto HandleVariableDecl(Context& context, Parse::NodeId parse_node) -> bool {
// Handle the optional initializer.
auto init_id = SemIR::InstId::Invalid;
bool has_init =
context.parse_tree().node_kind(context.node_stack().PeekParseNode()) !=
Parse::NodeKind::BindingPattern;
Parse::NodeKind next_kind =
context.parse_tree().node_kind(context.node_stack().PeekParseNode());
if (next_kind == Parse::NodeKind::ParamList) {
return context.TODO(parse_node, "tuple pattern in var");
}
// TODO: find a more robust way to determine if there was an initializer.
bool has_init = next_kind != Parse::NodeKind::BindingPattern;
if (has_init) {
init_id = context.node_stack().PopExpr();
context.node_stack()
.PopAndDiscardSoloParseNode<Parse::NodeKind::VariableInitializer>();
}

if (context.node_stack().PeekIs<Parse::NodeKind::ParamList>()) {
return context.TODO(parse_node, "tuple pattern in var");
}

// Extract the name binding.
auto value_id = context.node_stack().Pop<Parse::NodeKind::BindingPattern>();
if (auto bind_name = context.insts().Get(value_id).TryAs<SemIR::BindName>()) {
Expand Down
4 changes: 1 addition & 3 deletions toolchain/diagnostics/diagnostic_kind.def
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,13 @@ CARBON_DIAGNOSTIC_KIND(ExpectedCloseSymbol)
CARBON_DIAGNOSTIC_KIND(ExpectedCodeBlock)
CARBON_DIAGNOSTIC_KIND(ExpectedExpr)
CARBON_DIAGNOSTIC_KIND(ExpectedIdentifierAfterDotOrArrow)
CARBON_DIAGNOSTIC_KIND(ExpectedLetBindingName)
CARBON_DIAGNOSTIC_KIND(ExpectedParamName)
CARBON_DIAGNOSTIC_KIND(ExpectedBindingPattern)
CARBON_DIAGNOSTIC_KIND(ExpectedParenAfter)
CARBON_DIAGNOSTIC_KIND(ExpectedExprSemi)
CARBON_DIAGNOSTIC_KIND(ExpectedStatementSemi)
CARBON_DIAGNOSTIC_KIND(ExpectedStructLiteralField)
CARBON_DIAGNOSTIC_KIND(ExpectedVarAfterReturned)
CARBON_DIAGNOSTIC_KIND(ExpectedVariableDecl)
CARBON_DIAGNOSTIC_KIND(ExpectedVariableName)
CARBON_DIAGNOSTIC_KIND(OperatorRequiresParentheses)
CARBON_DIAGNOSTIC_KIND(StatementOperatorAsSubExpr)
CARBON_DIAGNOSTIC_KIND(UnaryOperatorRequiresParentheses)
Expand Down
10 changes: 0 additions & 10 deletions toolchain/parse/context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -166,16 +166,6 @@ auto Context::ConsumeIf(Lex::TokenKind kind) -> std::optional<Lex::TokenIndex> {
return Consume();
}

auto Context::ConsumeIfBindingPatternKeyword(Lex::TokenKind keyword_token,
State keyword_state,
int subtree_start) -> void {
if (auto token = ConsumeIf(keyword_token)) {
PushState(Context::StateStackEntry(
keyword_state, PrecedenceGroup::ForTopLevelExpr(),
PrecedenceGroup::ForTopLevelExpr(), *token, subtree_start));
}
}

auto Context::FindNextOf(std::initializer_list<Lex::TokenKind> desired_kinds)
-> std::optional<Lex::TokenIndex> {
auto new_position = position_;
Expand Down
13 changes: 0 additions & 13 deletions toolchain/parse/context.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,6 @@ class Context {
// Possible return values for FindListToken.
enum class ListTokenKind : int8_t { Comma, Close, CommaClose };

// Supported kinds for HandleBindingPattern.
enum class BindingPatternKind : int8_t {
ImplicitParam,
Param,
Variable,
Let
};

// Used for restricting ordering of `package` and `import` directives.
enum class PackagingState : int8_t {
FileStart,
Expand Down Expand Up @@ -295,11 +287,6 @@ class Context {
// Propagates an error up the state stack, to the parent state.
auto ReturnErrorOnState() -> void { state_stack_.back().has_error = true; }

// For HandleBindingPattern, tries to consume a wrapping keyword.
auto ConsumeIfBindingPatternKeyword(Lex::TokenKind keyword_token,
State keyword_state, int subtree_start)
-> void;

// Emits a diagnostic for a declaration missing a semi.
auto EmitExpectedDeclSemi(Lex::TokenKind expected_kind) -> void;

Expand Down
62 changes: 14 additions & 48 deletions toolchain/parse/handle_binding_pattern.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,46 +6,28 @@

namespace Carbon::Parse {

// Handles BindingPatternAs(ImplicitParam|FunctionParam|Variable|Let).
static auto HandleBindingPattern(Context& context,
Context::BindingPatternKind pattern_kind)
-> void {
auto HandleBindingPattern(Context& context) -> void {
auto state = context.PopState();

// Parameters may have keywords prefixing the pattern. They become the parent
// for the full BindingPattern.
if (pattern_kind != Context::BindingPatternKind::Variable) {
context.ConsumeIfBindingPatternKeyword(Lex::TokenKind::Template,
State::BindingPatternTemplate,
state.subtree_start);
context.ConsumeIfBindingPatternKeyword(Lex::TokenKind::Addr,
State::BindingPatternAddress,
state.subtree_start);
if (auto token = context.ConsumeIf(Lex::TokenKind::Template)) {
context.PushState(Context::StateStackEntry(
State::BindingPatternTemplate, PrecedenceGroup::ForTopLevelExpr(),
PrecedenceGroup::ForTopLevelExpr(), *token, state.subtree_start));
}

if (auto token = context.ConsumeIf(Lex::TokenKind::Addr)) {
context.PushState(Context::StateStackEntry(
State::BindingPatternAddress, PrecedenceGroup::ForTopLevelExpr(),
PrecedenceGroup::ForTopLevelExpr(), *token, state.subtree_start));
}

// Handle an invalid pattern introducer for parameters and variables.
auto on_error = [&]() {
switch (pattern_kind) {
case Context::BindingPatternKind::ImplicitParam:
case Context::BindingPatternKind::Param: {
CARBON_DIAGNOSTIC(ExpectedParamName, Error,
"Expected parameter declaration.");
context.emitter().Emit(*context.position(), ExpectedParamName);
break;
}
case Context::BindingPatternKind::Variable: {
CARBON_DIAGNOSTIC(ExpectedVariableName, Error,
"Expected pattern in `var` declaration.");
context.emitter().Emit(*context.position(), ExpectedVariableName);
break;
}
case Context::BindingPatternKind::Let: {
CARBON_DIAGNOSTIC(ExpectedLetBindingName, Error,
"Expected pattern in `let` declaration.");
context.emitter().Emit(*context.position(), ExpectedLetBindingName);
break;
}
}
CARBON_DIAGNOSTIC(ExpectedBindingPattern, Error,
"Expected binding pattern.");
context.emitter().Emit(*context.position(), ExpectedBindingPattern);
// Add a placeholder for the type.
context.AddLeafNode(NodeKind::InvalidParse, *context.position(),
/*has_error=*/true);
Expand Down Expand Up @@ -89,22 +71,6 @@ static auto HandleBindingPattern(Context& context,
}
}

auto HandleBindingPatternAsImplicitParam(Context& context) -> void {
HandleBindingPattern(context, Context::BindingPatternKind::ImplicitParam);
}

auto HandleBindingPatternAsParam(Context& context) -> void {
HandleBindingPattern(context, Context::BindingPatternKind::Param);
}

auto HandleBindingPatternAsVariable(Context& context) -> void {
HandleBindingPattern(context, Context::BindingPatternKind::Variable);
}

auto HandleBindingPatternAsLet(Context& context) -> void {
HandleBindingPattern(context, Context::BindingPatternKind::Let);
}

// Handles BindingPatternFinishAs(Generic|Regular).
static auto HandleBindingPatternFinish(Context& context, NodeKind node_kind)
-> void {
Expand Down
2 changes: 1 addition & 1 deletion toolchain/parse/handle_let.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ auto HandleLet(Context& context) -> void {
context.PushState(state);

// This will start at the pattern.
context.PushState(State::BindingPatternAsLet);
context.PushState(State::Pattern);
}

auto HandleLetAfterPattern(Context& context) -> void {
Expand Down
6 changes: 2 additions & 4 deletions toolchain/parse/handle_param.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,11 @@ static auto HandleParam(Context& context, State pattern_state,
}

auto HandleParamAsImplicit(Context& context) -> void {
HandleParam(context, State::BindingPatternAsImplicitParam,
State::ParamFinishAsImplicit);
HandleParam(context, State::BindingPattern, State::ParamFinishAsImplicit);
}

auto HandleParamAsRegular(Context& context) -> void {
HandleParam(context, State::BindingPatternAsParam,
State::ParamFinishAsRegular);
HandleParam(context, State::BindingPattern, State::ParamFinishAsRegular);
}

// Handles ParamFinishAs(Implicit|Regular).
Expand Down
18 changes: 18 additions & 0 deletions toolchain/parse/handle_pattern.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// 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 {

auto HandlePattern(Context& context) -> void {
context.PopAndDiscardState();
if (context.PositionKind() == Lex::TokenKind::OpenParen) {
context.PushState(State::ParamListAsRegular);
} else {
context.PushState(State::BindingPattern);
}
}

} // namespace Carbon::Parse
2 changes: 1 addition & 1 deletion toolchain/parse/handle_var.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ static auto HandleVar(Context& context, State finish_state,
context.AddLeafNode(NodeKind::ReturnedModifier, returned_token);
}

context.PushState(State::BindingPatternAsVariable);
context.PushState(State::Pattern);
}

auto HandleVarAsDecl(Context& context) -> void {
Expand Down
6 changes: 4 additions & 2 deletions toolchain/parse/node_kind.def
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,8 @@ CARBON_PARSE_NODE_KIND_BRACKET(FunctionDecl, FunctionIntroducer,
//
// [Generic]PatternBinding and ParamListComma may repeat with ParamListComma
// as a separator.
//
// TODO: rename ParamList to TuplePattern to reflect wider usage.
CARBON_PARSE_NODE_KIND_CHILD_COUNT(ParamListStart, 0, CARBON_TOKEN(OpenParen))
CARBON_PARSE_NODE_KIND_CHILD_COUNT(ImplicitParamListStart, 0,
CARBON_TOKEN(OpenSquareBracket))
Expand Down Expand Up @@ -285,7 +287,7 @@ CARBON_PARSE_NODE_KIND_CHILD_COUNT(Template, 1, CARBON_TOKEN(Template))
// `let`:
// LetIntroducer
// _repeated_ _external_: modifier
// _external_: BindingPattern
// _external_: BindingPattern or ParamList
// LetInitializer
// _external_: expression
// LetDecl
Expand All @@ -301,7 +303,7 @@ CARBON_PARSE_NODE_KIND_BRACKET(LetDecl, LetIntroducer,
// VariableIntroducer
// _repeated_ _external_: modifier
// _optional_ ReturnedModifier
// _external_: BindingPattern
// _external_: BindingPattern or ParamList
// VariableInitializer
// _external_: expression
// _optional_
Expand Down
42 changes: 36 additions & 6 deletions toolchain/parse/state.def
Original file line number Diff line number Diff line change
Expand Up @@ -649,7 +649,7 @@ CARBON_PARSE_STATE(Package)
//
// ...
// ^
// 1. BindingPatternAs(ImplicitParam|Param)
// 1. BindingPattern
// 2. ParamFinishAs(Implicit|Regular)
CARBON_PARSE_STATE_VARIANTS2(Param, Implicit, Regular)

Expand All @@ -671,6 +671,8 @@ CARBON_PARSE_STATE_VARIANTS2(ParamFinish, Implicit, Regular)

// Handles processing of a parameter list `[` or `(`.
//
// TODO: Rename this to TuplePattern to reflect wider usage.
//
// ( ) (variant is Regular)
// ^
// [ ] (variant is Implicit)
Expand Down Expand Up @@ -759,6 +761,17 @@ CARBON_PARSE_STATE_VARIANTS2(ParenExprParamFinish, Unknown, Tuple)
// (state done)
CARBON_PARSE_STATE_VARIANTS2(ParenExprFinish, Normal, Tuple)

// Handles processing of a pattern.
//
// ( ... )
// ^
// 1. ParamListAsRegular
//
// ...
// ^
// 1. BindingPattern
CARBON_PARSE_STATE(Pattern)

// Handles the initial part of a binding pattern, enqueuing type expression
// processing.
//
Expand Down Expand Up @@ -791,8 +804,7 @@ CARBON_PARSE_STATE_VARIANTS2(ParenExprFinish, Normal, Tuple)
// ???
// ^
// 1. BindingPatternFinishAsRegular
CARBON_PARSE_STATE_VARIANTS4(BindingPattern, ImplicitParam, Param, Variable,
Let)
CARBON_PARSE_STATE(BindingPattern)

// Handles `addr` in a binding pattern.
//
Expand Down Expand Up @@ -1056,13 +1068,25 @@ CARBON_PARSE_STATE(BaseDecl)
//
// var ... (variant is not Returned)
// ^
// 1. BindingPatternAsVariable
// 1. BindingPattern
// 2. VarAfterPattern
// 3. VarFinishAs(Decl|For)
//
// var (...)
// ^
// 1. ParamListAsRegular
// 2. VarAfterPattern
// 3. VarFinishAs(Decl|For)
//
// returned var ... (variant is Returned)
// ^~~~~~~~~~~~
// 1. BindingPatternAsVariable
// 1. BindingPattern
// 2. VarAfterPattern
// 3. VarFinishAsDecl
//
// returned var ... (variant is Returned)
// ^~~~~~~~~~~~
// 1. ParamListAsRegular
// 2. VarAfterPattern
// 3. VarFinishAsDecl
//
Expand Down Expand Up @@ -1102,9 +1126,15 @@ CARBON_PARSE_STATE_VARIANTS2(VarFinish, Decl, For)

// Handles the start of a `let`.
//
// let (...)
// ^~~
// 1. ParamListAsRegular
// 2. LetAfterPattern
// 3. LetFinish
//
// let ...
// ^
// 1. BindingPatternAsLet
// 1. BindingPattern
// 2. LetAfterPattern
// 3. LetFinish
CARBON_PARSE_STATE(Let)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
//
// AUTOUPDATE

// CHECK:STDERR: fail_paren_match_regression.carbon:[[@LINE+6]]:5: ERROR: Expected pattern in `var` declaration.
// CHECK:STDERR: fail_paren_match_regression.carbon:[[@LINE+6]]:5: ERROR: Expected binding pattern.
// CHECK:STDERR: var = (foo {})
// CHECK:STDERR: ^
// CHECK:STDERR: fail_paren_match_regression.carbon:[[@LINE+3]]:12: ERROR: Expected `,` or `)`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
// CHECK:STDERR: fail_missing_implicit_close.carbon:[[@LINE+6]]:7: ERROR: Closing symbol does not match most recent opening symbol.
// CHECK:STDERR: fn Div[();
// CHECK:STDERR: ^
// CHECK:STDERR: fail_missing_implicit_close.carbon:[[@LINE+3]]:8: ERROR: Expected parameter declaration.
// CHECK:STDERR: fail_missing_implicit_close.carbon:[[@LINE+3]]:8: ERROR: Expected binding pattern.
// CHECK:STDERR: fn Div[();
// CHECK:STDERR: ^
fn Div[();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
//
// AUTOUPDATE

// CHECK:STDERR: fail_with_identifier_as_param.carbon:[[@LINE+3]]:11: ERROR: Expected parameter declaration.
// CHECK:STDERR: fail_with_identifier_as_param.carbon:[[@LINE+3]]:11: ERROR: Expected binding pattern.
// CHECK:STDERR: fn foo(bar);
// CHECK:STDERR: ^
fn foo(bar);
Expand Down
Loading

0 comments on commit 39750b9

Please sign in to comment.