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

Refactor struct literal parse nodes. (#4470)

Split StructComma into StructLiteralComma and StructTypeLiteralComma in
order to easily differentiate handling (remains the same in this PR).

Add "Literal" to StructField and StructTypeField because it feels
inconsistent versus the other non-shared things. StructFieldDesignator
remains shared between value literals and type literals.

Note I probably would've made StructFieldDesignator non-shared too, but
that'd require either a lookahead of 2 (to see the separator`) or a
writeback after parsing the separator, neither of which felt especially
crucial for this, when what I'm really trying to do is split type
literal handling a little further.
Jon Ross-Perkins 1 год назад
Родитель
Сommit
dd43bb92b5
23 измененных файлов с 93 добавлено и 73 удалено
  1. 10 3
      toolchain/check/handle_struct.cpp
  2. 4 3
      toolchain/check/node_stack.h
  3. 3 3
      toolchain/check/param_and_arg_refs_stack.h
  4. 1 1
      toolchain/docs/parse.md
  5. 12 8
      toolchain/parse/handle_brace_expr.cpp
  6. 8 5
      toolchain/parse/node_kind.def
  7. 2 2
      toolchain/parse/testdata/basics/fail_bracket_recovery.carbon
  8. 3 3
      toolchain/parse/testdata/class/adapt.carbon
  9. 1 1
      toolchain/parse/testdata/class/fn_definitions.carbon
  10. 1 1
      toolchain/parse/testdata/struct/fail_comma_only.carbon
  11. 3 3
      toolchain/parse/testdata/struct/fail_comma_repeat_in_type.carbon
  12. 3 3
      toolchain/parse/testdata/struct/fail_comma_repeat_in_value.carbon
  13. 2 2
      toolchain/parse/testdata/struct/fail_extra_token_in_type.carbon
  14. 2 2
      toolchain/parse/testdata/struct/fail_extra_token_in_value.carbon
  15. 2 2
      toolchain/parse/testdata/struct/fail_mix_type_and_value.carbon
  16. 2 2
      toolchain/parse/testdata/struct/fail_mix_value_and_type.carbon
  17. 6 6
      toolchain/parse/testdata/struct/fail_mix_with_unknown.carbon
  18. 2 2
      toolchain/parse/testdata/struct/fail_period_string_colon.carbon
  19. 2 2
      toolchain/parse/testdata/struct/fail_period_string_equals.carbon
  20. 2 2
      toolchain/parse/testdata/struct/one_entry_no_comma.carbon
  21. 4 4
      toolchain/parse/testdata/struct/one_entry_with_comma.carbon
  22. 6 6
      toolchain/parse/testdata/struct/two_entries.carbon
  23. 12 7
      toolchain/parse/typed_nodes.h

+ 10 - 3
toolchain/check/handle_struct.cpp

@@ -34,13 +34,20 @@ auto HandleParseNode(Context& context,
   return true;
 }
 
-auto HandleParseNode(Context& context, Parse::StructCommaId /*node_id*/)
+auto HandleParseNode(Context& context, Parse::StructLiteralCommaId /*node_id*/)
     -> bool {
   context.param_and_arg_refs_stack().ApplyComma();
   return true;
 }
 
-auto HandleParseNode(Context& context, Parse::StructFieldId node_id) -> bool {
+auto HandleParseNode(Context& context,
+                     Parse::StructTypeLiteralCommaId /*node_id*/) -> bool {
+  context.param_and_arg_refs_stack().ApplyComma();
+  return true;
+}
+
+auto HandleParseNode(Context& context, Parse::StructLiteralFieldId node_id)
+    -> bool {
   auto value_inst_id = context.node_stack().PopExpr();
   auto [name_node, name_id] = context.node_stack().PopNameWithNodeId();
 
@@ -56,7 +63,7 @@ auto HandleParseNode(Context& context, Parse::StructFieldId node_id) -> bool {
   return true;
 }
 
-auto HandleParseNode(Context& context, Parse::StructTypeFieldId node_id)
+auto HandleParseNode(Context& context, Parse::StructTypeLiteralFieldId node_id)
     -> bool {
   auto [type_node, type_id] = context.node_stack().PopExprWithNodeId();
   SemIR::TypeId cast_type_id = ExprAsType(context, type_node, type_id).type_id;

+ 4 - 3
toolchain/check/node_stack.h

@@ -423,8 +423,8 @@ class NodeStack {
         case Parse::NodeKind::ReturnType:
         case Parse::NodeKind::ShortCircuitOperandAnd:
         case Parse::NodeKind::ShortCircuitOperandOr:
-        case Parse::NodeKind::StructField:
-        case Parse::NodeKind::StructTypeField:
+        case Parse::NodeKind::StructLiteralField:
+        case Parse::NodeKind::StructTypeLiteralField:
         case Parse::NodeKind::WhereOperand:
           return Id::KindFor<SemIR::InstId>();
         case Parse::NodeKind::IfCondition:
@@ -631,8 +631,9 @@ class NodeStack {
         case Parse::NodeKind::ShortCircuitOperatorOr:
         case Parse::NodeKind::StringLiteral:
         case Parse::NodeKind::StringTypeLiteral:
-        case Parse::NodeKind::StructComma:
+        case Parse::NodeKind::StructLiteralComma:
         case Parse::NodeKind::StructFieldDesignator:
+        case Parse::NodeKind::StructTypeLiteralComma:
         case Parse::NodeKind::StructLiteral:
         case Parse::NodeKind::StructTypeLiteral:
         case Parse::NodeKind::Template:

+ 3 - 3
toolchain/check/param_and_arg_refs_stack.h

@@ -30,8 +30,8 @@ class ParamAndArgRefsStack {
   // On a comma, pushes the most recent instruction, becoming param or arg ref.
   // This also pops the NodeStack, meaning its top will remain start_kind.
   auto ApplyComma() -> void {
-    // Support expressions, parameters, and other nodes like `StructField`
-    // that produce InstIds.
+    // Support expressions, parameters, and other nodes like
+    // `StructLiteralField` that produce InstIds.
     stack_.AddInstId(node_stack_->Pop<SemIR::InstId>());
   }
 
@@ -43,7 +43,7 @@ class ParamAndArgRefsStack {
   auto EndNoPop(Parse::NodeKind start_kind) -> void {
     if (!node_stack_->PeekIs(start_kind)) {
       // Support expressions, parameters, and other nodes like
-      // `StructField` that produce InstIds.
+      // `StructLiteralField` that produce InstIds.
       stack_.AddInstId(node_stack_->Pop<SemIR::InstId>());
     }
   }

+ 1 - 1
toolchain/docs/parse.md

@@ -156,7 +156,7 @@ have two children: the lhs and rhs expressions. Many nodes have a child count of
 
 Because the tree structure is always valid, these are treated as contracts. Some
 nodes exist only to be used to construct valid tree structures for invalid
-input, such as `StructFieldUnknown`.
+input, such as `InvalidParse`.
 
 Although each subtree's size is also tracked as part of the node, we're
 currently trying to avoid relying on it and may eliminate it if it turns out to

+ 12 - 8
toolchain/parse/handle_brace_expr.cpp

@@ -149,8 +149,9 @@ auto HandleBraceExprParamAfterDesignatorAsUnknown(Context& context) -> void {
 }
 
 // Handles BraceExprParamFinishAs(Type|Value|Unknown).
-static auto HandleBraceExprParamFinish(Context& context, NodeKind node_kind,
-                                       State param_state) -> void {
+static auto HandleBraceExprParamFinish(Context& context, NodeKind field_kind,
+                                       NodeKind comma_kind, State param_state)
+    -> void {
   auto state = context.PopState();
 
   if (state.has_error) {
@@ -158,28 +159,31 @@ static auto HandleBraceExprParamFinish(Context& context, NodeKind node_kind,
                         /*has_error=*/true);
     context.ReturnErrorOnState();
   } else {
-    context.AddNode(node_kind, state.token, /*has_error=*/false);
+    context.AddNode(field_kind, state.token, /*has_error=*/false);
   }
 
-  if (context.ConsumeListToken(
-          NodeKind::StructComma, Lex::TokenKind::CloseCurlyBrace,
-          state.has_error) == Context::ListTokenKind::Comma) {
+  if (context.ConsumeListToken(comma_kind, Lex::TokenKind::CloseCurlyBrace,
+                               state.has_error) ==
+      Context::ListTokenKind::Comma) {
     context.PushState(param_state);
   }
 }
 
 auto HandleBraceExprParamFinishAsType(Context& context) -> void {
-  HandleBraceExprParamFinish(context, NodeKind::StructTypeField,
+  HandleBraceExprParamFinish(context, NodeKind::StructTypeLiteralField,
+                             NodeKind::StructTypeLiteralComma,
                              State::BraceExprParamAsType);
 }
 
 auto HandleBraceExprParamFinishAsValue(Context& context) -> void {
-  HandleBraceExprParamFinish(context, NodeKind::StructField,
+  HandleBraceExprParamFinish(context, NodeKind::StructLiteralField,
+                             NodeKind::StructLiteralComma,
                              State::BraceExprParamAsValue);
 }
 
 auto HandleBraceExprParamFinishAsUnknown(Context& context) -> void {
   HandleBraceExprParamFinish(context, NodeKind::InvalidParse,
+                             NodeKind::InvalidParse,
                              State::BraceExprParamAsUnknown);
 }
 

+ 8 - 5
toolchain/parse/node_kind.def

@@ -287,13 +287,16 @@ CARBON_PARSE_NODE_KIND(IfExprIf)
 CARBON_PARSE_NODE_KIND(IfExprThen)
 CARBON_PARSE_NODE_KIND(IfExprElse)
 
-CARBON_PARSE_NODE_KIND(StructLiteralStart)
-CARBON_PARSE_NODE_KIND(StructTypeLiteralStart)
 CARBON_PARSE_NODE_KIND(StructFieldDesignator)
-CARBON_PARSE_NODE_KIND(StructField)
-CARBON_PARSE_NODE_KIND(StructTypeField)
-CARBON_PARSE_NODE_KIND(StructComma)
+
+CARBON_PARSE_NODE_KIND(StructLiteralStart)
+CARBON_PARSE_NODE_KIND(StructLiteralField)
+CARBON_PARSE_NODE_KIND(StructLiteralComma)
 CARBON_PARSE_NODE_KIND(StructLiteral)
+
+CARBON_PARSE_NODE_KIND(StructTypeLiteralStart)
+CARBON_PARSE_NODE_KIND(StructTypeLiteralField)
+CARBON_PARSE_NODE_KIND(StructTypeLiteralComma)
 CARBON_PARSE_NODE_KIND(StructTypeLiteral)
 
 CARBON_PARSE_NODE_KIND(ExternModifierWithLibrary)

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

@@ -63,7 +63,7 @@ fn F() {
 // CHECK:STDOUT:               {kind: 'StructFieldDesignator', text: '.', subtree_size: 2},
 // CHECK:STDOUT:                 {kind: 'TupleLiteralStart', text: '('},
 // CHECK:STDOUT:               {kind: 'TupleLiteral', text: ')', subtree_size: 2},
-// CHECK:STDOUT:             {kind: 'StructTypeField', text: ':', subtree_size: 5},
+// CHECK:STDOUT:             {kind: 'StructTypeLiteralField', text: ':', subtree_size: 5},
 // CHECK:STDOUT:           {kind: 'StructTypeLiteral', text: '}', subtree_size: 7},
 // CHECK:STDOUT:         {kind: 'BindingPattern', text: ':', subtree_size: 9},
 // CHECK:STDOUT:         {kind: 'VariableInitializer', text: '='},
@@ -74,7 +74,7 @@ fn F() {
 // CHECK:STDOUT:                 {kind: 'TupleLiteralStart', text: '('},
 // CHECK:STDOUT:               {kind: 'TupleLiteral', text: ')', subtree_size: 2},
 // CHECK:STDOUT:             {kind: 'ParenExpr', text: ')', subtree_size: 4},
-// CHECK:STDOUT:           {kind: 'StructField', text: '=', subtree_size: 7},
+// CHECK:STDOUT:           {kind: 'StructLiteralField', text: '=', subtree_size: 7},
 // CHECK:STDOUT:         {kind: 'StructLiteral', text: '}', subtree_size: 9},
 // CHECK:STDOUT:       {kind: 'VariableDecl', text: ';', subtree_size: 21},
 // CHECK:STDOUT:     {kind: 'FunctionDefinition', text: '}', subtree_size: 46},

+ 3 - 3
toolchain/parse/testdata/class/adapt.carbon

@@ -108,12 +108,12 @@ fn F() {
 // CHECK:STDOUT:               {kind: 'IdentifierName', text: 'a'},
 // CHECK:STDOUT:             {kind: 'StructFieldDesignator', text: '.', subtree_size: 2},
 // CHECK:STDOUT:             {kind: 'IntTypeLiteral', text: 'i32'},
-// CHECK:STDOUT:           {kind: 'StructTypeField', text: ':', subtree_size: 4},
-// CHECK:STDOUT:           {kind: 'StructComma', text: ','},
+// CHECK:STDOUT:           {kind: 'StructTypeLiteralField', text: ':', subtree_size: 4},
+// CHECK:STDOUT:           {kind: 'StructTypeLiteralComma', text: ','},
 // CHECK:STDOUT:               {kind: 'IdentifierName', text: 'b'},
 // CHECK:STDOUT:             {kind: 'StructFieldDesignator', text: '.', subtree_size: 2},
 // CHECK:STDOUT:             {kind: 'IntTypeLiteral', text: 'i32'},
-// CHECK:STDOUT:           {kind: 'StructTypeField', text: ':', subtree_size: 4},
+// CHECK:STDOUT:           {kind: 'StructTypeLiteralField', text: ':', subtree_size: 4},
 // CHECK:STDOUT:         {kind: 'StructTypeLiteral', text: '}', subtree_size: 11},
 // CHECK:STDOUT:       {kind: 'AdaptDecl', text: ';', subtree_size: 14},
 // CHECK:STDOUT:     {kind: 'ClassDefinition', text: '}', subtree_size: 18},

+ 1 - 1
toolchain/parse/testdata/class/fn_definitions.carbon

@@ -34,7 +34,7 @@ class Foo {
 // CHECK:STDOUT:                 {kind: 'IdentifierName', text: 'x'},
 // CHECK:STDOUT:               {kind: 'StructFieldDesignator', text: '.', subtree_size: 2},
 // CHECK:STDOUT:               {kind: 'IntLiteral', text: '0'},
-// CHECK:STDOUT:             {kind: 'StructField', text: '=', subtree_size: 4},
+// CHECK:STDOUT:             {kind: 'StructLiteralField', text: '=', subtree_size: 4},
 // CHECK:STDOUT:           {kind: 'StructLiteral', text: '}', subtree_size: 6},
 // CHECK:STDOUT:         {kind: 'ReturnStatement', text: ';', subtree_size: 8},
 // CHECK:STDOUT:       {kind: 'FunctionDefinition', text: '}', subtree_size: 16},

+ 1 - 1
toolchain/parse/testdata/struct/fail_comma_only.carbon

@@ -20,7 +20,7 @@ var x: {,} = {};
 // CHECK:STDOUT:         {kind: 'IdentifierName', text: 'x'},
 // CHECK:STDOUT:           {kind: 'StructLiteralStart', text: '{'},
 // CHECK:STDOUT:           {kind: 'InvalidParse', text: ',', has_error: yes},
-// CHECK:STDOUT:           {kind: 'StructComma', text: ','},
+// CHECK:STDOUT:           {kind: 'InvalidParse', text: ','},
 // CHECK:STDOUT:         {kind: 'StructLiteral', text: '}', has_error: yes, subtree_size: 4},
 // CHECK:STDOUT:       {kind: 'BindingPattern', text: ':', subtree_size: 6},
 // CHECK:STDOUT:       {kind: 'VariableInitializer', text: '='},

+ 3 - 3
toolchain/parse/testdata/struct/fail_comma_repeat_in_type.carbon

@@ -22,10 +22,10 @@ var x: {.a: i32,,} = {};
 // CHECK:STDOUT:               {kind: 'IdentifierName', text: 'a'},
 // CHECK:STDOUT:             {kind: 'StructFieldDesignator', text: '.', subtree_size: 2},
 // CHECK:STDOUT:             {kind: 'IntTypeLiteral', text: 'i32'},
-// CHECK:STDOUT:           {kind: 'StructTypeField', text: ':', subtree_size: 4},
-// CHECK:STDOUT:           {kind: 'StructComma', text: ','},
+// CHECK:STDOUT:           {kind: 'StructTypeLiteralField', text: ':', subtree_size: 4},
+// CHECK:STDOUT:           {kind: 'StructTypeLiteralComma', text: ','},
 // CHECK:STDOUT:           {kind: 'InvalidParse', text: ',', has_error: yes},
-// CHECK:STDOUT:           {kind: 'StructComma', text: ','},
+// CHECK:STDOUT:           {kind: 'StructTypeLiteralComma', text: ','},
 // CHECK:STDOUT:         {kind: 'StructTypeLiteral', text: '}', has_error: yes, subtree_size: 9},
 // CHECK:STDOUT:       {kind: 'BindingPattern', text: ':', subtree_size: 11},
 // CHECK:STDOUT:       {kind: 'VariableInitializer', text: '='},

+ 3 - 3
toolchain/parse/testdata/struct/fail_comma_repeat_in_value.carbon

@@ -22,10 +22,10 @@ var x: {.a = 0,,} = {};
 // CHECK:STDOUT:               {kind: 'IdentifierName', text: 'a'},
 // CHECK:STDOUT:             {kind: 'StructFieldDesignator', text: '.', subtree_size: 2},
 // CHECK:STDOUT:             {kind: 'IntLiteral', text: '0'},
-// CHECK:STDOUT:           {kind: 'StructField', text: '=', subtree_size: 4},
-// CHECK:STDOUT:           {kind: 'StructComma', text: ','},
+// CHECK:STDOUT:           {kind: 'StructLiteralField', text: '=', subtree_size: 4},
+// CHECK:STDOUT:           {kind: 'StructLiteralComma', text: ','},
 // CHECK:STDOUT:           {kind: 'InvalidParse', text: ',', has_error: yes},
-// CHECK:STDOUT:           {kind: 'StructComma', text: ','},
+// CHECK:STDOUT:           {kind: 'StructLiteralComma', text: ','},
 // CHECK:STDOUT:         {kind: 'StructLiteral', text: '}', has_error: yes, subtree_size: 9},
 // CHECK:STDOUT:       {kind: 'BindingPattern', text: ':', subtree_size: 11},
 // CHECK:STDOUT:       {kind: 'VariableInitializer', text: '='},

+ 2 - 2
toolchain/parse/testdata/struct/fail_extra_token_in_type.carbon

@@ -22,7 +22,7 @@ var x: {.a: i32 banana} = {.a = 0};
 // CHECK:STDOUT:               {kind: 'IdentifierName', text: 'a'},
 // CHECK:STDOUT:             {kind: 'StructFieldDesignator', text: '.', subtree_size: 2},
 // CHECK:STDOUT:             {kind: 'IntTypeLiteral', text: 'i32'},
-// CHECK:STDOUT:           {kind: 'StructTypeField', text: ':', subtree_size: 4},
+// CHECK:STDOUT:           {kind: 'StructTypeLiteralField', text: ':', subtree_size: 4},
 // CHECK:STDOUT:         {kind: 'StructTypeLiteral', text: '}', has_error: yes, subtree_size: 6},
 // CHECK:STDOUT:       {kind: 'BindingPattern', text: ':', subtree_size: 8},
 // CHECK:STDOUT:       {kind: 'VariableInitializer', text: '='},
@@ -30,7 +30,7 @@ var x: {.a: i32 banana} = {.a = 0};
 // CHECK:STDOUT:             {kind: 'IdentifierName', text: 'a'},
 // CHECK:STDOUT:           {kind: 'StructFieldDesignator', text: '.', subtree_size: 2},
 // CHECK:STDOUT:           {kind: 'IntLiteral', text: '0'},
-// CHECK:STDOUT:         {kind: 'StructField', text: '=', subtree_size: 4},
+// CHECK:STDOUT:         {kind: 'StructLiteralField', text: '=', subtree_size: 4},
 // CHECK:STDOUT:       {kind: 'StructLiteral', text: '}', subtree_size: 6},
 // CHECK:STDOUT:     {kind: 'VariableDecl', text: ';', subtree_size: 17},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},

+ 2 - 2
toolchain/parse/testdata/struct/fail_extra_token_in_value.carbon

@@ -22,7 +22,7 @@ var x: {.a: i32} = {.a = 0 banana};
 // CHECK:STDOUT:               {kind: 'IdentifierName', text: 'a'},
 // CHECK:STDOUT:             {kind: 'StructFieldDesignator', text: '.', subtree_size: 2},
 // CHECK:STDOUT:             {kind: 'IntTypeLiteral', text: 'i32'},
-// CHECK:STDOUT:           {kind: 'StructTypeField', text: ':', subtree_size: 4},
+// CHECK:STDOUT:           {kind: 'StructTypeLiteralField', text: ':', subtree_size: 4},
 // CHECK:STDOUT:         {kind: 'StructTypeLiteral', text: '}', subtree_size: 6},
 // CHECK:STDOUT:       {kind: 'BindingPattern', text: ':', subtree_size: 8},
 // CHECK:STDOUT:       {kind: 'VariableInitializer', text: '='},
@@ -30,7 +30,7 @@ var x: {.a: i32} = {.a = 0 banana};
 // CHECK:STDOUT:             {kind: 'IdentifierName', text: 'a'},
 // CHECK:STDOUT:           {kind: 'StructFieldDesignator', text: '.', subtree_size: 2},
 // CHECK:STDOUT:           {kind: 'IntLiteral', text: '0'},
-// CHECK:STDOUT:         {kind: 'StructField', text: '=', subtree_size: 4},
+// CHECK:STDOUT:         {kind: 'StructLiteralField', text: '=', subtree_size: 4},
 // CHECK:STDOUT:       {kind: 'StructLiteral', text: '}', has_error: yes, subtree_size: 6},
 // CHECK:STDOUT:     {kind: 'VariableDecl', text: ';', subtree_size: 17},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},

+ 2 - 2
toolchain/parse/testdata/struct/fail_mix_type_and_value.carbon

@@ -22,8 +22,8 @@ var x: {.a: i32, .b = 0} = {};
 // CHECK:STDOUT:               {kind: 'IdentifierName', text: 'a'},
 // CHECK:STDOUT:             {kind: 'StructFieldDesignator', text: '.', subtree_size: 2},
 // CHECK:STDOUT:             {kind: 'IntTypeLiteral', text: 'i32'},
-// CHECK:STDOUT:           {kind: 'StructTypeField', text: ':', subtree_size: 4},
-// CHECK:STDOUT:           {kind: 'StructComma', text: ','},
+// CHECK:STDOUT:           {kind: 'StructTypeLiteralField', text: ':', subtree_size: 4},
+// CHECK:STDOUT:           {kind: 'StructTypeLiteralComma', text: ','},
 // CHECK:STDOUT:             {kind: 'IdentifierName', text: 'b'},
 // CHECK:STDOUT:           {kind: 'StructFieldDesignator', text: '.', subtree_size: 2},
 // CHECK:STDOUT:           {kind: 'InvalidParse', text: '.', has_error: yes},

+ 2 - 2
toolchain/parse/testdata/struct/fail_mix_value_and_type.carbon

@@ -22,8 +22,8 @@ var x: {.a = 0, b: i32} = {};
 // CHECK:STDOUT:               {kind: 'IdentifierName', text: 'a'},
 // CHECK:STDOUT:             {kind: 'StructFieldDesignator', text: '.', subtree_size: 2},
 // CHECK:STDOUT:             {kind: 'IntLiteral', text: '0'},
-// CHECK:STDOUT:           {kind: 'StructField', text: '=', subtree_size: 4},
-// CHECK:STDOUT:           {kind: 'StructComma', text: ','},
+// CHECK:STDOUT:           {kind: 'StructLiteralField', text: '=', subtree_size: 4},
+// CHECK:STDOUT:           {kind: 'StructLiteralComma', text: ','},
 // CHECK:STDOUT:           {kind: 'InvalidParse', text: 'b', has_error: yes},
 // CHECK:STDOUT:         {kind: 'StructLiteral', text: '}', has_error: yes, subtree_size: 8},
 // CHECK:STDOUT:       {kind: 'BindingPattern', text: ':', subtree_size: 10},

+ 6 - 6
toolchain/parse/testdata/struct/fail_mix_with_unknown.carbon

@@ -39,12 +39,12 @@ var x: i32 = {.a: i32, .b, .c = 1};
 // CHECK:STDOUT:             {kind: 'IdentifierName', text: 'a'},
 // CHECK:STDOUT:           {kind: 'StructFieldDesignator', text: '.', subtree_size: 2},
 // CHECK:STDOUT:           {kind: 'IntLiteral', text: '1'},
-// CHECK:STDOUT:         {kind: 'StructField', text: '=', subtree_size: 4},
-// CHECK:STDOUT:         {kind: 'StructComma', text: ','},
+// CHECK:STDOUT:         {kind: 'StructLiteralField', text: '=', subtree_size: 4},
+// CHECK:STDOUT:         {kind: 'StructLiteralComma', text: ','},
 // CHECK:STDOUT:           {kind: 'IdentifierName', text: 'b'},
 // CHECK:STDOUT:         {kind: 'StructFieldDesignator', text: '.', subtree_size: 2},
 // CHECK:STDOUT:         {kind: 'InvalidParse', text: '.', has_error: yes},
-// CHECK:STDOUT:         {kind: 'StructComma', text: ','},
+// CHECK:STDOUT:         {kind: 'StructLiteralComma', text: ','},
 // CHECK:STDOUT:           {kind: 'IdentifierName', text: 'c'},
 // CHECK:STDOUT:         {kind: 'StructFieldDesignator', text: '.', subtree_size: 2},
 // CHECK:STDOUT:         {kind: 'InvalidParse', text: '.', has_error: yes},
@@ -59,12 +59,12 @@ var x: i32 = {.a: i32, .b, .c = 1};
 // CHECK:STDOUT:             {kind: 'IdentifierName', text: 'a'},
 // CHECK:STDOUT:           {kind: 'StructFieldDesignator', text: '.', subtree_size: 2},
 // CHECK:STDOUT:           {kind: 'IntTypeLiteral', text: 'i32'},
-// CHECK:STDOUT:         {kind: 'StructTypeField', text: ':', subtree_size: 4},
-// CHECK:STDOUT:         {kind: 'StructComma', text: ','},
+// CHECK:STDOUT:         {kind: 'StructTypeLiteralField', text: ':', subtree_size: 4},
+// CHECK:STDOUT:         {kind: 'StructTypeLiteralComma', text: ','},
 // CHECK:STDOUT:           {kind: 'IdentifierName', text: 'b'},
 // CHECK:STDOUT:         {kind: 'StructFieldDesignator', text: '.', subtree_size: 2},
 // CHECK:STDOUT:         {kind: 'InvalidParse', text: '.', has_error: yes},
-// CHECK:STDOUT:         {kind: 'StructComma', text: ','},
+// CHECK:STDOUT:         {kind: 'StructTypeLiteralComma', text: ','},
 // CHECK:STDOUT:           {kind: 'IdentifierName', text: 'c'},
 // CHECK:STDOUT:         {kind: 'StructFieldDesignator', text: '.', subtree_size: 2},
 // CHECK:STDOUT:         {kind: 'InvalidParse', text: '.', has_error: yes},

+ 2 - 2
toolchain/parse/testdata/struct/fail_period_string_colon.carbon

@@ -23,11 +23,11 @@ var x: {."hello": i32, .y: i32} = {};
 // CHECK:STDOUT:           {kind: 'StructFieldDesignator', text: '.', subtree_size: 2},
 // CHECK:STDOUT:           {kind: 'IntTypeLiteral', text: 'i32'},
 // CHECK:STDOUT:           {kind: 'InvalidParse', text: ':', has_error: yes},
-// CHECK:STDOUT:           {kind: 'StructComma', text: ','},
+// CHECK:STDOUT:           {kind: 'StructTypeLiteralComma', text: ','},
 // CHECK:STDOUT:               {kind: 'IdentifierName', text: 'y'},
 // CHECK:STDOUT:             {kind: 'StructFieldDesignator', text: '.', subtree_size: 2},
 // CHECK:STDOUT:             {kind: 'IntTypeLiteral', text: 'i32'},
-// CHECK:STDOUT:           {kind: 'StructTypeField', text: ':', subtree_size: 4},
+// CHECK:STDOUT:           {kind: 'StructTypeLiteralField', text: ':', subtree_size: 4},
 // CHECK:STDOUT:         {kind: 'StructTypeLiteral', text: '}', has_error: yes, subtree_size: 11},
 // CHECK:STDOUT:       {kind: 'BindingPattern', text: ':', subtree_size: 13},
 // CHECK:STDOUT:       {kind: 'VariableInitializer', text: '='},

+ 2 - 2
toolchain/parse/testdata/struct/fail_period_string_equals.carbon

@@ -23,11 +23,11 @@ var x: {."hello" = 0, .y = 4} = {};
 // CHECK:STDOUT:           {kind: 'StructFieldDesignator', text: '.', subtree_size: 2},
 // CHECK:STDOUT:           {kind: 'IntLiteral', text: '0'},
 // CHECK:STDOUT:           {kind: 'InvalidParse', text: '=', has_error: yes},
-// CHECK:STDOUT:           {kind: 'StructComma', text: ','},
+// CHECK:STDOUT:           {kind: 'StructLiteralComma', text: ','},
 // CHECK:STDOUT:               {kind: 'IdentifierName', text: 'y'},
 // CHECK:STDOUT:             {kind: 'StructFieldDesignator', text: '.', subtree_size: 2},
 // CHECK:STDOUT:             {kind: 'IntLiteral', text: '4'},
-// CHECK:STDOUT:           {kind: 'StructField', text: '=', subtree_size: 4},
+// CHECK:STDOUT:           {kind: 'StructLiteralField', text: '=', subtree_size: 4},
 // CHECK:STDOUT:         {kind: 'StructLiteral', text: '}', has_error: yes, subtree_size: 11},
 // CHECK:STDOUT:       {kind: 'BindingPattern', text: ':', subtree_size: 13},
 // CHECK:STDOUT:       {kind: 'VariableInitializer', text: '='},

+ 2 - 2
toolchain/parse/testdata/struct/one_entry_no_comma.carbon

@@ -19,7 +19,7 @@ var z: {.n: i32} = {.n = 4};
 // CHECK:STDOUT:               {kind: 'IdentifierName', text: 'n'},
 // CHECK:STDOUT:             {kind: 'StructFieldDesignator', text: '.', subtree_size: 2},
 // CHECK:STDOUT:             {kind: 'IntTypeLiteral', text: 'i32'},
-// CHECK:STDOUT:           {kind: 'StructTypeField', text: ':', subtree_size: 4},
+// CHECK:STDOUT:           {kind: 'StructTypeLiteralField', text: ':', subtree_size: 4},
 // CHECK:STDOUT:         {kind: 'StructTypeLiteral', text: '}', subtree_size: 6},
 // CHECK:STDOUT:       {kind: 'BindingPattern', text: ':', subtree_size: 8},
 // CHECK:STDOUT:       {kind: 'VariableInitializer', text: '='},
@@ -27,7 +27,7 @@ var z: {.n: i32} = {.n = 4};
 // CHECK:STDOUT:             {kind: 'IdentifierName', text: 'n'},
 // CHECK:STDOUT:           {kind: 'StructFieldDesignator', text: '.', subtree_size: 2},
 // CHECK:STDOUT:           {kind: 'IntLiteral', text: '4'},
-// CHECK:STDOUT:         {kind: 'StructField', text: '=', subtree_size: 4},
+// CHECK:STDOUT:         {kind: 'StructLiteralField', text: '=', subtree_size: 4},
 // CHECK:STDOUT:       {kind: 'StructLiteral', text: '}', subtree_size: 6},
 // CHECK:STDOUT:     {kind: 'VariableDecl', text: ';', subtree_size: 17},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},

+ 4 - 4
toolchain/parse/testdata/struct/one_entry_with_comma.carbon

@@ -19,8 +19,8 @@ var z: {.n: i32,} = {.n = 4,};
 // CHECK:STDOUT:               {kind: 'IdentifierName', text: 'n'},
 // CHECK:STDOUT:             {kind: 'StructFieldDesignator', text: '.', subtree_size: 2},
 // CHECK:STDOUT:             {kind: 'IntTypeLiteral', text: 'i32'},
-// CHECK:STDOUT:           {kind: 'StructTypeField', text: ':', subtree_size: 4},
-// CHECK:STDOUT:           {kind: 'StructComma', text: ','},
+// CHECK:STDOUT:           {kind: 'StructTypeLiteralField', text: ':', subtree_size: 4},
+// CHECK:STDOUT:           {kind: 'StructTypeLiteralComma', text: ','},
 // CHECK:STDOUT:         {kind: 'StructTypeLiteral', text: '}', subtree_size: 7},
 // CHECK:STDOUT:       {kind: 'BindingPattern', text: ':', subtree_size: 9},
 // CHECK:STDOUT:       {kind: 'VariableInitializer', text: '='},
@@ -28,8 +28,8 @@ var z: {.n: i32,} = {.n = 4,};
 // CHECK:STDOUT:             {kind: 'IdentifierName', text: 'n'},
 // CHECK:STDOUT:           {kind: 'StructFieldDesignator', text: '.', subtree_size: 2},
 // CHECK:STDOUT:           {kind: 'IntLiteral', text: '4'},
-// CHECK:STDOUT:         {kind: 'StructField', text: '=', subtree_size: 4},
-// CHECK:STDOUT:         {kind: 'StructComma', text: ','},
+// CHECK:STDOUT:         {kind: 'StructLiteralField', text: '=', subtree_size: 4},
+// CHECK:STDOUT:         {kind: 'StructLiteralComma', text: ','},
 // CHECK:STDOUT:       {kind: 'StructLiteral', text: '}', subtree_size: 7},
 // CHECK:STDOUT:     {kind: 'VariableDecl', text: ';', subtree_size: 19},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},

+ 6 - 6
toolchain/parse/testdata/struct/two_entries.carbon

@@ -19,12 +19,12 @@ var x: {.a: i32, .b: i32} = {.a = 1, .b = 2};
 // CHECK:STDOUT:               {kind: 'IdentifierName', text: 'a'},
 // CHECK:STDOUT:             {kind: 'StructFieldDesignator', text: '.', subtree_size: 2},
 // CHECK:STDOUT:             {kind: 'IntTypeLiteral', text: 'i32'},
-// CHECK:STDOUT:           {kind: 'StructTypeField', text: ':', subtree_size: 4},
-// CHECK:STDOUT:           {kind: 'StructComma', text: ','},
+// CHECK:STDOUT:           {kind: 'StructTypeLiteralField', text: ':', subtree_size: 4},
+// CHECK:STDOUT:           {kind: 'StructTypeLiteralComma', text: ','},
 // CHECK:STDOUT:               {kind: 'IdentifierName', text: 'b'},
 // CHECK:STDOUT:             {kind: 'StructFieldDesignator', text: '.', subtree_size: 2},
 // CHECK:STDOUT:             {kind: 'IntTypeLiteral', text: 'i32'},
-// CHECK:STDOUT:           {kind: 'StructTypeField', text: ':', subtree_size: 4},
+// CHECK:STDOUT:           {kind: 'StructTypeLiteralField', text: ':', subtree_size: 4},
 // CHECK:STDOUT:         {kind: 'StructTypeLiteral', text: '}', subtree_size: 11},
 // CHECK:STDOUT:       {kind: 'BindingPattern', text: ':', subtree_size: 13},
 // CHECK:STDOUT:       {kind: 'VariableInitializer', text: '='},
@@ -32,12 +32,12 @@ var x: {.a: i32, .b: i32} = {.a = 1, .b = 2};
 // CHECK:STDOUT:             {kind: 'IdentifierName', text: 'a'},
 // CHECK:STDOUT:           {kind: 'StructFieldDesignator', text: '.', subtree_size: 2},
 // CHECK:STDOUT:           {kind: 'IntLiteral', text: '1'},
-// CHECK:STDOUT:         {kind: 'StructField', text: '=', subtree_size: 4},
-// CHECK:STDOUT:         {kind: 'StructComma', text: ','},
+// CHECK:STDOUT:         {kind: 'StructLiteralField', text: '=', subtree_size: 4},
+// CHECK:STDOUT:         {kind: 'StructLiteralComma', text: ','},
 // CHECK:STDOUT:             {kind: 'IdentifierName', text: 'b'},
 // CHECK:STDOUT:           {kind: 'StructFieldDesignator', text: '.', subtree_size: 2},
 // CHECK:STDOUT:           {kind: 'IntLiteral', text: '2'},
-// CHECK:STDOUT:         {kind: 'StructField', text: '=', subtree_size: 4},
+// CHECK:STDOUT:         {kind: 'StructLiteralField', text: '=', subtree_size: 4},
 // CHECK:STDOUT:       {kind: 'StructLiteral', text: '}', subtree_size: 11},
 // CHECK:STDOUT:     {kind: 'VariableDecl', text: ';', subtree_size: 27},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},

+ 12 - 7
toolchain/parse/typed_nodes.h

@@ -1122,9 +1122,14 @@ using StructLiteralStart =
 using StructTypeLiteralStart =
     LeafNode<NodeKind::StructTypeLiteralStart, Lex::OpenCurlyBraceTokenIndex>;
 // `,`
-using StructComma = LeafNode<NodeKind::StructComma, Lex::CommaTokenIndex>;
+using StructLiteralComma =
+    LeafNode<NodeKind::StructLiteralComma, Lex::CommaTokenIndex>;
+using StructTypeLiteralComma =
+    LeafNode<NodeKind::StructTypeLiteralComma, Lex::CommaTokenIndex>;
 
 // `.a`
+// This is shared for struct literals and type literals in order to reduce
+// lookahead for parse (the `=` versus `:` would require lookahead of 2).
 struct StructFieldDesignator {
   static constexpr auto Kind =
       NodeKind::StructFieldDesignator.Define({.child_count = 1});
@@ -1134,8 +1139,8 @@ struct StructFieldDesignator {
 };
 
 // `.a = 0`
-struct StructField {
-  static constexpr auto Kind = NodeKind::StructField.Define(
+struct StructLiteralField {
+  static constexpr auto Kind = NodeKind::StructLiteralField.Define(
       {.bracketed_by = StructFieldDesignator::Kind, .child_count = 2});
 
   StructFieldDesignatorId designator;
@@ -1144,8 +1149,8 @@ struct StructField {
 };
 
 // `.a: i32`
-struct StructTypeField {
-  static constexpr auto Kind = NodeKind::StructTypeField.Define(
+struct StructTypeLiteralField {
+  static constexpr auto Kind = NodeKind::StructTypeLiteralField.Define(
       {.bracketed_by = StructFieldDesignator::Kind, .child_count = 2});
 
   StructFieldDesignatorId designator;
@@ -1160,7 +1165,7 @@ struct StructLiteral {
        .bracketed_by = StructLiteralStart::Kind});
 
   StructLiteralStartId start;
-  CommaSeparatedList<StructFieldId, StructCommaId> fields;
+  CommaSeparatedList<StructLiteralFieldId, StructLiteralCommaId> fields;
   Lex::CloseCurlyBraceTokenIndex token;
 };
 
@@ -1171,7 +1176,7 @@ struct StructTypeLiteral {
        .bracketed_by = StructTypeLiteralStart::Kind});
 
   StructTypeLiteralStartId start;
-  CommaSeparatedList<StructTypeFieldId, StructCommaId> fields;
+  CommaSeparatedList<StructTypeLiteralFieldId, StructTypeLiteralCommaId> fields;
   Lex::CloseCurlyBraceTokenIndex token;
 };