Просмотр исходного кода

Choice parsing in toolchain (#3574)

Implement Choice parsing in toolchain according to
[design](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/design/sum_types.md).
kshokhin 2 лет назад
Родитель
Сommit
65e95942de

+ 30 - 0
toolchain/check/handle_choice.cpp

@@ -0,0 +1,30 @@
+// 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"
+
+namespace Carbon::Check {
+
+auto HandleChoiceDefinition(Context& context,
+                            Parse::ChoiceDefinitionId parse_node) -> bool {
+  return context.TODO(parse_node, "HandleChoiceDefinition");
+}
+
+auto HandleChoiceIntroducer(Context& context,
+                            Parse::ChoiceIntroducerId parse_node) -> bool {
+  return context.TODO(parse_node, "HandleChoiceIntroducer");
+}
+
+auto HandleChoiceDefinitionStart(Context& context,
+                                 Parse::ChoiceDefinitionStartId parse_node)
+    -> bool {
+  return context.TODO(parse_node, "HandleChoiceDefinitionStart");
+}
+
+auto HandleChoiceAlternativeListComma(
+    Context& context, Parse::ChoiceAlternativeListCommaId parse_node) -> bool {
+  return context.TODO(parse_node, "HandleChoiceAlternativeListComma");
+}
+
+}  // namespace Carbon::Check

+ 2 - 0
toolchain/diagnostics/diagnostic_kind.def

@@ -71,6 +71,8 @@ CARBON_DIAGNOSTIC_KIND(ExpectedStatementSemi)
 CARBON_DIAGNOSTIC_KIND(ExpectedStructLiteralField)
 CARBON_DIAGNOSTIC_KIND(ExpectedVarAfterReturned)
 CARBON_DIAGNOSTIC_KIND(ExpectedVariableDecl)
+CARBON_DIAGNOSTIC_KIND(ExpectedChoiceDefinition)
+CARBON_DIAGNOSTIC_KIND(ExpectedChoiceAlternativeName)
 CARBON_DIAGNOSTIC_KIND(OperatorRequiresParentheses)
 CARBON_DIAGNOSTIC_KIND(StatementOperatorAsSubExpr)
 CARBON_DIAGNOSTIC_KIND(UnaryOperatorRequiresParentheses)

+ 98 - 0
toolchain/parse/handle_choice.cpp

@@ -0,0 +1,98 @@
+// 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 HandleChoiceIntroducer(Context& context) -> void {
+  auto state = context.PopState();
+
+  context.PushState(state, State::ChoiceDefinitionStart);
+  context.PushState(State::DeclNameAndParamsAsOptional, state.token);
+}
+
+auto HandleChoiceDefinitionStart(Context& context) -> void {
+  auto state = context.PopState();
+
+  if (!context.PositionIs(Lex::TokenKind::OpenCurlyBrace)) {
+    if (!state.has_error) {
+      CARBON_DIAGNOSTIC(ExpectedChoiceDefinition, Error,
+                        "Choice definition expected.");
+      context.emitter().Emit(*context.position(), ExpectedChoiceDefinition);
+    }
+
+    context.AddNode(NodeKind::ChoiceDefinitionStart, *context.position(),
+                    state.subtree_start, /*has_error=*/true);
+
+    context.AddNode(NodeKind::ChoiceDefinition, *context.position(),
+                    state.subtree_start, /*has_error=*/true);
+
+    context.SkipPastLikelyEnd(*context.position());
+    return;
+  }
+
+  context.AddNode(NodeKind::ChoiceDefinitionStart, context.Consume(),
+                  state.subtree_start, state.has_error);
+
+  state.has_error = false;
+  state.state = State::ChoiceDefinitionFinish;
+  context.PushState(state);
+
+  if (!context.PositionIs(Lex::TokenKind::CloseCurlyBrace)) {
+    context.PushState(State::ChoiceAlternative);
+  }
+}
+
+auto HandleChoiceAlternative(Context& context) -> void {
+  auto state = context.PopState();
+
+  context.PushState(State::ChoiceAlternativeFinish);
+
+  if (!context.ConsumeAndAddLeafNodeIf(Lex::TokenKind::Identifier,
+                                       NodeKind::IdentifierName)) {
+    if (!state.has_error) {
+      CARBON_DIAGNOSTIC(ExpectedChoiceAlternativeName, Error,
+                        "Expected choice alternative name.");
+      context.emitter().Emit(*context.position(),
+                             ExpectedChoiceAlternativeName);
+    }
+
+    context.SkipPastLikelyEnd(*context.position());
+
+    context.ReturnErrorOnState();
+
+    return;
+  }
+
+  if (context.PositionIs(Lex::TokenKind::OpenParen)) {
+    context.PushState(State::PatternListAsTuple);
+  }
+}
+
+auto HandleChoiceAlternativeFinish(Context& context) -> void {
+  const auto state = context.PopState();
+
+  if (state.has_error) {
+    context.ReturnErrorOnState();
+    if (!context.PositionIs(Lex::TokenKind::CloseCurlyBrace)) {
+      context.PushState(State::ChoiceAlternative);
+    }
+    return;
+  }
+
+  if (context.ConsumeListToken(
+          NodeKind::ChoiceAlternativeListComma, Lex::TokenKind::CloseCurlyBrace,
+          state.has_error) == Context::ListTokenKind::Comma) {
+    context.PushState(State::ChoiceAlternative);
+  }
+}
+
+auto HandleChoiceDefinitionFinish(Context& context) -> void {
+  const auto state = context.PopState();
+
+  context.AddNode(NodeKind::ChoiceDefinition,
+                  context.ConsumeChecked(Lex::TokenKind::CloseCurlyBrace),
+                  state.subtree_start, state.has_error);
+}
+}  // namespace Carbon::Parse

+ 6 - 0
toolchain/parse/handle_decl_scope_loop.cpp

@@ -158,6 +158,11 @@ static auto TryHandleAsDecl(Context& context, Context::StateStackEntry state,
                       State::VarAsDecl);
       return true;
     }
+    case Lex::TokenKind::Choice: {
+      ApplyIntroducer(context, state, NodeKind::ChoiceIntroducer,
+                      State::ChoiceIntroducer);
+      return true;
+    }
 
     case Lex::TokenKind::Semi: {
       if (saw_modifier) {
@@ -201,6 +206,7 @@ static auto ResolveAmbiguousTokenAsDeclaration(Context& context,
 #define CARBON_PARSE_NODE_KIND_TOKEN_MODIFIER(Name, ...) \
   case Lex::TokenKind::Name:
 #include "toolchain/parse/node_kind.def"
+
           return false;
 
         default:

+ 20 - 0
toolchain/parse/node_kind.def

@@ -711,6 +711,26 @@ CARBON_PARSE_NODE_KIND_BRACKET(NamedConstraintDefinition,
 CARBON_PARSE_NODE_KIND_BRACKET(NamedConstraintDecl, NamedConstraintIntroducer,
                                CARBON_IF_VALID(Semi))
 
+// `choice`:
+//     ChoiceIntroducer
+//     _external_: IdentifierName or QualifiedDecl
+//   ChoiceDefinitionStart
+//   _optional_ _external_: ChoiceAlternativeList
+// ChoiceDefinition
+CARBON_PARSE_NODE_KIND_CHILD_COUNT(ChoiceIntroducer, 0, Choice)
+CARBON_PARSE_NODE_KIND_BRACKET(ChoiceDefinitionStart, ChoiceIntroducer,
+                               CARBON_IF_VALID(OpenCurlyBrace))
+CARBON_PARSE_NODE_KIND_BRACKET(ChoiceDefinition, ChoiceDefinitionStart,
+                               CARBON_IF_VALID(CloseCurlyBrace))
+
+// Choice alternative list:
+//     _external_: IdentifierName
+//     _optional_ _external_ : TuplePattern
+//     _optional_: ChoiceAlternativeListComma
+//   _repeated_
+// ChoiceAlternativeList
+CARBON_PARSE_NODE_KIND_CHILD_COUNT(ChoiceAlternativeListComma, 0, Comma)
+
 #undef CARBON_PARSE_NODE_KIND
 #undef CARBON_PARSE_NODE_KIND_BRACKET
 #undef CARBON_PARSE_NODE_KIND_CHILD_COUNT

+ 63 - 0
toolchain/parse/state.def

@@ -357,6 +357,11 @@ CARBON_PARSE_STATE(DeclNameAndParamsAfterImplicit)
 //   1. Let
 //   2. DeclScopeLoop
 //
+//  choice ...
+// ^
+//   1. ChoiceIntroducer
+//   2. DeclScopeLoop
+//
 // ??? ;
 // ^~~~~
 //   (state done)
@@ -1241,4 +1246,62 @@ CARBON_PARSE_STATE(LetAfterPattern)
 //   (state done)
 CARBON_PARSE_STATE(LetFinish)
 
+// Handles a choice's introducer.
+//
+// choice ...
+// ^~~~~~
+//   1. DeclNameAndParamsAsOptional
+//   2. ChoiceAlternativeListStart
+//   3. ChoiceDefinitionFinish
+CARBON_PARSE_STATE(ChoiceIntroducer)
+
+// Handles processing of a choice after its optional parameters.
+//
+// choice name ... {}
+//                 ^
+//   (state done)
+//
+// choice name ... { ... }
+//                 ^
+//   1. ChoiceAlternative
+//
+// choice name ... ???
+//                 ^
+//   (state done)
+CARBON_PARSE_STATE(ChoiceDefinitionStart)
+
+// Starts alternative parsing.
+//
+//  name( ... )
+//  ^~~~
+//   1. ParamListAsRegular
+//   2. ChoiceAlternativeFinish
+//  name ...
+//  ^~~~
+//   1. ChoiceAlternativeFinish
+CARBON_PARSE_STATE(ChoiceAlternative)
+
+// Finishes parsing a choice's alternative, including the optional trailing `,`. If there
+// are more alternatives, enqueues another alternative parsing state.
+//
+// ... , }
+//     ^
+//   (state done)
+//
+// ... , ...
+//     ^
+//   1. ChoiceAlternative
+//
+// ...
+//    ^
+//   (state done)
+CARBON_PARSE_STATE(ChoiceAlternativeFinish)
+
+// Finishes a choice definition.
+//
+// choice ... }
+//            ^
+//   (state done)
+CARBON_PARSE_STATE(ChoiceDefinitionFinish)
+
 #undef CARBON_PARSE_STATE

+ 29 - 0
toolchain/parse/testdata/choice/basic.carbon

@@ -0,0 +1,29 @@
+// 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
+
+choice Ordering {
+  Less,
+  Equivalent,
+  Greater,
+  Incomparable
+}
+
+// CHECK:STDOUT: - filename: basic.carbon
+// CHECK:STDOUT:   parse_tree: [
+// CHECK:STDOUT:     {kind: 'FileStart', text: ''},
+// CHECK:STDOUT:         {kind: 'ChoiceIntroducer', text: 'choice'},
+// CHECK:STDOUT:         {kind: 'IdentifierName', text: 'Ordering'},
+// CHECK:STDOUT:       {kind: 'ChoiceDefinitionStart', text: '{', subtree_size: 3},
+// CHECK:STDOUT:       {kind: 'IdentifierName', text: 'Less'},
+// CHECK:STDOUT:       {kind: 'ChoiceAlternativeListComma', text: ','},
+// CHECK:STDOUT:       {kind: 'IdentifierName', text: 'Equivalent'},
+// CHECK:STDOUT:       {kind: 'ChoiceAlternativeListComma', text: ','},
+// CHECK:STDOUT:       {kind: 'IdentifierName', text: 'Greater'},
+// CHECK:STDOUT:       {kind: 'ChoiceAlternativeListComma', text: ','},
+// CHECK:STDOUT:       {kind: 'IdentifierName', text: 'Incomparable'},
+// CHECK:STDOUT:     {kind: 'ChoiceDefinition', text: '}', subtree_size: 11},
+// CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
+// CHECK:STDOUT:   ]

+ 23 - 0
toolchain/parse/testdata/choice/fail_alternative_with_implicit_params.carbon

@@ -0,0 +1,23 @@
+// 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
+
+choice X {
+  // CHECK:STDERR: fail_alternative_with_implicit_params.carbon:[[@LINE+3]]:4: ERROR: Expected `,` or `}`.
+  // CHECK:STDERR:   A[T:! type](value: T)
+  // CHECK:STDERR:    ^
+  A[T:! type](value: T)
+}
+
+// CHECK:STDOUT: - filename: fail_alternative_with_implicit_params.carbon
+// CHECK:STDOUT:   parse_tree: [
+// CHECK:STDOUT:     {kind: 'FileStart', text: ''},
+// CHECK:STDOUT:         {kind: 'ChoiceIntroducer', text: 'choice'},
+// CHECK:STDOUT:         {kind: 'IdentifierName', text: 'X'},
+// CHECK:STDOUT:       {kind: 'ChoiceDefinitionStart', text: '{', subtree_size: 3},
+// CHECK:STDOUT:       {kind: 'IdentifierName', text: 'A'},
+// CHECK:STDOUT:     {kind: 'ChoiceDefinition', text: '}', has_error: yes, subtree_size: 5},
+// CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
+// CHECK:STDOUT:   ]

+ 28 - 0
toolchain/parse/testdata/choice/fail_invalid_alternative_identifier.carbon

@@ -0,0 +1,28 @@
+// 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
+
+choice InvalidAlternativeIdentifier {
+    // CHECK:STDERR: fail_invalid_alternative_identifier.carbon:[[@LINE+3]]:5: ERROR: Expected choice alternative name.
+    // CHECK:STDERR:     ,Some,
+    // CHECK:STDERR:     ^
+    ,Some,
+    // CHECK:STDERR: fail_invalid_alternative_identifier.carbon:[[@LINE+3]]:5: ERROR: Expected choice alternative name.
+    // CHECK:STDERR:     42,
+    // CHECK:STDERR:     ^~
+    42,
+    Other
+}
+
+// CHECK:STDOUT: - filename: fail_invalid_alternative_identifier.carbon
+// CHECK:STDOUT:   parse_tree: [
+// CHECK:STDOUT:     {kind: 'FileStart', text: ''},
+// CHECK:STDOUT:         {kind: 'ChoiceIntroducer', text: 'choice'},
+// CHECK:STDOUT:         {kind: 'IdentifierName', text: 'InvalidAlternativeIdentifier'},
+// CHECK:STDOUT:       {kind: 'ChoiceDefinitionStart', text: '{', subtree_size: 3},
+// CHECK:STDOUT:       {kind: 'IdentifierName', text: 'Other'},
+// CHECK:STDOUT:     {kind: 'ChoiceDefinition', text: '}', has_error: yes, subtree_size: 5},
+// CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
+// CHECK:STDOUT:   ]

+ 27 - 0
toolchain/parse/testdata/choice/fail_invalid_braces.carbon

@@ -0,0 +1,27 @@
+// 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
+
+// CHECK:STDERR: fail_invalid_braces.carbon:[[@LINE+3]]:22: ERROR: Choice definition expected.
+// CHECK:STDERR: choice InvalidBraces |
+// CHECK:STDERR:                      ^
+choice InvalidBraces |
+  Some
+// CHECK:STDERR: fail_invalid_braces.carbon:[[@LINE+3]]:1: ERROR: Unrecognized declaration introducer.
+// CHECK:STDERR: |
+// CHECK:STDERR: ^
+|
+
+// CHECK:STDOUT: - filename: fail_invalid_braces.carbon
+// CHECK:STDOUT:   parse_tree: [
+// CHECK:STDOUT:     {kind: 'FileStart', text: ''},
+// CHECK:STDOUT:         {kind: 'ChoiceIntroducer', text: 'choice'},
+// CHECK:STDOUT:         {kind: 'IdentifierName', text: 'InvalidBraces'},
+// CHECK:STDOUT:       {kind: 'ChoiceDefinitionStart', text: '|', has_error: yes, subtree_size: 3},
+// CHECK:STDOUT:     {kind: 'ChoiceDefinition', text: '|', has_error: yes, subtree_size: 4},
+// CHECK:STDOUT:       {kind: 'InvalidParseStart', text: '|', has_error: yes},
+// CHECK:STDOUT:     {kind: 'InvalidParseSubtree', text: '|', has_error: yes, subtree_size: 2},
+// CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
+// CHECK:STDOUT:   ]

+ 20 - 0
toolchain/parse/testdata/choice/fail_missing_definition.carbon

@@ -0,0 +1,20 @@
+// 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
+
+// CHECK:STDERR: fail_missing_definition.carbon:[[@LINE+3]]:25: ERROR: Choice definition expected.
+// CHECK:STDERR: choice MissingDefinition;
+// CHECK:STDERR:                         ^
+choice MissingDefinition;
+
+// CHECK:STDOUT: - filename: fail_missing_definition.carbon
+// CHECK:STDOUT:   parse_tree: [
+// CHECK:STDOUT:     {kind: 'FileStart', text: ''},
+// CHECK:STDOUT:         {kind: 'ChoiceIntroducer', text: 'choice'},
+// CHECK:STDOUT:         {kind: 'IdentifierName', text: 'MissingDefinition'},
+// CHECK:STDOUT:       {kind: 'ChoiceDefinitionStart', text: ';', has_error: yes, subtree_size: 3},
+// CHECK:STDOUT:     {kind: 'ChoiceDefinition', text: ';', has_error: yes, subtree_size: 4},
+// CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
+// CHECK:STDOUT:   ]

+ 25 - 0
toolchain/parse/testdata/choice/fail_missing_definition_parameterized.carbon

@@ -0,0 +1,25 @@
+// 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
+
+// CHECK:STDERR: fail_missing_definition_parameterized.carbon:[[@LINE+3]]:35: ERROR: Choice definition expected.
+// CHECK:STDERR: choice MissingDefinition(T:! type);
+// CHECK:STDERR:                                   ^
+choice MissingDefinition(T:! type);
+
+// CHECK:STDOUT: - filename: fail_missing_definition_parameterized.carbon
+// CHECK:STDOUT:   parse_tree: [
+// CHECK:STDOUT:     {kind: 'FileStart', text: ''},
+// CHECK:STDOUT:         {kind: 'ChoiceIntroducer', text: 'choice'},
+// CHECK:STDOUT:         {kind: 'IdentifierName', text: 'MissingDefinition'},
+// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:             {kind: 'IdentifierName', text: 'T'},
+// CHECK:STDOUT:             {kind: 'TypeTypeLiteral', text: 'type'},
+// CHECK:STDOUT:           {kind: 'GenericBindingPattern', text: ':!', subtree_size: 3},
+// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 5},
+// CHECK:STDOUT:       {kind: 'ChoiceDefinitionStart', text: ';', has_error: yes, subtree_size: 8},
+// CHECK:STDOUT:     {kind: 'ChoiceDefinition', text: ';', has_error: yes, subtree_size: 9},
+// CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
+// CHECK:STDOUT:   ]

+ 23 - 0
toolchain/parse/testdata/choice/fail_qualified_alternative_name.carbon

@@ -0,0 +1,23 @@
+// 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
+
+choice X {
+  // CHECK:STDERR: fail_qualified_alternative_name.carbon:[[@LINE+3]]:4: ERROR: Expected `,` or `}`.
+  // CHECK:STDERR:   A.B
+  // CHECK:STDERR:    ^
+  A.B
+}
+
+// CHECK:STDOUT: - filename: fail_qualified_alternative_name.carbon
+// CHECK:STDOUT:   parse_tree: [
+// CHECK:STDOUT:     {kind: 'FileStart', text: ''},
+// CHECK:STDOUT:         {kind: 'ChoiceIntroducer', text: 'choice'},
+// CHECK:STDOUT:         {kind: 'IdentifierName', text: 'X'},
+// CHECK:STDOUT:       {kind: 'ChoiceDefinitionStart', text: '{', subtree_size: 3},
+// CHECK:STDOUT:       {kind: 'IdentifierName', text: 'A'},
+// CHECK:STDOUT:     {kind: 'ChoiceDefinition', text: '}', has_error: yes, subtree_size: 5},
+// CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
+// CHECK:STDOUT:   ]

+ 30 - 0
toolchain/parse/testdata/choice/modifiers.carbon

@@ -0,0 +1,30 @@
+// 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
+
+private choice Ordering {
+  Less,
+  Equivalent,
+  Greater,
+  Incomparable
+}
+
+// CHECK:STDOUT: - filename: modifiers.carbon
+// CHECK:STDOUT:   parse_tree: [
+// CHECK:STDOUT:     {kind: 'FileStart', text: ''},
+// CHECK:STDOUT:         {kind: 'ChoiceIntroducer', text: 'choice'},
+// CHECK:STDOUT:         {kind: 'PrivateModifier', text: 'private'},
+// CHECK:STDOUT:         {kind: 'IdentifierName', text: 'Ordering'},
+// CHECK:STDOUT:       {kind: 'ChoiceDefinitionStart', text: '{', subtree_size: 4},
+// CHECK:STDOUT:       {kind: 'IdentifierName', text: 'Less'},
+// CHECK:STDOUT:       {kind: 'ChoiceAlternativeListComma', text: ','},
+// CHECK:STDOUT:       {kind: 'IdentifierName', text: 'Equivalent'},
+// CHECK:STDOUT:       {kind: 'ChoiceAlternativeListComma', text: ','},
+// CHECK:STDOUT:       {kind: 'IdentifierName', text: 'Greater'},
+// CHECK:STDOUT:       {kind: 'ChoiceAlternativeListComma', text: ','},
+// CHECK:STDOUT:       {kind: 'IdentifierName', text: 'Incomparable'},
+// CHECK:STDOUT:     {kind: 'ChoiceDefinition', text: '}', subtree_size: 12},
+// CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
+// CHECK:STDOUT:   ]

+ 34 - 0
toolchain/parse/testdata/choice/parameterized.carbon

@@ -0,0 +1,34 @@
+// 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
+
+choice OptionalElement(T:! type) {
+  Element(value: T),
+  None,
+}
+
+// CHECK:STDOUT: - filename: parameterized.carbon
+// CHECK:STDOUT:   parse_tree: [
+// CHECK:STDOUT:     {kind: 'FileStart', text: ''},
+// CHECK:STDOUT:         {kind: 'ChoiceIntroducer', text: 'choice'},
+// CHECK:STDOUT:         {kind: 'IdentifierName', text: 'OptionalElement'},
+// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:             {kind: 'IdentifierName', text: 'T'},
+// CHECK:STDOUT:             {kind: 'TypeTypeLiteral', text: 'type'},
+// CHECK:STDOUT:           {kind: 'GenericBindingPattern', text: ':!', subtree_size: 3},
+// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 5},
+// CHECK:STDOUT:       {kind: 'ChoiceDefinitionStart', text: '{', subtree_size: 8},
+// CHECK:STDOUT:       {kind: 'IdentifierName', text: 'Element'},
+// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:           {kind: 'IdentifierName', text: 'value'},
+// CHECK:STDOUT:           {kind: 'IdentifierNameExpr', text: 'T'},
+// CHECK:STDOUT:         {kind: 'BindingPattern', text: ':', subtree_size: 3},
+// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', subtree_size: 5},
+// CHECK:STDOUT:       {kind: 'ChoiceAlternativeListComma', text: ','},
+// CHECK:STDOUT:       {kind: 'IdentifierName', text: 'None'},
+// CHECK:STDOUT:       {kind: 'ChoiceAlternativeListComma', text: ','},
+// CHECK:STDOUT:     {kind: 'ChoiceDefinition', text: '}', subtree_size: 18},
+// CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
+// CHECK:STDOUT:   ]

+ 33 - 0
toolchain/parse/typed_nodes.h

@@ -677,6 +677,39 @@ struct IfExprElse {
   AnyExprId else_result;
 };
 
+// Choice nodes
+// ------------
+
+using ChoiceIntroducer = LeafNode<NodeKind::ChoiceIntroducer>;
+
+struct ChoiceSignature {
+  static constexpr auto Kind =
+      NodeKind::ChoiceDefinitionStart.Define(NodeCategory::None);
+
+  ChoiceIntroducerId introducer;
+  llvm::SmallVector<AnyModifierId> modifiers;
+  AnyNameComponentId name;
+  std::optional<ImplicitParamListId> implicit_params;
+  std::optional<TuplePatternId> params;
+};
+
+using ChoiceDefinitionStart = ChoiceSignature;
+
+using ChoiceAlternativeListComma =
+    LeafNode<NodeKind::ChoiceAlternativeListComma>;
+
+struct ChoiceDefinition {
+  static constexpr auto Kind =
+      NodeKind::ChoiceDefinition.Define(NodeCategory::Decl);
+
+  ChoiceDefinitionStartId signature;
+  struct Alternative {
+    IdentifierNameId name;
+    std::optional<TuplePatternId> parameters;
+  };
+  CommaSeparatedList<Alternative, ChoiceAlternativeListCommaId> alternatives;
+};
+
 // Struct literals and struct type literals
 // ----------------------------------------