Browse Source

Change tuple/paren expr parsing to use placeholders. (#3849)

I've been thinking about this since we decided to add placeholders in
the parse tree. This allows a clearer division of work in check
handling, where we were doing work for ExprOpenParen that's only
necessary for tuples (splitting/renaming handle_paren.cpp accordingly).
Jon Ross-Perkins 2 years ago
parent
commit
b42612bcec

+ 22 - 0
toolchain/check/handle_paren_expr.cpp

@@ -0,0 +1,22 @@
+// 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 HandleParenExprStart(Context& /*context*/,
+                          Parse::ParenExprStartId /*node_id*/) -> bool {
+  // The open paren is unused.
+  return true;
+}
+
+auto HandleParenExpr(Context& context, Parse::ParenExprId node_id) -> bool {
+  // We re-push because the ParenExpr is valid for member expressions, whereas
+  // the child expression might not be.
+  context.node_stack().Push(node_id, context.node_stack().PopExpr());
+  return true;
+}
+
+}  // namespace Carbon::Check

+ 4 - 17
toolchain/check/handle_paren.cpp → toolchain/check/handle_tuple_literal.cpp

@@ -6,26 +6,13 @@
 
 namespace Carbon::Check {
 
-auto HandleExprOpenParen(Context& context, Parse::ExprOpenParenId node_id)
-    -> bool {
+auto HandleTupleLiteralStart(Context& context,
+                             Parse::TupleLiteralStartId node_id) -> bool {
   context.node_stack().Push(node_id);
   context.param_and_arg_refs_stack().Push();
   return true;
 }
 
-auto HandleParenExpr(Context& context, Parse::ParenExprId node_id) -> bool {
-  auto value_id = context.node_stack().PopExpr();
-
-  // This always is always pushed at the open paren. It's only used for tuples,
-  // not paren exprs, but we still need to clean up.
-  context.param_and_arg_refs_stack().PopAndDiscard();
-
-  context.node_stack()
-      .PopAndDiscardSoloNodeId<Parse::NodeKind::ExprOpenParen>();
-  context.node_stack().Push(node_id, value_id);
-  return true;
-}
-
 auto HandleTupleLiteralComma(Context& context,
                              Parse::TupleLiteralCommaId /*node_id*/) -> bool {
   context.param_and_arg_refs_stack().ApplyComma();
@@ -35,10 +22,10 @@ auto HandleTupleLiteralComma(Context& context,
 auto HandleTupleLiteral(Context& context, Parse::TupleLiteralId node_id)
     -> bool {
   auto refs_id = context.param_and_arg_refs_stack().EndAndPop(
-      Parse::NodeKind::ExprOpenParen);
+      Parse::NodeKind::TupleLiteralStart);
 
   context.node_stack()
-      .PopAndDiscardSoloNodeId<Parse::NodeKind::ExprOpenParen>();
+      .PopAndDiscardSoloNodeId<Parse::NodeKind::TupleLiteralStart>();
   const auto& inst_block = context.inst_blocks().Get(refs_id);
   llvm::SmallVector<SemIR::TypeId> type_ids;
   type_ids.reserve(inst_block.size());

+ 2 - 1
toolchain/check/node_stack.h

@@ -438,7 +438,6 @@ class NodeStack {
         case Parse::NodeKind::BuiltinName:
         case Parse::NodeKind::ClassIntroducer:
         case Parse::NodeKind::CodeBlockStart:
-        case Parse::NodeKind::ExprOpenParen:
         case Parse::NodeKind::FunctionIntroducer:
         case Parse::NodeKind::IfStatementElse:
         case Parse::NodeKind::ImplicitParamListStart:
@@ -446,11 +445,13 @@ class NodeStack {
         case Parse::NodeKind::InterfaceIntroducer:
         case Parse::NodeKind::LetInitializer:
         case Parse::NodeKind::LetIntroducer:
+        case Parse::NodeKind::ParenExprStart:
         case Parse::NodeKind::QualifiedName:
         case Parse::NodeKind::ReturnedModifier:
         case Parse::NodeKind::ReturnStatementStart:
         case Parse::NodeKind::ReturnVarModifier:
         case Parse::NodeKind::StructLiteralOrStructTypeLiteralStart:
+        case Parse::NodeKind::TupleLiteralStart:
         case Parse::NodeKind::TuplePatternStart:
         case Parse::NodeKind::VariableInitializer:
         case Parse::NodeKind::VariableIntroducer:

+ 8 - 3
toolchain/parse/handle_paren_expr.cpp

@@ -11,7 +11,7 @@ auto HandleOnlyParenExpr(Context& context) -> void {
 
   // Advance past the open paren.
   auto open_paren = context.ConsumeChecked(Lex::TokenKind::OpenParen);
-  context.AddLeafNode(NodeKind::ExprOpenParen, open_paren);
+  context.AddLeafNode(NodeKind::ParenExprStart, open_paren);
 
   state.token = open_paren;
   context.PushState(state, State::OnlyParenExprFinish);
@@ -46,8 +46,9 @@ auto HandleOnlyParenExprFinish(Context& context) -> void {
 auto HandleParenExpr(Context& context) -> void {
   auto state = context.PopState();
 
-  // Advance past the open paren.
-  context.AddLeafNode(NodeKind::ExprOpenParen,
+  // Advance past the open paren. The placeholder will be replaced at the end
+  // based on whether we determine this is a tuple or parenthesized expression.
+  context.AddLeafNode(NodeKind::Placeholder,
                       context.ConsumeChecked(Lex::TokenKind::OpenParen));
 
   if (context.PositionIs(Lex::TokenKind::CloseParen)) {
@@ -96,12 +97,16 @@ auto HandleTupleLiteralElementFinish(Context& context) -> void {
 auto HandleParenExprFinish(Context& context) -> void {
   auto state = context.PopState();
 
+  context.ReplacePlaceholderNode(state.subtree_start, NodeKind::ParenExprStart,
+                                 state.token);
   FinishParenExpr(context, state);
 }
 
 auto HandleTupleLiteralFinish(Context& context) -> void {
   auto state = context.PopState();
 
+  context.ReplacePlaceholderNode(state.subtree_start,
+                                 NodeKind::TupleLiteralStart, state.token);
   context.AddNode(NodeKind::TupleLiteral, context.Consume(),
                   state.subtree_start, state.has_error);
 }

+ 7 - 6
toolchain/parse/node_kind.def

@@ -450,12 +450,14 @@ CARBON_PARSE_NODE_KIND_CHILD_COUNT(IndexExprStart, 1, OpenSquareBracket)
 CARBON_PARSE_NODE_KIND_BRACKET(IndexExpr, IndexExprStart, CloseSquareBracket)
 
 // Parenthesized single expressions, such as `(2)`:
-//   ExprOpenParen
+//   ParenExprStart
 //   _external_: expression
 // ParenExpr
-//
+CARBON_PARSE_NODE_KIND_CHILD_COUNT(ParenExprStart, 0, OpenParen)
+CARBON_PARSE_NODE_KIND_BRACKET(ParenExpr, ParenExprStart, CloseParen)
+
 // Tuples, such as `(1, 2)`:
-//   ExprOpenParen
+//   TupleLiteralStart
 //     _external_: expression
 //     TupleLiteralComma
 //   _repeated_
@@ -463,10 +465,9 @@ CARBON_PARSE_NODE_KIND_BRACKET(IndexExpr, IndexExprStart, CloseSquareBracket)
 //
 // Expressions and TupleLiteralComma may repeat with TupleLiteralComma as a
 // separator.
-CARBON_PARSE_NODE_KIND_CHILD_COUNT(ExprOpenParen, 0, OpenParen)
-CARBON_PARSE_NODE_KIND_BRACKET(ParenExpr, ExprOpenParen, CloseParen)
+CARBON_PARSE_NODE_KIND_CHILD_COUNT(TupleLiteralStart, 0, OpenParen)
 CARBON_PARSE_NODE_KIND_CHILD_COUNT(TupleLiteralComma, 0, Comma)
-CARBON_PARSE_NODE_KIND_BRACKET(TupleLiteral, ExprOpenParen, CloseParen)
+CARBON_PARSE_NODE_KIND_BRACKET(TupleLiteral, TupleLiteralStart, CloseParen)
 
 // Call expressions, such as `a()`:
 //     _external_: expression

+ 1 - 1
toolchain/parse/testdata/basics/fail_paren_match_regression.carbon

@@ -25,7 +25,7 @@ var = (foo {})
 // CHECK:STDOUT:         {kind: 'InvalidParse', text: '=', has_error: yes},
 // CHECK:STDOUT:       {kind: 'BindingPattern', text: '=', has_error: yes, subtree_size: 3},
 // CHECK:STDOUT:       {kind: 'VariableInitializer', text: '='},
-// CHECK:STDOUT:         {kind: 'ExprOpenParen', text: '('},
+// CHECK:STDOUT:         {kind: 'ParenExprStart', text: '('},
 // CHECK:STDOUT:         {kind: 'IdentifierNameExpr', text: 'foo'},
 // CHECK:STDOUT:       {kind: 'ParenExpr', text: ')', has_error: yes, subtree_size: 3},
 // CHECK:STDOUT:     {kind: 'VariableDecl', text: ')', has_error: yes, subtree_size: 9},

+ 1 - 1
toolchain/parse/testdata/basics/function_call.carbon

@@ -27,7 +27,7 @@ fn F() {
 // CHECK:STDOUT:                   {kind: 'IdentifierName', text: 'd'},
 // CHECK:STDOUT:                 {kind: 'MemberAccessExpr', text: '.', subtree_size: 3},
 // CHECK:STDOUT:                 {kind: 'CallExprComma', text: ','},
-// CHECK:STDOUT:                   {kind: 'ExprOpenParen', text: '('},
+// CHECK:STDOUT:                   {kind: 'ParenExprStart', text: '('},
 // CHECK:STDOUT:                   {kind: 'IdentifierNameExpr', text: 'e'},
 // CHECK:STDOUT:                 {kind: 'ParenExpr', text: ')', subtree_size: 3},
 // CHECK:STDOUT:               {kind: 'CallExpr', text: ')', subtree_size: 14},

+ 2 - 2
toolchain/parse/testdata/basics/numeric_literals.carbon

@@ -42,7 +42,7 @@ fn F() {
 // CHECK:STDOUT:           {kind: 'ArrayExpr', text: ']', subtree_size: 5},
 // CHECK:STDOUT:         {kind: 'BindingPattern', text: ':', subtree_size: 7},
 // CHECK:STDOUT:         {kind: 'VariableInitializer', text: '='},
-// CHECK:STDOUT:           {kind: 'ExprOpenParen', text: '('},
+// CHECK:STDOUT:           {kind: 'TupleLiteralStart', text: '('},
 // CHECK:STDOUT:           {kind: 'IntLiteral', text: '8'},
 // CHECK:STDOUT:           {kind: 'TupleLiteralComma', text: ','},
 // CHECK:STDOUT:           {kind: 'IntLiteral', text: '9'},
@@ -64,7 +64,7 @@ fn F() {
 // CHECK:STDOUT:           {kind: 'ArrayExpr', text: ']', subtree_size: 5},
 // CHECK:STDOUT:         {kind: 'BindingPattern', text: ':', subtree_size: 7},
 // CHECK:STDOUT:         {kind: 'VariableInitializer', text: '='},
-// CHECK:STDOUT:           {kind: 'ExprOpenParen', text: '('},
+// CHECK:STDOUT:           {kind: 'TupleLiteralStart', text: '('},
 // CHECK:STDOUT:           {kind: 'RealLiteral', text: '0.9'},
 // CHECK:STDOUT:           {kind: 'TupleLiteralComma', text: ','},
 // CHECK:STDOUT:           {kind: 'RealLiteral', text: '8.0'},

+ 4 - 4
toolchain/parse/testdata/basics/parens.carbon

@@ -22,10 +22,10 @@ fn F(n: i32) -> i32 {
 // CHECK:STDOUT:         {kind: 'ReturnType', text: '->', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 10},
 // CHECK:STDOUT:         {kind: 'ReturnStatementStart', text: 'return'},
-// CHECK:STDOUT:           {kind: 'ExprOpenParen', text: '('},
-// CHECK:STDOUT:             {kind: 'ExprOpenParen', text: '('},
-// CHECK:STDOUT:               {kind: 'ExprOpenParen', text: '('},
-// CHECK:STDOUT:                 {kind: 'ExprOpenParen', text: '('},
+// CHECK:STDOUT:           {kind: 'ParenExprStart', text: '('},
+// CHECK:STDOUT:             {kind: 'ParenExprStart', text: '('},
+// CHECK:STDOUT:               {kind: 'ParenExprStart', text: '('},
+// CHECK:STDOUT:                 {kind: 'ParenExprStart', text: '('},
 // CHECK:STDOUT:                 {kind: 'IdentifierNameExpr', text: 'n'},
 // CHECK:STDOUT:               {kind: 'ParenExpr', text: ')', subtree_size: 3},
 // CHECK:STDOUT:             {kind: 'ParenExpr', text: ')', subtree_size: 5},

+ 1 - 1
toolchain/parse/testdata/let/let_tuple.carbon

@@ -27,7 +27,7 @@ fn F() {
 // CHECK:STDOUT:           {kind: 'BindingPattern', text: ':', subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 9},
 // CHECK:STDOUT:         {kind: 'LetInitializer', text: '='},
-// CHECK:STDOUT:           {kind: 'ExprOpenParen', text: '('},
+// CHECK:STDOUT:           {kind: 'TupleLiteralStart', text: '('},
 // CHECK:STDOUT:           {kind: 'StringLiteral', text: '"hello"'},
 // CHECK:STDOUT:           {kind: 'TupleLiteralComma', text: ','},
 // CHECK:STDOUT:           {kind: 'IntLiteral', text: '0'},

+ 2 - 2
toolchain/parse/testdata/member_access/compound.carbon

@@ -18,13 +18,13 @@ fn F() {
 // CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:           {kind: 'IdentifierNameExpr', text: 'a'},
-// CHECK:STDOUT:             {kind: 'ExprOpenParen', text: '('},
+// CHECK:STDOUT:             {kind: 'ParenExprStart', text: '('},
 // CHECK:STDOUT:             {kind: 'IdentifierNameExpr', text: 'b'},
 // CHECK:STDOUT:           {kind: 'ParenExpr', text: ')', subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'MemberAccessExpr', text: '.', subtree_size: 5},
 // CHECK:STDOUT:       {kind: 'ExprStatement', text: ';', subtree_size: 6},
 // CHECK:STDOUT:           {kind: 'IdentifierNameExpr', text: 'a'},
-// CHECK:STDOUT:             {kind: 'ExprOpenParen', text: '('},
+// CHECK:STDOUT:             {kind: 'ParenExprStart', text: '('},
 // CHECK:STDOUT:             {kind: 'IdentifierNameExpr', text: 'b'},
 // CHECK:STDOUT:           {kind: 'ParenExpr', text: ')', subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'PointerMemberAccessExpr', text: '->', subtree_size: 5},

+ 2 - 2
toolchain/parse/testdata/operators/fail_precedence_assign.carbon

@@ -47,12 +47,12 @@ fn F() {
 // CHECK:STDOUT:         {kind: 'BindingPattern', text: ':', subtree_size: 3},
 // CHECK:STDOUT:       {kind: 'VariableDecl', text: ';', subtree_size: 5},
 // CHECK:STDOUT:           {kind: 'IntLiteral', text: '1'},
-// CHECK:STDOUT:             {kind: 'ExprOpenParen', text: '('},
+// CHECK:STDOUT:             {kind: 'ParenExprStart', text: '('},
 // CHECK:STDOUT:             {kind: 'IdentifierNameExpr', text: 'a'},
 // CHECK:STDOUT:           {kind: 'ParenExpr', text: ')', has_error: yes, subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'InfixOperatorPlus', text: '+', subtree_size: 5},
 // CHECK:STDOUT:       {kind: 'ExprStatement', text: ';', subtree_size: 6},
-// CHECK:STDOUT:           {kind: 'ExprOpenParen', text: '('},
+// CHECK:STDOUT:           {kind: 'ParenExprStart', text: '('},
 // CHECK:STDOUT:               {kind: 'BoolLiteralTrue', text: 'true'},
 // CHECK:STDOUT:             {kind: 'IfExprIf', text: 'if', subtree_size: 2},
 // CHECK:STDOUT:               {kind: 'IdentifierNameExpr', text: 'a'},

+ 1 - 1
toolchain/parse/testdata/operators/infix_with_paren_after.carbon

@@ -15,7 +15,7 @@ var n: i8 = 3*(n);
 // CHECK:STDOUT:       {kind: 'BindingPattern', text: ':', subtree_size: 3},
 // CHECK:STDOUT:       {kind: 'VariableInitializer', text: '='},
 // CHECK:STDOUT:         {kind: 'IntLiteral', text: '3'},
-// CHECK:STDOUT:           {kind: 'ExprOpenParen', text: '('},
+// CHECK:STDOUT:           {kind: 'ParenExprStart', text: '('},
 // CHECK:STDOUT:           {kind: 'IdentifierNameExpr', text: 'n'},
 // CHECK:STDOUT:         {kind: 'ParenExpr', text: ')', subtree_size: 3},
 // CHECK:STDOUT:       {kind: 'InfixOperatorStar', text: '*', subtree_size: 5},

+ 1 - 1
toolchain/parse/testdata/operators/infix_with_paren_before.carbon

@@ -14,7 +14,7 @@ var n: i8 = (n)*3;
 // CHECK:STDOUT:         {kind: 'IntTypeLiteral', text: 'i8'},
 // CHECK:STDOUT:       {kind: 'BindingPattern', text: ':', subtree_size: 3},
 // CHECK:STDOUT:       {kind: 'VariableInitializer', text: '='},
-// CHECK:STDOUT:           {kind: 'ExprOpenParen', text: '('},
+// CHECK:STDOUT:           {kind: 'ParenExprStart', text: '('},
 // CHECK:STDOUT:           {kind: 'IdentifierNameExpr', text: 'n'},
 // CHECK:STDOUT:         {kind: 'ParenExpr', text: ')', subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'IntLiteral', text: '3'},

+ 1 - 1
toolchain/parse/testdata/operators/precedence_assign.carbon

@@ -50,7 +50,7 @@ fn F(c: bool) {
 // CHECK:STDOUT:           {kind: 'IfExprElse', text: 'else', subtree_size: 6},
 // CHECK:STDOUT:         {kind: 'InfixOperatorEqual', text: '=', subtree_size: 9},
 // CHECK:STDOUT:       {kind: 'ExprStatement', text: ';', subtree_size: 10},
-// CHECK:STDOUT:             {kind: 'ExprOpenParen', text: '('},
+// CHECK:STDOUT:             {kind: 'ParenExprStart', text: '('},
 // CHECK:STDOUT:                 {kind: 'IdentifierNameExpr', text: 'c'},
 // CHECK:STDOUT:               {kind: 'IfExprIf', text: 'if', subtree_size: 2},
 // CHECK:STDOUT:                 {kind: 'IdentifierNameExpr', text: 'a'},

+ 2 - 2
toolchain/parse/testdata/pointer/const_pointer.carbon

@@ -30,7 +30,7 @@ fn C() -> const (i32*) { return C(); }
 // CHECK:STDOUT:         {kind: 'IdentifierName', text: 'B'},
 // CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
 // CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
-// CHECK:STDOUT:               {kind: 'ExprOpenParen', text: '('},
+// CHECK:STDOUT:               {kind: 'ParenExprStart', text: '('},
 // CHECK:STDOUT:                 {kind: 'IntTypeLiteral', text: 'i32'},
 // CHECK:STDOUT:               {kind: 'PrefixOperatorConst', text: 'const', subtree_size: 2},
 // CHECK:STDOUT:             {kind: 'ParenExpr', text: ')', subtree_size: 4},
@@ -47,7 +47,7 @@ fn C() -> const (i32*) { return C(); }
 // CHECK:STDOUT:         {kind: 'IdentifierName', text: 'C'},
 // CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
 // CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
-// CHECK:STDOUT:               {kind: 'ExprOpenParen', text: '('},
+// CHECK:STDOUT:               {kind: 'ParenExprStart', text: '('},
 // CHECK:STDOUT:                 {kind: 'IntTypeLiteral', text: 'i32'},
 // CHECK:STDOUT:               {kind: 'PostfixOperatorStar', text: '*', subtree_size: 2},
 // CHECK:STDOUT:             {kind: 'ParenExpr', text: ')', subtree_size: 4},

+ 8 - 8
toolchain/parse/testdata/pointer/fail_tuple_instead_of_compound_member_access.carbon

@@ -62,49 +62,49 @@ fn F() {
 // CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:           {kind: 'IdentifierNameExpr', text: 'x'},
-// CHECK:STDOUT:             {kind: 'ExprOpenParen', text: '('},
+// CHECK:STDOUT:             {kind: 'ParenExprStart', text: '('},
 // CHECK:STDOUT:             {kind: 'InvalidParse', text: ')', has_error: yes},
 // CHECK:STDOUT:           {kind: 'ParenExpr', text: ')', has_error: yes, subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'MemberAccessExpr', text: '.', subtree_size: 5},
 // CHECK:STDOUT:       {kind: 'ExprStatement', text: ';', subtree_size: 6},
 // CHECK:STDOUT:           {kind: 'IdentifierNameExpr', text: 'y'},
-// CHECK:STDOUT:             {kind: 'ExprOpenParen', text: '('},
+// CHECK:STDOUT:             {kind: 'ParenExprStart', text: '('},
 // CHECK:STDOUT:             {kind: 'IntLiteral', text: '1'},
 // CHECK:STDOUT:           {kind: 'ParenExpr', text: ')', has_error: yes, subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'MemberAccessExpr', text: '.', subtree_size: 5},
 // CHECK:STDOUT:       {kind: 'ExprStatement', text: ';', subtree_size: 6},
 // CHECK:STDOUT:           {kind: 'IdentifierNameExpr', text: 'z'},
-// CHECK:STDOUT:             {kind: 'ExprOpenParen', text: '('},
+// CHECK:STDOUT:             {kind: 'ParenExprStart', text: '('},
 // CHECK:STDOUT:             {kind: 'IdentifierNameExpr', text: 'a'},
 // CHECK:STDOUT:           {kind: 'ParenExpr', text: ')', has_error: yes, subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'MemberAccessExpr', text: '.', subtree_size: 5},
 // CHECK:STDOUT:       {kind: 'ExprStatement', text: ';', subtree_size: 6},
 // CHECK:STDOUT:           {kind: 'IdentifierNameExpr', text: 'w'},
-// CHECK:STDOUT:             {kind: 'ExprOpenParen', text: '('},
+// CHECK:STDOUT:             {kind: 'ParenExprStart', text: '('},
 // CHECK:STDOUT:             {kind: 'IdentifierNameExpr', text: 'c'},
 // CHECK:STDOUT:           {kind: 'ParenExpr', text: ')', has_error: yes, subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'MemberAccessExpr', text: '.', subtree_size: 5},
 // CHECK:STDOUT:       {kind: 'ExprStatement', text: ';', subtree_size: 6},
 // CHECK:STDOUT:           {kind: 'IdentifierNameExpr', text: 'v'},
-// CHECK:STDOUT:             {kind: 'ExprOpenParen', text: '('},
+// CHECK:STDOUT:             {kind: 'ParenExprStart', text: '('},
 // CHECK:STDOUT:             {kind: 'InvalidParse', text: ')', has_error: yes},
 // CHECK:STDOUT:           {kind: 'ParenExpr', text: ')', has_error: yes, subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'PointerMemberAccessExpr', text: '->', subtree_size: 5},
 // CHECK:STDOUT:       {kind: 'ExprStatement', text: ';', subtree_size: 6},
 // CHECK:STDOUT:           {kind: 'IdentifierNameExpr', text: 'u'},
-// CHECK:STDOUT:             {kind: 'ExprOpenParen', text: '('},
+// CHECK:STDOUT:             {kind: 'ParenExprStart', text: '('},
 // CHECK:STDOUT:             {kind: 'IdentifierNameExpr', text: 'f'},
 // CHECK:STDOUT:           {kind: 'ParenExpr', text: ')', has_error: yes, subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'PointerMemberAccessExpr', text: '->', subtree_size: 5},
 // CHECK:STDOUT:       {kind: 'ExprStatement', text: ';', subtree_size: 6},
 // CHECK:STDOUT:           {kind: 'IdentifierNameExpr', text: 't'},
-// CHECK:STDOUT:             {kind: 'ExprOpenParen', text: '('},
+// CHECK:STDOUT:             {kind: 'ParenExprStart', text: '('},
 // CHECK:STDOUT:             {kind: 'IntLiteral', text: '2'},
 // CHECK:STDOUT:           {kind: 'ParenExpr', text: ')', has_error: yes, subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'PointerMemberAccessExpr', text: '->', subtree_size: 5},
 // CHECK:STDOUT:       {kind: 'ExprStatement', text: ';', subtree_size: 6},
 // CHECK:STDOUT:           {kind: 'IdentifierNameExpr', text: 's'},
-// CHECK:STDOUT:             {kind: 'ExprOpenParen', text: '('},
+// CHECK:STDOUT:             {kind: 'ParenExprStart', text: '('},
 // CHECK:STDOUT:             {kind: 'IdentifierNameExpr', text: 'g'},
 // CHECK:STDOUT:           {kind: 'ParenExpr', text: ')', has_error: yes, subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'PointerMemberAccessExpr', text: '->', subtree_size: 5},

+ 4 - 4
toolchain/parse/testdata/tuple/nested.carbon

@@ -11,14 +11,14 @@ var y: ((), (), ());
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:       {kind: 'VariableIntroducer', text: 'var'},
 // CHECK:STDOUT:         {kind: 'IdentifierName', text: 'y'},
-// CHECK:STDOUT:           {kind: 'ExprOpenParen', text: '('},
-// CHECK:STDOUT:             {kind: 'ExprOpenParen', text: '('},
+// CHECK:STDOUT:           {kind: 'TupleLiteralStart', text: '('},
+// CHECK:STDOUT:             {kind: 'TupleLiteralStart', text: '('},
 // CHECK:STDOUT:           {kind: 'TupleLiteral', text: ')', subtree_size: 2},
 // CHECK:STDOUT:           {kind: 'TupleLiteralComma', text: ','},
-// CHECK:STDOUT:             {kind: 'ExprOpenParen', text: '('},
+// CHECK:STDOUT:             {kind: 'TupleLiteralStart', text: '('},
 // CHECK:STDOUT:           {kind: 'TupleLiteral', text: ')', subtree_size: 2},
 // CHECK:STDOUT:           {kind: 'TupleLiteralComma', text: ','},
-// CHECK:STDOUT:             {kind: 'ExprOpenParen', text: '('},
+// CHECK:STDOUT:             {kind: 'TupleLiteralStart', text: '('},
 // CHECK:STDOUT:           {kind: 'TupleLiteral', text: ')', subtree_size: 2},
 // CHECK:STDOUT:         {kind: 'TupleLiteral', text: ')', subtree_size: 10},
 // CHECK:STDOUT:       {kind: 'BindingPattern', text: ':', subtree_size: 12},

+ 2 - 2
toolchain/parse/testdata/tuple/two_entries.carbon

@@ -11,14 +11,14 @@ var x: (i32, i32) = (1, 2);
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:       {kind: 'VariableIntroducer', text: 'var'},
 // CHECK:STDOUT:         {kind: 'IdentifierName', text: 'x'},
-// CHECK:STDOUT:           {kind: 'ExprOpenParen', text: '('},
+// CHECK:STDOUT:           {kind: 'TupleLiteralStart', text: '('},
 // CHECK:STDOUT:           {kind: 'IntTypeLiteral', text: 'i32'},
 // CHECK:STDOUT:           {kind: 'TupleLiteralComma', text: ','},
 // CHECK:STDOUT:           {kind: 'IntTypeLiteral', text: 'i32'},
 // CHECK:STDOUT:         {kind: 'TupleLiteral', text: ')', subtree_size: 5},
 // CHECK:STDOUT:       {kind: 'BindingPattern', text: ':', subtree_size: 7},
 // CHECK:STDOUT:       {kind: 'VariableInitializer', text: '='},
-// CHECK:STDOUT:         {kind: 'ExprOpenParen', text: '('},
+// CHECK:STDOUT:         {kind: 'TupleLiteralStart', text: '('},
 // CHECK:STDOUT:         {kind: 'IntLiteral', text: '1'},
 // CHECK:STDOUT:         {kind: 'TupleLiteralComma', text: ','},
 // CHECK:STDOUT:         {kind: 'IntLiteral', text: '2'},

+ 1 - 1
toolchain/parse/testdata/var/var_tuple.carbon

@@ -27,7 +27,7 @@ fn F() {
 // CHECK:STDOUT:           {kind: 'BindingPattern', text: ':', subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 9},
 // CHECK:STDOUT:         {kind: 'VariableInitializer', text: '='},
-// CHECK:STDOUT:           {kind: 'ExprOpenParen', text: '('},
+// CHECK:STDOUT:           {kind: 'TupleLiteralStart', text: '('},
 // CHECK:STDOUT:           {kind: 'StringLiteral', text: '"hello"'},
 // CHECK:STDOUT:           {kind: 'TupleLiteralComma', text: ','},
 // CHECK:STDOUT:           {kind: 'IntLiteral', text: '0'},

+ 4 - 3
toolchain/parse/typed_nodes.h

@@ -630,17 +630,18 @@ struct IndexExpr {
   AnyExprId index;
 };
 
-using ExprOpenParen = LeafNode<NodeKind::ExprOpenParen>;
+using ParenExprStart = LeafNode<NodeKind::ParenExprStart>;
 
 // A parenthesized expression: `(a)`.
 struct ParenExpr {
   static constexpr auto Kind =
       NodeKind::ParenExpr.Define(NodeCategory::Expr | NodeCategory::MemberExpr);
 
-  ExprOpenParenId left_paren;
+  ParenExprStartId start;
   AnyExprId expr;
 };
 
+using TupleLiteralStart = LeafNode<NodeKind::TupleLiteralStart>;
 using TupleLiteralComma = LeafNode<NodeKind::TupleLiteralComma>;
 
 // A tuple literal: `()`, `(a, b, c)`, or `(a,)`.
@@ -648,7 +649,7 @@ struct TupleLiteral {
   static constexpr auto Kind =
       NodeKind::TupleLiteral.Define(NodeCategory::Expr);
 
-  ExprOpenParenId left_paren;
+  TupleLiteralStartId start;
   CommaSeparatedList<AnyExprId, TupleLiteralCommaId> elements;
 };