Procházet zdrojové kódy

Support tuple patterns outside parameter lists (#4923)

Parameter lists need substantially different treatment than tuple
patterns in other contexts, so this change splits them into separate
parse node kinds.

---------

Co-authored-by: Jon Ross-Perkins <jperkins@google.com>
Geoff Romer před 1 rokem
rodič
revize
74e1a9949f
100 změnil soubory, kde provedl 1357 přidání a 586 odebrání
  1. 1 0
      toolchain/check/eval.cpp
  2. 16 27
      toolchain/check/handle_let_and_var.cpp
  3. 55 27
      toolchain/check/handle_pattern_list.cpp
  4. 3 2
      toolchain/check/name_component.cpp
  5. 4 8
      toolchain/check/node_stack.h
  6. 295 172
      toolchain/check/pattern_match.cpp
  7. 4 2
      toolchain/check/subpattern.cpp
  8. 2 2
      toolchain/check/subpattern.h
  9. 32 7
      toolchain/check/testdata/function/declaration/no_prelude/fail_pattern_in_signature.carbon
  10. 2 2
      toolchain/check/testdata/interface/no_prelude/fail_assoc_const_not_binding.carbon
  11. 577 0
      toolchain/check/testdata/tuple/no_prelude/tuple_pattern.carbon
  12. 4 0
      toolchain/diagnostics/diagnostic_kind.def
  13. 1 1
      toolchain/parse/handle_choice.cpp
  14. 2 2
      toolchain/parse/handle_decl_name_and_params.cpp
  15. 49 27
      toolchain/parse/handle_pattern_list.cpp
  16. 2 0
      toolchain/parse/node_kind.def
  17. 16 15
      toolchain/parse/state.def
  18. 2 2
      toolchain/parse/testdata/alias/basic.carbon
  19. 2 2
      toolchain/parse/testdata/alias/fail_syntax.carbon
  20. 2 2
      toolchain/parse/testdata/array/fail_syntax.carbon
  21. 2 2
      toolchain/parse/testdata/auto/match_case.carbon
  22. 2 2
      toolchain/parse/testdata/auto/return.carbon
  23. 2 2
      toolchain/parse/testdata/basics/fail_bracket_recovery.carbon
  24. 2 2
      toolchain/parse/testdata/basics/fail_invalid_designators.carbon
  25. 2 2
      toolchain/parse/testdata/basics/function_call.carbon
  26. 4 4
      toolchain/parse/testdata/basics/multifile.carbon
  27. 2 2
      toolchain/parse/testdata/basics/numeric_literals.carbon
  28. 2 2
      toolchain/parse/testdata/basics/parens.carbon
  29. 2 2
      toolchain/parse/testdata/choice/fail_missing_definition_parameterized.carbon
  30. 2 2
      toolchain/parse/testdata/choice/local.carbon
  31. 4 4
      toolchain/parse/testdata/choice/parameterized.carbon
  32. 2 2
      toolchain/parse/testdata/class/adapt.carbon
  33. 2 2
      toolchain/parse/testdata/class/base_misplaced.carbon
  34. 2 2
      toolchain/parse/testdata/class/basic.carbon
  35. 6 6
      toolchain/parse/testdata/class/fn_definitions.carbon
  36. 8 8
      toolchain/parse/testdata/class/local.carbon
  37. 2 2
      toolchain/parse/testdata/class/mismatched_introducer.carbon
  38. 2 2
      toolchain/parse/testdata/for/fail_colon_instead_of_in.carbon
  39. 2 2
      toolchain/parse/testdata/for/fail_missing_cond.carbon
  40. 2 2
      toolchain/parse/testdata/for/fail_missing_in.carbon
  41. 2 2
      toolchain/parse/testdata/for/fail_missing_var.carbon
  42. 2 2
      toolchain/parse/testdata/for/fail_returned_var.carbon
  43. 2 2
      toolchain/parse/testdata/for/fail_square_brackets.carbon
  44. 2 2
      toolchain/parse/testdata/for/nested.carbon
  45. 2 2
      toolchain/parse/testdata/for/simple.carbon
  46. 2 2
      toolchain/parse/testdata/function/declaration/addr.carbon
  47. 2 2
      toolchain/parse/testdata/function/declaration/basic.carbon
  48. 8 8
      toolchain/parse/testdata/function/declaration/extern.carbon
  49. 2 2
      toolchain/parse/testdata/function/declaration/fail_skip_indented_newline_until_outdent.carbon
  50. 2 2
      toolchain/parse/testdata/function/declaration/fail_skip_indented_newline_with_semi.carbon
  51. 2 2
      toolchain/parse/testdata/function/declaration/fail_skip_indented_newline_without_semi.carbon
  52. 2 2
      toolchain/parse/testdata/function/declaration/fail_skip_to_newline_without_semi.carbon
  53. 2 2
      toolchain/parse/testdata/function/declaration/fail_skip_without_semi_to_curly.carbon
  54. 2 2
      toolchain/parse/testdata/function/declaration/fail_with_identifier_as_param.carbon
  55. 8 8
      toolchain/parse/testdata/function/declaration/impl_fn.carbon
  56. 2 2
      toolchain/parse/testdata/function/declaration/implicit_empty.carbon
  57. 2 2
      toolchain/parse/testdata/function/declaration/implicit_params.carbon
  58. 2 2
      toolchain/parse/testdata/function/declaration/params.carbon
  59. 2 2
      toolchain/parse/testdata/function/declaration/with_return_type.carbon
  60. 2 2
      toolchain/parse/testdata/function/definition/basic.carbon
  61. 4 4
      toolchain/parse/testdata/function/definition/builtin.carbon
  62. 12 12
      toolchain/parse/testdata/function/definition/decl_statement.carbon
  63. 4 4
      toolchain/parse/testdata/function/definition/extern.carbon
  64. 8 8
      toolchain/parse/testdata/function/definition/fail_builtin.carbon
  65. 2 2
      toolchain/parse/testdata/function/definition/fail_identifier_in_statements.carbon
  66. 4 4
      toolchain/parse/testdata/function/definition/nested.carbon
  67. 2 2
      toolchain/parse/testdata/function/definition/with_params.carbon
  68. 2 2
      toolchain/parse/testdata/function/definition/with_return_type.carbon
  69. 4 4
      toolchain/parse/testdata/generics/deduced_params/empty.carbon
  70. 4 4
      toolchain/parse/testdata/generics/deduced_params/one.carbon
  71. 4 4
      toolchain/parse/testdata/generics/deduced_params/one_suffix_comma.carbon
  72. 4 4
      toolchain/parse/testdata/generics/deduced_params/six.carbon
  73. 4 4
      toolchain/parse/testdata/generics/deduced_params/two.carbon
  74. 4 4
      toolchain/parse/testdata/generics/deduced_params/two_suffix_comma.carbon
  75. 2 2
      toolchain/parse/testdata/generics/generic_params/basic.carbon
  76. 10 10
      toolchain/parse/testdata/generics/generic_params/fail_template_no_param.carbon
  77. 2 2
      toolchain/parse/testdata/generics/generic_params/template.carbon
  78. 4 4
      toolchain/parse/testdata/generics/generic_params/template_addr.carbon
  79. 2 2
      toolchain/parse/testdata/generics/impl/basic.carbon
  80. 2 2
      toolchain/parse/testdata/generics/impl/class.carbon
  81. 6 6
      toolchain/parse/testdata/generics/impl/fail_out_of_line_member.carbon
  82. 4 4
      toolchain/parse/testdata/generics/interface/basic.carbon
  83. 4 4
      toolchain/parse/testdata/generics/interface/default_fn.carbon
  84. 4 4
      toolchain/parse/testdata/generics/interface/fail_self_param_syntax.carbon
  85. 4 4
      toolchain/parse/testdata/generics/interface/final_fn.carbon
  86. 2 2
      toolchain/parse/testdata/generics/interface/final_member_definition.carbon
  87. 2 2
      toolchain/parse/testdata/generics/interface/non_instance_fn.carbon
  88. 4 4
      toolchain/parse/testdata/generics/interface/self_pointer.carbon
  89. 2 2
      toolchain/parse/testdata/generics/named_constraint/basic.carbon
  90. 2 2
      toolchain/parse/testdata/generics/named_constraint/defined_method.carbon
  91. 4 4
      toolchain/parse/testdata/generics/params/empty.carbon
  92. 32 32
      toolchain/parse/testdata/generics/params/name_qualifier.carbon
  93. 4 4
      toolchain/parse/testdata/generics/params/one.carbon
  94. 4 4
      toolchain/parse/testdata/generics/params/one_suffix_comma.carbon
  95. 4 4
      toolchain/parse/testdata/generics/params/six.carbon
  96. 4 4
      toolchain/parse/testdata/generics/params/two.carbon
  97. 4 4
      toolchain/parse/testdata/generics/params/two_suffix_comma.carbon
  98. 2 2
      toolchain/parse/testdata/if/basic.carbon
  99. 2 2
      toolchain/parse/testdata/if/else.carbon
  100. 2 2
      toolchain/parse/testdata/if/fail_else_unbraced.carbon

+ 1 - 0
toolchain/check/eval.cpp

@@ -2155,6 +2155,7 @@ static auto TryEvalInstInContext(EvalContext& eval_context,
     case SemIR::ReturnSlotPattern::Kind:
     case SemIR::StructLiteral::Kind:
     case SemIR::TupleLiteral::Kind:
+    case SemIR::TuplePattern::Kind:
     case SemIR::ValueParam::Kind:
     case SemIR::VarPattern::Kind:
     case SemIR::VarStorage::Kind:

+ 16 - 27
toolchain/check/handle_let_and_var.cpp

@@ -15,6 +15,7 @@
 #include "toolchain/check/return.h"
 #include "toolchain/check/subpattern.h"
 #include "toolchain/diagnostics/diagnostic_emitter.h"
+#include "toolchain/diagnostics/format_providers.h"
 #include "toolchain/lex/token_kind.h"
 #include "toolchain/parse/node_kind.h"
 #include "toolchain/sem_ir/ids.h"
@@ -135,17 +136,7 @@ static auto GetOrAddStorage(Context& context, SemIR::InstId pattern_id)
 auto HandleParseNode(Context& context, Parse::VariablePatternId node_id)
     -> bool {
   auto subpattern_id = SemIR::InstId::None;
-  if (context.node_stack().PeekIs(Parse::NodeKind::TuplePattern)) {
-    context.node_stack().PopAndIgnore();
-    CARBON_CHECK(
-        context.node_stack().PeekIs(Parse::NodeKind::TuplePatternStart));
-    context.node_stack().PopAndIgnore();
-    context.inst_block_stack().PopAndDiscard();
-    context.TODO(node_id, "tuple pattern in let/var");
-    subpattern_id = SemIR::ErrorInst::SingletonInstId;
-  } else {
-    subpattern_id = context.node_stack().PopPattern();
-  }
+  subpattern_id = context.node_stack().PopPattern();
   auto type_id = context.insts().Get(subpattern_id).type_id();
 
   auto pattern_id = AddPatternInst<SemIR::VarPattern>(
@@ -251,21 +242,7 @@ static auto HandleDecl(Context& context) -> DeclInfo {
   }
   context.full_pattern_stack().PopFullPattern();
 
-  if (auto [tuple_pattern_node_id, tuple_inst_block_id] =
-          context.node_stack().PopWithNodeIdIf<Parse::NodeKind::TuplePattern>();
-      tuple_inst_block_id) {
-    context.TODO(tuple_pattern_node_id, "tuple pattern in let/var");
-
-    // TODO: Tuple patterns don't behave like other patterns. They are
-    // associated with an InstBlockId on the node stack rather than an InstId,
-    // and leave behind an entry on the subpattern stack and one on the node
-    // stack.
-    EndSubpatternAsExpr(context, SemIR::ErrorInst::SingletonInstId);
-    context.node_stack().PopForSoloNodeId<Parse::NodeKind::TuplePatternStart>();
-    decl_info.pattern_id = SemIR::ErrorInst::SingletonInstId;
-  } else {
-    decl_info.pattern_id = context.node_stack().PopPattern();
-  }
+  decl_info.pattern_id = context.node_stack().PopPattern();
 
   context.node_stack().PopAndDiscardSoloNodeId<IntroducerNodeKind>();
 
@@ -382,7 +359,19 @@ auto HandleParseNode(Context& context, Parse::VariableDeclId node_id) -> bool {
       context, decl_info.introducer,
       KeywordModifierSet::Access | KeywordModifierSet::Returned);
 
-  if (context.scope_stack().GetCurrentScopeAs<SemIR::ClassDecl>()) {
+  if (auto class_scope =
+          context.scope_stack().GetCurrentScopeAs<SemIR::ClassDecl>()) {
+    auto var = context.insts().GetAs<SemIR::VarPattern>(decl_info.pattern_id);
+    if (!context.insts().TryGetAs<SemIR::FieldDecl>(var.subpattern_id)) {
+      CARBON_DIAGNOSTIC(ExpectedSymbolicBindingInFieldDecl, Error,
+                        "pattern in field declaration is not a "
+                        "single `:` binding");
+      context.emitter().Emit(context.insts().GetLocId(var.subpattern_id),
+                             ExpectedSymbolicBindingInFieldDecl);
+      context.name_scopes()
+          .Get(context.classes().Get(class_scope->class_id).scope_id)
+          .set_has_error();
+    }
     if (decl_info.init_id.has_value()) {
       // TODO: In a class scope, we should instead save the initializer
       // somewhere so that we can use it as a default.

+ 55 - 27
toolchain/check/handle_pattern_list.cpp

@@ -4,11 +4,14 @@
 
 #include "toolchain/check/context.h"
 #include "toolchain/check/handle.h"
+#include "toolchain/check/inst.h"
 #include "toolchain/check/subpattern.h"
+#include "toolchain/check/type.h"
 
 namespace Carbon::Check {
 
-auto HandleParseNode(Context& context, Parse::ImplicitParamListStartId node_id)
+// Handle the start of any kind of pattern list.
+static auto HandlePatternListStart(Context& context, Parse::NodeId node_id)
     -> bool {
   context.node_stack().Push(node_id);
   context.param_and_arg_refs_stack().Push();
@@ -16,55 +19,80 @@ auto HandleParseNode(Context& context, Parse::ImplicitParamListStartId node_id)
   return true;
 }
 
-auto HandleParseNode(Context& context, Parse::ImplicitParamListId node_id)
+auto HandleParseNode(Context& context, Parse::ImplicitParamListStartId node_id)
+    -> bool {
+  return HandlePatternListStart(context, node_id);
+}
+
+auto HandleParseNode(Context& context, Parse::TuplePatternStartId node_id)
+    -> bool {
+  return HandlePatternListStart(context, node_id);
+}
+
+auto HandleParseNode(Context& context, Parse::ExplicitParamListStartId node_id)
     -> bool {
-  if (context.node_stack().PeekIs(Parse::NodeKind::ImplicitParamListStart)) {
+  context.full_pattern_stack().EndImplicitParamList();
+  return HandlePatternListStart(context, node_id);
+}
+
+// Handle the end of any kind of parameter list (tuple patterns have separate
+// logic).
+static auto HandleParamListEnd(Context& context, Parse::NodeId node_id,
+                               Parse::NodeKind start_kind) -> bool {
+  if (context.node_stack().PeekIs(start_kind)) {
     // End the subpattern started by a trailing comma, or the opening delimiter
     // of an empty list.
-    EndSubpatternAsEmpty(context);
+    EndSubpatternAsNonExpr(context);
   }
   // Note the Start node remains on the stack, where the param list handler can
   // make use of it.
-  auto refs_id = context.param_and_arg_refs_stack().EndAndPop(
-      Parse::NodeKind::ImplicitParamListStart);
+  auto refs_id = context.param_and_arg_refs_stack().EndAndPop(start_kind);
   context.node_stack().Push(node_id, refs_id);
-  // The implicit parameter list's scope extends to the end of the following
-  // parameter list.
   return true;
 }
 
-auto HandleParseNode(Context& context, Parse::TuplePatternStartId node_id)
+auto HandleParseNode(Context& context, Parse::ImplicitParamListId node_id)
     -> bool {
-  context.node_stack().Push(node_id);
-  context.param_and_arg_refs_stack().Push();
-  BeginSubpattern(context);
-  // TODO: Remove this branch once the parse tree differentiates between
-  // tuple patterns and param patterns.
-  if (context.full_pattern_stack().CurrentKind() ==
-      FullPatternStack::Kind::ImplicitParamList) {
-    context.full_pattern_stack().EndImplicitParamList();
-  }
-  return true;
+  return HandleParamListEnd(context, node_id,
+                            Parse::NodeKind::ImplicitParamListStart);
 }
 
-auto HandleParseNode(Context& context, Parse::PatternListCommaId /*node_id*/)
+auto HandleParseNode(Context& context, Parse::ExplicitParamListId node_id)
     -> bool {
-  context.param_and_arg_refs_stack().ApplyComma();
-  BeginSubpattern(context);
-  return true;
+  return HandleParamListEnd(context, node_id,
+                            Parse::NodeKind::ExplicitParamListStart);
 }
 
 auto HandleParseNode(Context& context, Parse::TuplePatternId node_id) -> bool {
   if (context.node_stack().PeekIs(Parse::NodeKind::TuplePatternStart)) {
     // End the subpattern started by a trailing comma, or the opening delimiter
     // of an empty list.
-    EndSubpatternAsEmpty(context);
+    EndSubpatternAsNonExpr(context);
   }
-  // Note the Start node remains on the stack, where the param list handler can
-  // make use of it.
   auto refs_id = context.param_and_arg_refs_stack().EndAndPop(
       Parse::NodeKind::TuplePatternStart);
-  context.node_stack().Push(node_id, refs_id);
+  context.node_stack()
+      .PopAndDiscardSoloNodeId<Parse::NodeKind::TuplePatternStart>();
+
+  const auto& inst_block = context.inst_blocks().Get(refs_id);
+  llvm::SmallVector<SemIR::TypeId> type_ids;
+  type_ids.reserve(inst_block.size());
+  for (auto inst : inst_block) {
+    type_ids.push_back(context.insts().Get(inst).type_id());
+  }
+  auto type_id = GetTupleType(context, type_ids);
+  context.node_stack().Push(
+      node_id,
+      AddPatternInst<SemIR::TuplePattern>(
+          context, node_id, {.type_id = type_id, .elements_id = refs_id}));
+  EndSubpatternAsNonExpr(context);
+  return true;
+}
+
+auto HandleParseNode(Context& context, Parse::PatternListCommaId /*node_id*/)
+    -> bool {
+  context.param_and_arg_refs_stack().ApplyComma();
+  BeginSubpattern(context);
   return true;
 }
 

+ 3 - 2
toolchain/check/name_component.cpp

@@ -16,11 +16,12 @@ auto PopNameComponent(Context& context, SemIR::InstId return_slot_pattern_id)
 
   // Explicit params.
   auto [params_loc_id, param_patterns_id] =
-      context.node_stack().PopWithNodeIdIf<Parse::NodeKind::TuplePattern>();
+      context.node_stack()
+          .PopWithNodeIdIf<Parse::NodeKind::ExplicitParamList>();
   if (param_patterns_id) {
     first_param_node_id =
         context.node_stack()
-            .PopForSoloNodeId<Parse::NodeKind::TuplePatternStart>();
+            .PopForSoloNodeId<Parse::NodeKind::ExplicitParamListStart>();
     last_param_node_id = params_loc_id;
   } else {
     param_patterns_id = SemIR::InstBlockId::None;

+ 4 - 8
toolchain/check/node_stack.h

@@ -373,8 +373,8 @@ class NodeStack {
       }
     };
 
-    // TODO: Patterns should also produce an `InstId`, but currently
-    // `TuplePattern` produces an `InstBlockId`.
+    set_id_if_category_is(Parse::NodeCategory::Pattern,
+                          Id::KindFor<SemIR::InstId>());
     set_id_if_category_is(Parse::NodeCategory::Expr,
                           Id::KindFor<SemIR::InstId>());
     set_id_if_category_is(Parse::NodeCategory::MemberName |
@@ -394,23 +394,18 @@ class NodeStack {
   static constexpr auto NodeKindToIdKindSpecialCases(Parse::NodeKind node_kind)
       -> std::optional<Id::Kind> {
     switch (node_kind) {
-      case Parse::NodeKind::Addr:
       case Parse::NodeKind::CallExprStart:
-      case Parse::NodeKind::CompileTimeBindingPattern:
       case Parse::NodeKind::IfExprThen:
-      case Parse::NodeKind::LetBindingPattern:
       case Parse::NodeKind::ReturnType:
       case Parse::NodeKind::ShortCircuitOperandAnd:
       case Parse::NodeKind::ShortCircuitOperandOr:
       case Parse::NodeKind::StructLiteralField:
-      case Parse::NodeKind::VarBindingPattern:
-      case Parse::NodeKind::VariablePattern:
       case Parse::NodeKind::WhereOperand:
         return Id::KindFor<SemIR::InstId>();
+      case Parse::NodeKind::ExplicitParamList:
       case Parse::NodeKind::IfCondition:
       case Parse::NodeKind::IfExprIf:
       case Parse::NodeKind::ImplicitParamList:
-      case Parse::NodeKind::TuplePattern:
       case Parse::NodeKind::WhileCondition:
       case Parse::NodeKind::WhileConditionStart:
         return Id::KindFor<SemIR::InstBlockId>();
@@ -436,6 +431,7 @@ class NodeStack {
       case Parse::NodeKind::ChoiceIntroducer:
       case Parse::NodeKind::ClassIntroducer:
       case Parse::NodeKind::CodeBlockStart:
+      case Parse::NodeKind::ExplicitParamListStart:
       case Parse::NodeKind::FunctionIntroducer:
       case Parse::NodeKind::IfStatementElse:
       case Parse::NodeKind::ImplicitParamListStart:

+ 295 - 172
toolchain/check/pattern_match.cpp

@@ -14,6 +14,7 @@
 #include "toolchain/check/convert.h"
 #include "toolchain/check/subpattern.h"
 #include "toolchain/check/type.h"
+#include "toolchain/diagnostics/format_providers.h"
 
 namespace Carbon::Check {
 
@@ -97,6 +98,29 @@ class MatchContext {
   // `ParamPattern` case.
   auto EmitPatternMatch(Context& context, MatchContext::WorkItem entry) -> void;
 
+  // Implementations of `EmitPatternMatch` for particular pattern inst kinds.
+  // The pattern argument is always equal to
+  // `context.insts().Get(entry.pattern_id)`, and `pattern_loc_id` is always
+  // equal to `context.insts().GetLocId(entry.pattern_id)`.
+  auto DoEmitPatternMatch(Context& context,
+                          SemIR::AnyBindingPattern binding_pattern,
+                          SemIR::LocId pattern_loc_id, WorkItem entry) -> void;
+  auto DoEmitPatternMatch(Context& context, SemIR::AddrPattern addr_pattern,
+                          SemIR::LocId pattern_loc_id, WorkItem entry) -> void;
+  auto DoEmitPatternMatch(Context& context,
+                          SemIR::ValueParamPattern param_pattern,
+                          SemIR::LocId pattern_loc_id, WorkItem entry) -> void;
+  auto DoEmitPatternMatch(Context& context,
+                          SemIR::OutParamPattern param_pattern,
+                          SemIR::LocId pattern_loc_id, WorkItem entry) -> void;
+  auto DoEmitPatternMatch(Context& context,
+                          SemIR::ReturnSlotPattern return_slot_pattern,
+                          SemIR::LocId pattern_loc_id, WorkItem entry) -> void;
+  auto DoEmitPatternMatch(Context& context, SemIR::VarPattern var_pattern,
+                          SemIR::LocId pattern_loc_id, WorkItem entry) -> void;
+  auto DoEmitPatternMatch(Context& context, SemIR::TuplePattern tuple_pattern,
+                          SemIR::LocId pattern_loc_id, WorkItem entry) -> void;
+
   // The stack of work to be processed.
   llvm::SmallVector<WorkItem> stack_;
 
@@ -172,6 +196,266 @@ static auto InsertHere(Context& context, SemIR::ExprRegionId region_id)
   return region.result_id;
 }
 
+auto MatchContext::DoEmitPatternMatch(Context& context,
+                                      SemIR::AnyBindingPattern binding_pattern,
+                                      SemIR::LocId /*pattern_loc_id*/,
+                                      MatchContext::WorkItem entry) -> void {
+  // We're logically consuming this map entry, so we invalidate it in order
+  // to avoid accidentally consuming it twice.
+  auto [bind_name_id, type_expr_region_id] =
+      std::exchange(context.bind_name_map().Lookup(entry.pattern_id).value(),
+                    {.bind_name_id = SemIR::InstId::None,
+                     .type_expr_region_id = SemIR::ExprRegionId::None});
+  InsertHere(context, type_expr_region_id);
+  auto value_id = entry.scrutinee_id;
+  switch (kind_) {
+    case MatchKind::Local: {
+      value_id = ConvertToValueOrRefOfType(
+          context, context.insts().GetLocId(entry.scrutinee_id),
+          entry.scrutinee_id, binding_pattern.type_id);
+      break;
+    }
+    case MatchKind::Callee: {
+      if (context.insts()
+              .GetAs<SemIR::AnyParam>(value_id)
+              .runtime_index.has_value()) {
+        results_.push_back(value_id);
+      }
+      break;
+    }
+    case MatchKind::Caller:
+      CARBON_FATAL("Found binding pattern during caller pattern match");
+  }
+  auto bind_name = context.insts().GetAs<SemIR::AnyBindName>(bind_name_id);
+  CARBON_CHECK(!bind_name.value_id.has_value());
+  bind_name.value_id = value_id;
+  ReplaceInstBeforeConstantUse(context, bind_name_id, bind_name);
+  context.inst_block_stack().AddInstId(bind_name_id);
+}
+
+auto MatchContext::DoEmitPatternMatch(Context& context,
+                                      SemIR::AddrPattern addr_pattern,
+                                      SemIR::LocId /*pattern_loc_id*/,
+                                      WorkItem entry) -> void {
+  CARBON_CHECK(kind_ != MatchKind::Local);
+  if (kind_ == MatchKind::Callee) {
+    // We're emitting pattern-match IR for the callee, but we're still on
+    // the caller side of the pattern, so we traverse without emitting any
+    // insts.
+    AddWork({.pattern_id = addr_pattern.inner_id,
+             .scrutinee_id = SemIR::InstId::None});
+    return;
+  }
+  CARBON_CHECK(entry.scrutinee_id.has_value());
+  auto scrutinee_ref_id = ConvertToValueOrRefExpr(context, entry.scrutinee_id);
+  switch (SemIR::GetExprCategory(context.sem_ir(), scrutinee_ref_id)) {
+    case SemIR::ExprCategory::Error:
+    case SemIR::ExprCategory::DurableRef:
+    case SemIR::ExprCategory::EphemeralRef:
+      break;
+    default:
+      CARBON_DIAGNOSTIC(AddrSelfIsNonRef, Error,
+                        "`addr self` method cannot be invoked on a value");
+      context.emitter().Emit(
+          TokenOnly(context.insts().GetLocId(entry.scrutinee_id)),
+          AddrSelfIsNonRef);
+      results_.push_back(SemIR::ErrorInst::SingletonInstId);
+      return;
+  }
+  auto scrutinee_ref = context.insts().Get(scrutinee_ref_id);
+  auto new_scrutinee = AddInst<SemIR::AddrOf>(
+      context, context.insts().GetLocId(scrutinee_ref_id),
+      {.type_id = GetPointerType(context, scrutinee_ref.type_id()),
+       .lvalue_id = scrutinee_ref_id});
+  AddWork({.pattern_id = addr_pattern.inner_id, .scrutinee_id = new_scrutinee});
+}
+
+auto MatchContext::DoEmitPatternMatch(Context& context,
+                                      SemIR::ValueParamPattern param_pattern,
+                                      SemIR::LocId pattern_loc_id,
+                                      WorkItem entry) -> void {
+  CARBON_CHECK(param_pattern.runtime_index.index < 0 ||
+                   static_cast<size_t>(param_pattern.runtime_index.index) ==
+                       results_.size(),
+               "Parameters out of order; expecting {0} but got {1}",
+               results_.size(), param_pattern.runtime_index.index);
+  switch (kind_) {
+    case MatchKind::Caller: {
+      CARBON_CHECK(entry.scrutinee_id.has_value());
+      if (entry.scrutinee_id == SemIR::ErrorInst::SingletonInstId) {
+        results_.push_back(SemIR::ErrorInst::SingletonInstId);
+      } else {
+        results_.push_back(ConvertToValueOfType(
+            context, context.insts().GetLocId(entry.scrutinee_id),
+            entry.scrutinee_id,
+            SemIR::GetTypeInSpecific(context.sem_ir(), callee_specific_id_,
+                                     param_pattern.type_id)));
+      }
+      // Do not traverse farther, because the caller side of the pattern
+      // ends here.
+      break;
+    }
+    case MatchKind::Callee: {
+      if (param_pattern.runtime_index == SemIR::RuntimeParamIndex::Unknown) {
+        param_pattern.runtime_index = NextRuntimeIndex();
+        ReplaceInstBeforeConstantUse(context, entry.pattern_id, param_pattern);
+      }
+      AddWork({.pattern_id = param_pattern.subpattern_id,
+               .scrutinee_id = AddInst<SemIR::ValueParam>(
+                   context, pattern_loc_id,
+                   {.type_id = param_pattern.type_id,
+                    .runtime_index = param_pattern.runtime_index,
+                    .pretty_name_id = GetPrettyName(context, param_pattern)})});
+      break;
+    }
+    case MatchKind::Local: {
+      CARBON_FATAL("Found ValueParamPattern during local pattern match");
+    }
+  }
+}
+
+auto MatchContext::DoEmitPatternMatch(Context& context,
+                                      SemIR::OutParamPattern param_pattern,
+                                      SemIR::LocId pattern_loc_id,
+                                      WorkItem entry) -> void {
+  switch (kind_) {
+    case MatchKind::Caller: {
+      CARBON_CHECK(entry.scrutinee_id.has_value());
+      CARBON_CHECK(context.insts().Get(entry.scrutinee_id).type_id() ==
+                   SemIR::GetTypeInSpecific(context.sem_ir(),
+                                            callee_specific_id_,
+                                            param_pattern.type_id));
+      results_.push_back(entry.scrutinee_id);
+      // Do not traverse farther, because the caller side of the pattern
+      // ends here.
+      break;
+    }
+    case MatchKind::Callee: {
+      // TODO: Consider ways to address near-duplication with the
+      // ValueParamPattern case.
+      if (param_pattern.runtime_index == SemIR::RuntimeParamIndex::Unknown) {
+        param_pattern.runtime_index = NextRuntimeIndex();
+        ReplaceInstBeforeConstantUse(context, entry.pattern_id, param_pattern);
+      }
+      AddWork({.pattern_id = param_pattern.subpattern_id,
+               .scrutinee_id = AddInst<SemIR::OutParam>(
+                   context, pattern_loc_id,
+                   {.type_id = param_pattern.type_id,
+                    .runtime_index = param_pattern.runtime_index,
+                    .pretty_name_id = GetPrettyName(context, param_pattern)})});
+      break;
+    }
+    case MatchKind::Local: {
+      CARBON_FATAL("Found OutParamPattern during local pattern match");
+    }
+  }
+}
+
+auto MatchContext::DoEmitPatternMatch(
+    Context& context, SemIR::ReturnSlotPattern return_slot_pattern,
+    SemIR::LocId pattern_loc_id, WorkItem entry) -> void {
+  CARBON_CHECK(kind_ == MatchKind::Callee);
+  auto return_slot_id = AddInst<SemIR::ReturnSlot>(
+      context, pattern_loc_id,
+      {.type_id = return_slot_pattern.type_id,
+       .type_inst_id = return_slot_pattern.type_inst_id,
+       .storage_id = entry.scrutinee_id});
+  bool already_in_lookup =
+      context.scope_stack()
+          .LookupOrAddName(SemIR::NameId::ReturnSlot, return_slot_id)
+          .has_value();
+  CARBON_CHECK(!already_in_lookup);
+  results_.push_back(entry.scrutinee_id);
+}
+
+auto MatchContext::DoEmitPatternMatch(Context& context,
+                                      SemIR::VarPattern var_pattern,
+                                      SemIR::LocId pattern_loc_id,
+                                      WorkItem entry) -> void {
+  auto var_id = context.var_storage_map().Lookup(entry.pattern_id).value();
+  // TODO: Find a more efficient way to put these insts in the global_init
+  // block (or drop the distinction between the global_init block and the
+  // file scope?)
+  if (context.scope_stack().PeekIndex() == ScopeIndex::Package) {
+    context.global_init().Resume();
+  }
+  if (entry.scrutinee_id.has_value()) {
+    auto init_id =
+        Initialize(context, pattern_loc_id, var_id, entry.scrutinee_id);
+    // TODO: Consider using different instruction kinds for assignment
+    // versus initialization.
+    AddInst<SemIR::Assign>(context, pattern_loc_id,
+                           {.lhs_id = var_id, .rhs_id = init_id});
+  }
+  AddWork({.pattern_id = var_pattern.subpattern_id, .scrutinee_id = var_id});
+  if (context.scope_stack().PeekIndex() == ScopeIndex::Package) {
+    context.global_init().Suspend();
+  }
+}
+
+auto MatchContext::DoEmitPatternMatch(Context& context,
+                                      SemIR::TuplePattern tuple_pattern,
+                                      SemIR::LocId pattern_loc_id,
+                                      WorkItem entry) -> void {
+  if (tuple_pattern.type_id == SemIR::ErrorInst::SingletonTypeId) {
+    return;
+  }
+  auto subpattern_ids = context.inst_blocks().Get(tuple_pattern.elements_id);
+  auto add_all_subscrutinees =
+      [&](llvm::ArrayRef<SemIR::InstId> subscrutinee_ids) {
+        for (auto [subpattern_id, subscrutinee_id] :
+             llvm::reverse(llvm::zip(subpattern_ids, subscrutinee_ids))) {
+          AddWork(
+              {.pattern_id = subpattern_id, .scrutinee_id = subscrutinee_id});
+        }
+      };
+  if (!entry.scrutinee_id.has_value()) {
+    CARBON_CHECK(kind_ == MatchKind::Callee);
+    context.TODO(pattern_loc_id,
+                 "Support patterns besides bindings in parameter list");
+    return;
+  }
+  auto scrutinee = context.insts().GetWithLocId(entry.scrutinee_id);
+  if (auto scrutinee_literal = scrutinee.inst.TryAs<SemIR::TupleLiteral>()) {
+    auto subscrutinee_ids =
+        context.inst_blocks().Get(scrutinee_literal->elements_id);
+    if (subscrutinee_ids.size() != subpattern_ids.size()) {
+      CARBON_DIAGNOSTIC(TuplePatternSizeDoesntMatchLiteral, Error,
+                        "tuple pattern expects {0} element{0:s}, but tuple "
+                        "literal has {1}",
+                        IntAsSelect, IntAsSelect);
+      context.emitter().Emit(pattern_loc_id, TuplePatternSizeDoesntMatchLiteral,
+                             subpattern_ids.size(), subscrutinee_ids.size());
+      return;
+    }
+    add_all_subscrutinees(subscrutinee_ids);
+    return;
+  }
+
+  auto converted_scrutinee = ConvertToValueOrRefOfType(
+      context, pattern_loc_id, entry.scrutinee_id, tuple_pattern.type_id);
+  if (auto scrutinee_value =
+          context.insts().TryGetAs<SemIR::TupleValue>(converted_scrutinee)) {
+    add_all_subscrutinees(
+        context.inst_blocks().Get(scrutinee_value->elements_id));
+    return;
+  }
+
+  auto tuple_type =
+      context.types().GetAs<SemIR::TupleType>(tuple_pattern.type_id);
+  auto element_type_ids = context.type_blocks().Get(tuple_type.elements_id);
+  llvm::SmallVector<SemIR::InstId> subscrutinee_ids;
+  subscrutinee_ids.reserve(element_type_ids.size());
+  for (auto [i, element_type_id] : llvm::enumerate(element_type_ids)) {
+    subscrutinee_ids.push_back(
+        AddInst<SemIR::TupleAccess>(context, scrutinee.loc_id,
+                                    {.type_id = element_type_id,
+                                     .tuple_id = entry.scrutinee_id,
+                                     .index = SemIR::ElementIndex(i)}));
+  }
+  add_all_subscrutinees(subscrutinee_ids);
+}
+
 auto MatchContext::EmitPatternMatch(Context& context,
                                     MatchContext::WorkItem entry) -> void {
   if (entry.pattern_id == SemIR::ErrorInst::SingletonInstId) {
@@ -190,193 +474,32 @@ auto MatchContext::EmitPatternMatch(Context& context,
   CARBON_KIND_SWITCH(pattern.inst) {
     case SemIR::BindingPattern::Kind:
     case SemIR::SymbolicBindingPattern::Kind: {
-      auto binding_pattern = pattern.inst.As<SemIR::AnyBindingPattern>();
-      // We're logically consuming this map entry, so we invalidate it in order
-      // to avoid accidentally consuming it twice.
-      auto [bind_name_id, type_expr_region_id] = std::exchange(
-          context.bind_name_map().Lookup(entry.pattern_id).value(),
-          {.bind_name_id = SemIR::InstId::None,
-           .type_expr_region_id = SemIR::ExprRegionId::None});
-      InsertHere(context, type_expr_region_id);
-      auto value_id = entry.scrutinee_id;
-      switch (kind_) {
-        case MatchKind::Local: {
-          value_id = ConvertToValueOrRefOfType(
-              context, context.insts().GetLocId(entry.scrutinee_id),
-              entry.scrutinee_id, binding_pattern.type_id);
-          break;
-        }
-        case MatchKind::Callee: {
-          if (context.insts()
-                  .GetAs<SemIR::AnyParam>(value_id)
-                  .runtime_index.has_value()) {
-            results_.push_back(value_id);
-          }
-          break;
-        }
-        case MatchKind::Caller:
-          CARBON_FATAL("Found binding pattern during caller pattern match");
-      }
-      auto bind_name = context.insts().GetAs<SemIR::AnyBindName>(bind_name_id);
-      CARBON_CHECK(!bind_name.value_id.has_value());
-      bind_name.value_id = value_id;
-      ReplaceInstBeforeConstantUse(context, bind_name_id, bind_name);
-      context.inst_block_stack().AddInstId(bind_name_id);
+      DoEmitPatternMatch(context, pattern.inst.As<SemIR::AnyBindingPattern>(),
+                         pattern.loc_id, entry);
       break;
     }
     case CARBON_KIND(SemIR::AddrPattern addr_pattern): {
-      CARBON_CHECK(kind_ != MatchKind::Local);
-      if (kind_ == MatchKind::Callee) {
-        // We're emitting pattern-match IR for the callee, but we're still on
-        // the caller side of the pattern, so we traverse without emitting any
-        // insts.
-        AddWork({.pattern_id = addr_pattern.inner_id,
-                 .scrutinee_id = SemIR::InstId::None});
-        break;
-      }
-      CARBON_CHECK(entry.scrutinee_id.has_value());
-      auto scrutinee_ref_id =
-          ConvertToValueOrRefExpr(context, entry.scrutinee_id);
-      switch (SemIR::GetExprCategory(context.sem_ir(), scrutinee_ref_id)) {
-        case SemIR::ExprCategory::Error:
-        case SemIR::ExprCategory::DurableRef:
-        case SemIR::ExprCategory::EphemeralRef:
-          break;
-        default:
-          CARBON_DIAGNOSTIC(AddrSelfIsNonRef, Error,
-                            "`addr self` method cannot be invoked on a value");
-          context.emitter().Emit(
-              TokenOnly(context.insts().GetLocId(entry.scrutinee_id)),
-              AddrSelfIsNonRef);
-          results_.push_back(SemIR::ErrorInst::SingletonInstId);
-          return;
-      }
-      auto scrutinee_ref = context.insts().Get(scrutinee_ref_id);
-      auto new_scrutinee = AddInst<SemIR::AddrOf>(
-          context, context.insts().GetLocId(scrutinee_ref_id),
-          {.type_id = GetPointerType(context, scrutinee_ref.type_id()),
-           .lvalue_id = scrutinee_ref_id});
-      AddWork(
-          {.pattern_id = addr_pattern.inner_id, .scrutinee_id = new_scrutinee});
+      DoEmitPatternMatch(context, addr_pattern, pattern.loc_id, entry);
       break;
     }
     case CARBON_KIND(SemIR::ValueParamPattern param_pattern): {
-      CARBON_CHECK(param_pattern.runtime_index.index < 0 ||
-                       static_cast<size_t>(param_pattern.runtime_index.index) ==
-                           results_.size(),
-                   "Parameters out of order; expecting {0} but got {1}",
-                   results_.size(), param_pattern.runtime_index.index);
-      switch (kind_) {
-        case MatchKind::Caller: {
-          CARBON_CHECK(entry.scrutinee_id.has_value());
-          if (entry.scrutinee_id == SemIR::ErrorInst::SingletonInstId) {
-            results_.push_back(SemIR::ErrorInst::SingletonInstId);
-          } else {
-            results_.push_back(ConvertToValueOfType(
-                context, context.insts().GetLocId(entry.scrutinee_id),
-                entry.scrutinee_id,
-                SemIR::GetTypeInSpecific(context.sem_ir(), callee_specific_id_,
-                                         param_pattern.type_id)));
-          }
-          // Do not traverse farther, because the caller side of the pattern
-          // ends here.
-          break;
-        }
-        case MatchKind::Callee: {
-          if (param_pattern.runtime_index ==
-              SemIR::RuntimeParamIndex::Unknown) {
-            param_pattern.runtime_index = NextRuntimeIndex();
-            ReplaceInstBeforeConstantUse(context, entry.pattern_id,
-                                         param_pattern);
-          }
-          AddWork(
-              {.pattern_id = param_pattern.subpattern_id,
-               .scrutinee_id = AddInst<SemIR::ValueParam>(
-                   context, pattern.loc_id,
-                   {.type_id = param_pattern.type_id,
-                    .runtime_index = param_pattern.runtime_index,
-                    .pretty_name_id = GetPrettyName(context, param_pattern)})});
-          break;
-        }
-        case MatchKind::Local: {
-          CARBON_FATAL("Found ValueParamPattern during local pattern match");
-        }
-      }
+      DoEmitPatternMatch(context, param_pattern, pattern.loc_id, entry);
       break;
     }
     case CARBON_KIND(SemIR::OutParamPattern param_pattern): {
-      switch (kind_) {
-        case MatchKind::Caller: {
-          CARBON_CHECK(entry.scrutinee_id.has_value());
-          CARBON_CHECK(context.insts().Get(entry.scrutinee_id).type_id() ==
-                       SemIR::GetTypeInSpecific(context.sem_ir(),
-                                                callee_specific_id_,
-                                                param_pattern.type_id));
-          results_.push_back(entry.scrutinee_id);
-          // Do not traverse farther, because the caller side of the pattern
-          // ends here.
-          break;
-        }
-        case MatchKind::Callee: {
-          // TODO: Consider ways to address near-duplication with the
-          // ValueParamPattern case.
-          if (param_pattern.runtime_index ==
-              SemIR::RuntimeParamIndex::Unknown) {
-            param_pattern.runtime_index = NextRuntimeIndex();
-            ReplaceInstBeforeConstantUse(context, entry.pattern_id,
-                                         param_pattern);
-          }
-          AddWork(
-              {.pattern_id = param_pattern.subpattern_id,
-               .scrutinee_id = AddInst<SemIR::OutParam>(
-                   context, pattern.loc_id,
-                   {.type_id = param_pattern.type_id,
-                    .runtime_index = param_pattern.runtime_index,
-                    .pretty_name_id = GetPrettyName(context, param_pattern)})});
-          break;
-        }
-        case MatchKind::Local: {
-          CARBON_FATAL("Found OutParamPattern during local pattern match");
-        }
-      }
+      DoEmitPatternMatch(context, param_pattern, pattern.loc_id, entry);
       break;
     }
     case CARBON_KIND(SemIR::ReturnSlotPattern return_slot_pattern): {
-      CARBON_CHECK(kind_ == MatchKind::Callee);
-      auto return_slot_id = AddInst<SemIR::ReturnSlot>(
-          context, pattern.loc_id,
-          {.type_id = return_slot_pattern.type_id,
-           .type_inst_id = return_slot_pattern.type_inst_id,
-           .storage_id = entry.scrutinee_id});
-      bool already_in_lookup =
-          context.scope_stack()
-              .LookupOrAddName(SemIR::NameId::ReturnSlot, return_slot_id)
-              .has_value();
-      CARBON_CHECK(!already_in_lookup);
-      results_.push_back(entry.scrutinee_id);
+      DoEmitPatternMatch(context, return_slot_pattern, pattern.loc_id, entry);
       break;
     }
     case CARBON_KIND(SemIR::VarPattern var_pattern): {
-      auto var_id = context.var_storage_map().Lookup(entry.pattern_id).value();
-      // TODO: Find a more efficient way to put these insts in the global_init
-      // block (or drop the distinction between the global_init block and the
-      // file scope?)
-      if (context.scope_stack().PeekIndex() == ScopeIndex::Package) {
-        context.global_init().Resume();
-      }
-      if (entry.scrutinee_id.has_value()) {
-        auto init_id =
-            Initialize(context, pattern.loc_id, var_id, entry.scrutinee_id);
-        // TODO: Consider using different instruction kinds for assignment
-        // versus initialization.
-        AddInst<SemIR::Assign>(context, pattern.loc_id,
-                               {.lhs_id = var_id, .rhs_id = init_id});
-      }
-      AddWork(
-          {.pattern_id = var_pattern.subpattern_id, .scrutinee_id = var_id});
-      if (context.scope_stack().PeekIndex() == ScopeIndex::Package) {
-        context.global_init().Suspend();
-      }
+      DoEmitPatternMatch(context, var_pattern, pattern.loc_id, entry);
+      break;
+    }
+    case CARBON_KIND(SemIR::TuplePattern tuple_pattern): {
+      DoEmitPatternMatch(context, tuple_pattern, pattern.loc_id, entry);
       break;
     }
     default: {

+ 4 - 2
toolchain/check/subpattern.cpp

@@ -35,11 +35,13 @@ auto EndSubpatternAsExpr(Context& context, SemIR::InstId result_id)
        .result_id = result_id});
 }
 
-auto EndSubpatternAsEmpty(Context& context) -> void {
+auto EndSubpatternAsNonExpr(Context& context) -> void {
   auto block_id = context.inst_block_stack().Pop();
   CARBON_CHECK(block_id == context.region_stack().PeekRegion().back());
   CARBON_CHECK(context.region_stack().PeekRegion().size() == 1);
-  CARBON_CHECK(context.inst_blocks().Get(block_id).empty());
+  // TODO: Add `CARBON_CHECK(inst_blocks().Get(block_id).empty())`.
+  // Currently that can fail when ending a tuple pattern in a name binding
+  // decl in a class or interface.
   context.region_stack().PopAndDiscardRegion();
 }
 

+ 2 - 2
toolchain/check/subpattern.h

@@ -24,8 +24,8 @@ auto EndSubpatternAsExpr(Context& context, SemIR::InstId result_id)
     -> SemIR::ExprRegionId;
 
 // Ends a region started by BeginSubpattern (in stack order), asserting that
-// it was empty.
-auto EndSubpatternAsEmpty(Context& context) -> void;
+// it had no expression content.
+auto EndSubpatternAsNonExpr(Context& context) -> void;
 
 // TODO: Add EndSubpatternAsPattern, when needed.
 

+ 32 - 7
toolchain/check/testdata/function/declaration/no_prelude/fail_pattern_in_signature.carbon

@@ -8,17 +8,42 @@
 // TIP: To dump output, run:
 // TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/declaration/no_prelude/fail_pattern_in_signature.carbon
 
-// CHECK:STDERR: fail_pattern_in_signature.carbon:[[@LINE+8]]:6: error: expected name in binding pattern [ExpectedBindingPattern]
+// CHECK:STDERR: fail_pattern_in_signature.carbon:[[@LINE+4]]:6: error: semantics TODO: `Support patterns besides bindings in parameter list` [SemanticsTodo]
 // CHECK:STDERR: fn F((a: {}, b: {}), c: {});
-// CHECK:STDERR:      ^
-// CHECK:STDERR:
-// CHECK:STDERR: fail_pattern_in_signature.carbon:[[@LINE+4]]:6: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo]
-// CHECK:STDERR: fn F((a: {}, b: {}), c: {});
-// CHECK:STDERR:      ^
+// CHECK:STDERR:      ^~~~~~~~~~~~~~
 // CHECK:STDERR:
 fn F((a: {}, b: {}), c: {});
 
 // CHECK:STDOUT: --- fail_pattern_in_signature.carbon
 // CHECK:STDOUT:
-// CHECK:STDOUT: file {}
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %tuple.type: type = tuple_type (%empty_struct_type, %empty_struct_type) [concrete]
+// CHECK:STDOUT:   %F.type: type = fn_type @F [concrete]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {
+// CHECK:STDOUT:     %a.patt: %empty_struct_type = binding_pattern a
+// CHECK:STDOUT:     %a.param_patt: %empty_struct_type = value_param_pattern %a.patt, runtime_param<unknown>
+// CHECK:STDOUT:     %b.patt: %empty_struct_type = binding_pattern b
+// CHECK:STDOUT:     %b.param_patt: %empty_struct_type = value_param_pattern %b.patt, runtime_param<unknown>
+// CHECK:STDOUT:     %.loc15_19: %tuple.type = tuple_pattern (%a.param_patt, %b.param_patt)
+// CHECK:STDOUT:     %c.patt: %empty_struct_type = binding_pattern c
+// CHECK:STDOUT:     %c.param_patt: %empty_struct_type = value_param_pattern %c.patt, runtime_param0
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %c.param: %empty_struct_type = value_param runtime_param0
+// CHECK:STDOUT:     %.loc15_26.1: type = splice_block %.loc15_26.3 [concrete = constants.%empty_struct_type] {
+// CHECK:STDOUT:       %.loc15_26.2: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:       %.loc15_26.3: type = converted %.loc15_26.2, constants.%empty_struct_type [concrete = constants.%empty_struct_type]
+// CHECK:STDOUT:     }
+// CHECK:STDOUT:     %c: %empty_struct_type = bind_name c, %c.param
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F(%.loc15_19: %tuple.type, %c.param_patt: %empty_struct_type);
 // CHECK:STDOUT:

+ 2 - 2
toolchain/check/testdata/interface/no_prelude/fail_assoc_const_not_binding.carbon

@@ -13,7 +13,7 @@
 library "[[@TEST_NAME]]";
 
 interface I {
-  // CHECK:STDERR: fail_tuple_pattern.carbon:[[@LINE+4]]:7: error: semantics TODO: `tuple pattern in let/var` [SemanticsTodo]
+  // CHECK:STDERR: fail_tuple_pattern.carbon:[[@LINE+4]]:7: error: pattern in associated constant declaration must be a single `:!` binding [ExpectedSymbolicBindingInAssociatedConstant]
   // CHECK:STDERR:   let (T:! type, U:! type);
   // CHECK:STDERR:       ^~~~~~~~~~~~~~~~~~~~
   // CHECK:STDERR:
@@ -25,7 +25,7 @@ interface I {
 library "[[@TEST_NAME]]";
 
 interface I {
-  // CHECK:STDERR: fail_tuple_pattern_with_default.carbon:[[@LINE+4]]:15: error: semantics TODO: `tuple pattern in let/var` [SemanticsTodo]
+  // CHECK:STDERR: fail_tuple_pattern_with_default.carbon:[[@LINE+4]]:15: error: pattern in associated constant declaration must be a single `:!` binding [ExpectedSymbolicBindingInAssociatedConstant]
   // CHECK:STDERR:   default let (T:! type, U:! type) = ({}, {});
   // CHECK:STDERR:               ^~~~~~~~~~~~~~~~~~~~
   // CHECK:STDERR:

+ 577 - 0
toolchain/check/testdata/tuple/no_prelude/tuple_pattern.carbon

@@ -0,0 +1,577 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// AUTOUPDATE
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/tuple/no_prelude/tuple_pattern.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/tuple/no_prelude/tuple_pattern.carbon
+
+
+// --- basic.carbon
+
+library "[[@TEST_NAME]]";
+
+fn F() {
+  let (x: {}, y: {}) = ({}, {});
+  var (a: {}, b: {});
+  var (c: {}, d: {}) = ({}, {});
+}
+
+// --- variable.carbon
+
+library "[[@TEST_NAME]]";
+
+fn F() {
+  var tuple: ({}, {}) = ({}, {});
+  var (x: {}, y: {}) = tuple;
+}
+
+fn G() {
+  let tuple: ({}, {}) = ({}, {});
+  var (x: {}, y: {}) = tuple;
+}
+
+fn MakeTuple() -> ({}, {});
+
+fn H() {
+  var (x: {}, y: {}) = MakeTuple();
+}
+
+// --- nested.carbon
+
+library "[[@TEST_NAME]]";
+
+fn F() {
+  let (x: {}, (y: {}, z: {})) = ({}, ({}, {}));
+}
+
+// --- package_scope.carbon
+
+library "[[@TEST_NAME]]";
+
+let (x: {}, y: {}) = ({}, {});
+
+// --- fail_in_interface.carbon
+
+library "[[@TEST_NAME]]";
+
+interface I {
+  // CHECK:STDERR: fail_in_interface.carbon:[[@LINE+4]]:7: error: pattern in associated constant declaration must be a single `:!` binding [ExpectedSymbolicBindingInAssociatedConstant]
+  // CHECK:STDERR:   let (x: {}, y: {});
+  // CHECK:STDERR:       ^~~~~~~~~~~~~~
+  // CHECK:STDERR:
+  let (x: {}, y: {});
+}
+
+// --- fail_in_class.carbon
+
+library "[[@TEST_NAME]]";
+
+class C {
+  // CHECK:STDERR: fail_in_class.carbon:[[@LINE+4]]:7: error: pattern in field declaration is not a single `:` binding [ExpectedSymbolicBindingInFieldDecl]
+  // CHECK:STDERR:   var (x: {}, y: {});
+  // CHECK:STDERR:       ^~~~~~~~~~~~~~
+  // CHECK:STDERR:
+  var (x: {}, y: {});
+}
+
+// --- fail_initializer_mismatch.carbon
+
+library "[[@TEST_NAME]]";
+
+// CHECK:STDERR: fail_initializer_mismatch.carbon:[[@LINE+4]]:5: error: tuple pattern expects 2 elements, but tuple literal has 1 [TuplePatternSizeDoesntMatchLiteral]
+// CHECK:STDERR: let (x: {}, y: {}) = ({},);
+// CHECK:STDERR:     ^~~~~~~~~~~~~~
+// CHECK:STDERR:
+let (x: {}, y: {}) = ({},);
+
+// CHECK:STDERR: fail_initializer_mismatch.carbon:[[@LINE+4]]:5: error: tuple pattern expects 2 elements, but tuple literal has 3 [TuplePatternSizeDoesntMatchLiteral]
+// CHECK:STDERR: let (a: {}, b: {}) = ({}, {}, {});
+// CHECK:STDERR:     ^~~~~~~~~~~~~~
+// CHECK:STDERR:
+let (a: {}, b: {}) = ({}, {}, {});
+
+// CHECK:STDOUT: --- basic.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %F.type: type = fn_type @F [concrete]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [concrete]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %tuple.type.b6b: type = tuple_type (%empty_struct_type, %empty_struct_type) [concrete]
+// CHECK:STDOUT:   %empty_struct: %empty_struct_type = struct_value () [concrete]
+// CHECK:STDOUT:   %tuple: %tuple.type.b6b = tuple_value (%empty_struct, %empty_struct) [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   name_binding_decl {
+// CHECK:STDOUT:     %x.patt: %empty_struct_type = binding_pattern x
+// CHECK:STDOUT:     %y.patt: %empty_struct_type = binding_pattern y
+// CHECK:STDOUT:     %.loc5_20: %tuple.type.b6b = tuple_pattern (%x.patt, %y.patt)
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc5_26.1: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:   %.loc5_30.1: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:   %.loc5_31: %tuple.type.b6b = tuple_literal (%.loc5_26.1, %.loc5_30.1)
+// CHECK:STDOUT:   %.loc5_12.1: type = splice_block %.loc5_12.3 [concrete = constants.%empty_struct_type] {
+// CHECK:STDOUT:     %.loc5_12.2: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:     %.loc5_12.3: type = converted %.loc5_12.2, constants.%empty_struct_type [concrete = constants.%empty_struct_type]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %empty_struct.loc5_26: %empty_struct_type = struct_value () [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %.loc5_26.2: %empty_struct_type = converted %.loc5_26.1, %empty_struct.loc5_26 [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %x: %empty_struct_type = bind_name x, %.loc5_26.2
+// CHECK:STDOUT:   %.loc5_19.1: type = splice_block %.loc5_19.3 [concrete = constants.%empty_struct_type] {
+// CHECK:STDOUT:     %.loc5_19.2: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:     %.loc5_19.3: type = converted %.loc5_19.2, constants.%empty_struct_type [concrete = constants.%empty_struct_type]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %empty_struct.loc5_30: %empty_struct_type = struct_value () [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %.loc5_30.2: %empty_struct_type = converted %.loc5_30.1, %empty_struct.loc5_30 [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %y: %empty_struct_type = bind_name y, %.loc5_30.2
+// CHECK:STDOUT:   name_binding_decl {
+// CHECK:STDOUT:     %a.patt: %empty_struct_type = binding_pattern a
+// CHECK:STDOUT:     %b.patt: %empty_struct_type = binding_pattern b
+// CHECK:STDOUT:     %.loc6_20: %tuple.type.b6b = tuple_pattern (%a.patt, %b.patt)
+// CHECK:STDOUT:     %.loc6_3: %tuple.type.b6b = var_pattern %.loc6_20
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.var.loc6: ref %tuple.type.b6b = var <none>
+// CHECK:STDOUT:   %tuple.elem0.loc6: ref %empty_struct_type = tuple_access %.var.loc6, element0
+// CHECK:STDOUT:   %tuple.elem1.loc6: ref %empty_struct_type = tuple_access %.var.loc6, element1
+// CHECK:STDOUT:   %.loc6_12.1: type = splice_block %.loc6_12.3 [concrete = constants.%empty_struct_type] {
+// CHECK:STDOUT:     %.loc6_12.2: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:     %.loc6_12.3: type = converted %.loc6_12.2, constants.%empty_struct_type [concrete = constants.%empty_struct_type]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %a: ref %empty_struct_type = bind_name a, %tuple.elem0.loc6
+// CHECK:STDOUT:   %.loc6_19.1: type = splice_block %.loc6_19.3 [concrete = constants.%empty_struct_type] {
+// CHECK:STDOUT:     %.loc6_19.2: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:     %.loc6_19.3: type = converted %.loc6_19.2, constants.%empty_struct_type [concrete = constants.%empty_struct_type]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %b: ref %empty_struct_type = bind_name b, %tuple.elem1.loc6
+// CHECK:STDOUT:   name_binding_decl {
+// CHECK:STDOUT:     %c.patt: %empty_struct_type = binding_pattern c
+// CHECK:STDOUT:     %d.patt: %empty_struct_type = binding_pattern d
+// CHECK:STDOUT:     %.loc7_20: %tuple.type.b6b = tuple_pattern (%c.patt, %d.patt)
+// CHECK:STDOUT:     %.loc7_3.1: %tuple.type.b6b = var_pattern %.loc7_20
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.var.loc7: ref %tuple.type.b6b = var <none>
+// CHECK:STDOUT:   %.loc7_26.1: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:   %.loc7_30.1: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:   %.loc7_31.1: %tuple.type.b6b = tuple_literal (%.loc7_26.1, %.loc7_30.1)
+// CHECK:STDOUT:   %tuple.elem0.loc7_31: ref %empty_struct_type = tuple_access %.var.loc7, element0
+// CHECK:STDOUT:   %.loc7_26.2: init %empty_struct_type = struct_init () to %tuple.elem0.loc7_31 [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %.loc7_31.2: init %empty_struct_type = converted %.loc7_26.1, %.loc7_26.2 [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %tuple.elem1.loc7_31: ref %empty_struct_type = tuple_access %.var.loc7, element1
+// CHECK:STDOUT:   %.loc7_30.2: init %empty_struct_type = struct_init () to %tuple.elem1.loc7_31 [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %.loc7_31.3: init %empty_struct_type = converted %.loc7_30.1, %.loc7_30.2 [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %.loc7_31.4: init %tuple.type.b6b = tuple_init (%.loc7_31.2, %.loc7_31.3) to %.var.loc7 [concrete = constants.%tuple]
+// CHECK:STDOUT:   %.loc7_3.2: init %tuple.type.b6b = converted %.loc7_31.1, %.loc7_31.4 [concrete = constants.%tuple]
+// CHECK:STDOUT:   assign %.var.loc7, %.loc7_3.2
+// CHECK:STDOUT:   %tuple.elem0.loc7_3: ref %empty_struct_type = tuple_access %.var.loc7, element0
+// CHECK:STDOUT:   %tuple.elem1.loc7_3: ref %empty_struct_type = tuple_access %.var.loc7, element1
+// CHECK:STDOUT:   %.loc7_12.1: type = splice_block %.loc7_12.3 [concrete = constants.%empty_struct_type] {
+// CHECK:STDOUT:     %.loc7_12.2: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:     %.loc7_12.3: type = converted %.loc7_12.2, constants.%empty_struct_type [concrete = constants.%empty_struct_type]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %c: ref %empty_struct_type = bind_name c, %tuple.elem0.loc7_3
+// CHECK:STDOUT:   %.loc7_19.1: type = splice_block %.loc7_19.3 [concrete = constants.%empty_struct_type] {
+// CHECK:STDOUT:     %.loc7_19.2: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:     %.loc7_19.3: type = converted %.loc7_19.2, constants.%empty_struct_type [concrete = constants.%empty_struct_type]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %d: ref %empty_struct_type = bind_name d, %tuple.elem1.loc7_3
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- variable.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %F.type: type = fn_type @F [concrete]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [concrete]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %tuple.type.b6b: type = tuple_type (%empty_struct_type, %empty_struct_type) [concrete]
+// CHECK:STDOUT:   %empty_struct: %empty_struct_type = struct_value () [concrete]
+// CHECK:STDOUT:   %tuple: %tuple.type.b6b = tuple_value (%empty_struct, %empty_struct) [concrete]
+// CHECK:STDOUT:   %G.type: type = fn_type @G [concrete]
+// CHECK:STDOUT:   %G: %G.type = struct_value () [concrete]
+// CHECK:STDOUT:   %MakeTuple.type: type = fn_type @MakeTuple [concrete]
+// CHECK:STDOUT:   %MakeTuple: %MakeTuple.type = struct_value () [concrete]
+// CHECK:STDOUT:   %H.type: type = fn_type @H [concrete]
+// CHECK:STDOUT:   %H: %H.type = struct_value () [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:     .G = %G.decl
+// CHECK:STDOUT:     .MakeTuple = %MakeTuple.decl
+// CHECK:STDOUT:     .H = %H.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {}
+// CHECK:STDOUT:   %G.decl: %G.type = fn_decl @G [concrete = constants.%G] {} {}
+// CHECK:STDOUT:   %MakeTuple.decl: %MakeTuple.type = fn_decl @MakeTuple [concrete = constants.%MakeTuple] {
+// CHECK:STDOUT:     %return.patt: %tuple.type.b6b = return_slot_pattern
+// CHECK:STDOUT:     %return.param_patt: %tuple.type.b6b = out_param_pattern %return.patt, runtime_param0
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %.loc14_21: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:     %.loc14_25: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:     %.loc14_26.1: %tuple.type.b6b = tuple_literal (%.loc14_21, %.loc14_25)
+// CHECK:STDOUT:     %.loc14_26.2: type = converted %.loc14_21, constants.%empty_struct_type [concrete = constants.%empty_struct_type]
+// CHECK:STDOUT:     %.loc14_26.3: type = converted %.loc14_25, constants.%empty_struct_type [concrete = constants.%empty_struct_type]
+// CHECK:STDOUT:     %.loc14_26.4: type = converted %.loc14_26.1, constants.%tuple.type.b6b [concrete = constants.%tuple.type.b6b]
+// CHECK:STDOUT:     %return.param: ref %tuple.type.b6b = out_param runtime_param0
+// CHECK:STDOUT:     %return: ref %tuple.type.b6b = return_slot %return.param
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %H.decl: %H.type = fn_decl @H [concrete = constants.%H] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   name_binding_decl {
+// CHECK:STDOUT:     %tuple.patt: %tuple.type.b6b = binding_pattern tuple
+// CHECK:STDOUT:     %.loc5_3.1: %tuple.type.b6b = var_pattern %tuple.patt
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %tuple.var: ref %tuple.type.b6b = var tuple
+// CHECK:STDOUT:   %.loc5_27.1: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:   %.loc5_31.1: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:   %.loc5_32.1: %tuple.type.b6b = tuple_literal (%.loc5_27.1, %.loc5_31.1)
+// CHECK:STDOUT:   %tuple.elem0.loc5: ref %empty_struct_type = tuple_access %tuple.var, element0
+// CHECK:STDOUT:   %.loc5_27.2: init %empty_struct_type = struct_init () to %tuple.elem0.loc5 [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %.loc5_32.2: init %empty_struct_type = converted %.loc5_27.1, %.loc5_27.2 [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %tuple.elem1.loc5: ref %empty_struct_type = tuple_access %tuple.var, element1
+// CHECK:STDOUT:   %.loc5_31.2: init %empty_struct_type = struct_init () to %tuple.elem1.loc5 [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %.loc5_32.3: init %empty_struct_type = converted %.loc5_31.1, %.loc5_31.2 [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %.loc5_32.4: init %tuple.type.b6b = tuple_init (%.loc5_32.2, %.loc5_32.3) to %tuple.var [concrete = constants.%tuple]
+// CHECK:STDOUT:   %.loc5_3.2: init %tuple.type.b6b = converted %.loc5_32.1, %.loc5_32.4 [concrete = constants.%tuple]
+// CHECK:STDOUT:   assign %tuple.var, %.loc5_3.2
+// CHECK:STDOUT:   %.loc5_21.1: type = splice_block %.loc5_21.5 [concrete = constants.%tuple.type.b6b] {
+// CHECK:STDOUT:     %.loc5_16: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:     %.loc5_20: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:     %.loc5_21.2: %tuple.type.b6b = tuple_literal (%.loc5_16, %.loc5_20)
+// CHECK:STDOUT:     %.loc5_21.3: type = converted %.loc5_16, constants.%empty_struct_type [concrete = constants.%empty_struct_type]
+// CHECK:STDOUT:     %.loc5_21.4: type = converted %.loc5_20, constants.%empty_struct_type [concrete = constants.%empty_struct_type]
+// CHECK:STDOUT:     %.loc5_21.5: type = converted %.loc5_21.2, constants.%tuple.type.b6b [concrete = constants.%tuple.type.b6b]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %tuple: ref %tuple.type.b6b = bind_name tuple, %tuple.var
+// CHECK:STDOUT:   name_binding_decl {
+// CHECK:STDOUT:     %x.patt: %empty_struct_type = binding_pattern x
+// CHECK:STDOUT:     %y.patt: %empty_struct_type = binding_pattern y
+// CHECK:STDOUT:     %.loc6_20: %tuple.type.b6b = tuple_pattern (%x.patt, %y.patt)
+// CHECK:STDOUT:     %.loc6_3.1: %tuple.type.b6b = var_pattern %.loc6_20
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.var: ref %tuple.type.b6b = var <none>
+// CHECK:STDOUT:   %tuple.ref: ref %tuple.type.b6b = name_ref tuple, %tuple
+// CHECK:STDOUT:   %tuple.elem0.loc6_24.1: ref %empty_struct_type = tuple_access %tuple.ref, element0
+// CHECK:STDOUT:   %tuple.elem0.loc6_24.2: ref %empty_struct_type = tuple_access %.var, element0
+// CHECK:STDOUT:   %.loc6_24.1: init %empty_struct_type = struct_init () to %tuple.elem0.loc6_24.2 [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %.loc6_24.2: init %empty_struct_type = converted %tuple.elem0.loc6_24.1, %.loc6_24.1 [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %tuple.elem1.loc6_24.1: ref %empty_struct_type = tuple_access %tuple.ref, element1
+// CHECK:STDOUT:   %tuple.elem1.loc6_24.2: ref %empty_struct_type = tuple_access %.var, element1
+// CHECK:STDOUT:   %.loc6_24.3: init %empty_struct_type = struct_init () to %tuple.elem1.loc6_24.2 [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %.loc6_24.4: init %empty_struct_type = converted %tuple.elem1.loc6_24.1, %.loc6_24.3 [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %.loc6_24.5: init %tuple.type.b6b = tuple_init (%.loc6_24.2, %.loc6_24.4) to %.var [concrete = constants.%tuple]
+// CHECK:STDOUT:   %.loc6_3.2: init %tuple.type.b6b = converted %tuple.ref, %.loc6_24.5 [concrete = constants.%tuple]
+// CHECK:STDOUT:   assign %.var, %.loc6_3.2
+// CHECK:STDOUT:   %tuple.elem0.loc6_3: ref %empty_struct_type = tuple_access %.var, element0
+// CHECK:STDOUT:   %tuple.elem1.loc6_3: ref %empty_struct_type = tuple_access %.var, element1
+// CHECK:STDOUT:   %.loc6_12.1: type = splice_block %.loc6_12.3 [concrete = constants.%empty_struct_type] {
+// CHECK:STDOUT:     %.loc6_12.2: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:     %.loc6_12.3: type = converted %.loc6_12.2, constants.%empty_struct_type [concrete = constants.%empty_struct_type]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %x: ref %empty_struct_type = bind_name x, %tuple.elem0.loc6_3
+// CHECK:STDOUT:   %.loc6_19.1: type = splice_block %.loc6_19.3 [concrete = constants.%empty_struct_type] {
+// CHECK:STDOUT:     %.loc6_19.2: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:     %.loc6_19.3: type = converted %.loc6_19.2, constants.%empty_struct_type [concrete = constants.%empty_struct_type]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %y: ref %empty_struct_type = bind_name y, %tuple.elem1.loc6_3
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @G() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   name_binding_decl {
+// CHECK:STDOUT:     %tuple.patt: %tuple.type.b6b = binding_pattern tuple
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc10_27: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:   %.loc10_31: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:   %.loc10_32.1: %tuple.type.b6b = tuple_literal (%.loc10_27, %.loc10_31)
+// CHECK:STDOUT:   %.loc10_21.1: type = splice_block %.loc10_21.5 [concrete = constants.%tuple.type.b6b] {
+// CHECK:STDOUT:     %.loc10_16: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:     %.loc10_20: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:     %.loc10_21.2: %tuple.type.b6b = tuple_literal (%.loc10_16, %.loc10_20)
+// CHECK:STDOUT:     %.loc10_21.3: type = converted %.loc10_16, constants.%empty_struct_type [concrete = constants.%empty_struct_type]
+// CHECK:STDOUT:     %.loc10_21.4: type = converted %.loc10_20, constants.%empty_struct_type [concrete = constants.%empty_struct_type]
+// CHECK:STDOUT:     %.loc10_21.5: type = converted %.loc10_21.2, constants.%tuple.type.b6b [concrete = constants.%tuple.type.b6b]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %empty_struct.loc10_27: %empty_struct_type = struct_value () [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %.loc10_32.2: %empty_struct_type = converted %.loc10_27, %empty_struct.loc10_27 [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %empty_struct.loc10_31: %empty_struct_type = struct_value () [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %.loc10_32.3: %empty_struct_type = converted %.loc10_31, %empty_struct.loc10_31 [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %tuple.loc10_32: %tuple.type.b6b = tuple_value (%.loc10_32.2, %.loc10_32.3) [concrete = constants.%tuple]
+// CHECK:STDOUT:   %.loc10_32.4: %tuple.type.b6b = converted %.loc10_32.1, %tuple.loc10_32 [concrete = constants.%tuple]
+// CHECK:STDOUT:   %tuple.loc10_7: %tuple.type.b6b = bind_name tuple, %.loc10_32.4
+// CHECK:STDOUT:   name_binding_decl {
+// CHECK:STDOUT:     %x.patt: %empty_struct_type = binding_pattern x
+// CHECK:STDOUT:     %y.patt: %empty_struct_type = binding_pattern y
+// CHECK:STDOUT:     %.loc11_20: %tuple.type.b6b = tuple_pattern (%x.patt, %y.patt)
+// CHECK:STDOUT:     %.loc11_3.1: %tuple.type.b6b = var_pattern %.loc11_20
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.var: ref %tuple.type.b6b = var <none>
+// CHECK:STDOUT:   %tuple.ref: %tuple.type.b6b = name_ref tuple, %tuple.loc10_7
+// CHECK:STDOUT:   %tuple.elem0.loc11_24.1: %empty_struct_type = tuple_access %tuple.ref, element0
+// CHECK:STDOUT:   %tuple.elem0.loc11_24.2: ref %empty_struct_type = tuple_access %.var, element0
+// CHECK:STDOUT:   %.loc11_24.1: init %empty_struct_type = struct_init () to %tuple.elem0.loc11_24.2 [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %.loc11_24.2: init %empty_struct_type = converted %tuple.elem0.loc11_24.1, %.loc11_24.1 [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %tuple.elem1.loc11_24.1: %empty_struct_type = tuple_access %tuple.ref, element1
+// CHECK:STDOUT:   %tuple.elem1.loc11_24.2: ref %empty_struct_type = tuple_access %.var, element1
+// CHECK:STDOUT:   %.loc11_24.3: init %empty_struct_type = struct_init () to %tuple.elem1.loc11_24.2 [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %.loc11_24.4: init %empty_struct_type = converted %tuple.elem1.loc11_24.1, %.loc11_24.3 [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %.loc11_24.5: init %tuple.type.b6b = tuple_init (%.loc11_24.2, %.loc11_24.4) to %.var [concrete = constants.%tuple]
+// CHECK:STDOUT:   %.loc11_3.2: init %tuple.type.b6b = converted %tuple.ref, %.loc11_24.5 [concrete = constants.%tuple]
+// CHECK:STDOUT:   assign %.var, %.loc11_3.2
+// CHECK:STDOUT:   %tuple.elem0.loc11_3: ref %empty_struct_type = tuple_access %.var, element0
+// CHECK:STDOUT:   %tuple.elem1.loc11_3: ref %empty_struct_type = tuple_access %.var, element1
+// CHECK:STDOUT:   %.loc11_12.1: type = splice_block %.loc11_12.3 [concrete = constants.%empty_struct_type] {
+// CHECK:STDOUT:     %.loc11_12.2: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:     %.loc11_12.3: type = converted %.loc11_12.2, constants.%empty_struct_type [concrete = constants.%empty_struct_type]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %x: ref %empty_struct_type = bind_name x, %tuple.elem0.loc11_3
+// CHECK:STDOUT:   %.loc11_19.1: type = splice_block %.loc11_19.3 [concrete = constants.%empty_struct_type] {
+// CHECK:STDOUT:     %.loc11_19.2: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:     %.loc11_19.3: type = converted %.loc11_19.2, constants.%empty_struct_type [concrete = constants.%empty_struct_type]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %y: ref %empty_struct_type = bind_name y, %tuple.elem1.loc11_3
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @MakeTuple() -> %tuple.type.b6b;
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @H() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   name_binding_decl {
+// CHECK:STDOUT:     %x.patt: %empty_struct_type = binding_pattern x
+// CHECK:STDOUT:     %y.patt: %empty_struct_type = binding_pattern y
+// CHECK:STDOUT:     %.loc17_20: %tuple.type.b6b = tuple_pattern (%x.patt, %y.patt)
+// CHECK:STDOUT:     %.loc17_3.1: %tuple.type.b6b = var_pattern %.loc17_20
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.var: ref %tuple.type.b6b = var <none>
+// CHECK:STDOUT:   %MakeTuple.ref: %MakeTuple.type = name_ref MakeTuple, file.%MakeTuple.decl [concrete = constants.%MakeTuple]
+// CHECK:STDOUT:   %.loc17_3.2: ref %tuple.type.b6b = splice_block %.var {}
+// CHECK:STDOUT:   %MakeTuple.call: init %tuple.type.b6b = call %MakeTuple.ref() to %.loc17_3.2
+// CHECK:STDOUT:   assign %.var, %MakeTuple.call
+// CHECK:STDOUT:   %tuple.elem0: ref %empty_struct_type = tuple_access %.var, element0
+// CHECK:STDOUT:   %tuple.elem1: ref %empty_struct_type = tuple_access %.var, element1
+// CHECK:STDOUT:   %.loc17_12.1: type = splice_block %.loc17_12.3 [concrete = constants.%empty_struct_type] {
+// CHECK:STDOUT:     %.loc17_12.2: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:     %.loc17_12.3: type = converted %.loc17_12.2, constants.%empty_struct_type [concrete = constants.%empty_struct_type]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %x: ref %empty_struct_type = bind_name x, %tuple.elem0
+// CHECK:STDOUT:   %.loc17_19.1: type = splice_block %.loc17_19.3 [concrete = constants.%empty_struct_type] {
+// CHECK:STDOUT:     %.loc17_19.2: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:     %.loc17_19.3: type = converted %.loc17_19.2, constants.%empty_struct_type [concrete = constants.%empty_struct_type]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %y: ref %empty_struct_type = bind_name y, %tuple.elem1
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- nested.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %F.type: type = fn_type @F [concrete]
+// CHECK:STDOUT:   %F: %F.type = struct_value () [concrete]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %tuple.type.b6b: type = tuple_type (%empty_struct_type, %empty_struct_type) [concrete]
+// CHECK:STDOUT:   %tuple.type.6ca: type = tuple_type (%empty_struct_type, %tuple.type.b6b) [concrete]
+// CHECK:STDOUT:   %empty_struct: %empty_struct_type = struct_value () [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .F = %F.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   name_binding_decl {
+// CHECK:STDOUT:     %x.patt: %empty_struct_type = binding_pattern x
+// CHECK:STDOUT:     %y.patt: %empty_struct_type = binding_pattern y
+// CHECK:STDOUT:     %z.patt: %empty_struct_type = binding_pattern z
+// CHECK:STDOUT:     %.loc5_28: %tuple.type.b6b = tuple_pattern (%y.patt, %z.patt)
+// CHECK:STDOUT:     %.loc5_29: %tuple.type.6ca = tuple_pattern (%x.patt, %.loc5_28)
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc5_35.1: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:   %.loc5_40.1: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:   %.loc5_44.1: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:   %.loc5_45: %tuple.type.b6b = tuple_literal (%.loc5_40.1, %.loc5_44.1)
+// CHECK:STDOUT:   %.loc5_46: %tuple.type.6ca = tuple_literal (%.loc5_35.1, %.loc5_45)
+// CHECK:STDOUT:   %.loc5_12.1: type = splice_block %.loc5_12.3 [concrete = constants.%empty_struct_type] {
+// CHECK:STDOUT:     %.loc5_12.2: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:     %.loc5_12.3: type = converted %.loc5_12.2, constants.%empty_struct_type [concrete = constants.%empty_struct_type]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %empty_struct.loc5_35: %empty_struct_type = struct_value () [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %.loc5_35.2: %empty_struct_type = converted %.loc5_35.1, %empty_struct.loc5_35 [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %x: %empty_struct_type = bind_name x, %.loc5_35.2
+// CHECK:STDOUT:   %.loc5_20.1: type = splice_block %.loc5_20.3 [concrete = constants.%empty_struct_type] {
+// CHECK:STDOUT:     %.loc5_20.2: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:     %.loc5_20.3: type = converted %.loc5_20.2, constants.%empty_struct_type [concrete = constants.%empty_struct_type]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %empty_struct.loc5_40: %empty_struct_type = struct_value () [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %.loc5_40.2: %empty_struct_type = converted %.loc5_40.1, %empty_struct.loc5_40 [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %y: %empty_struct_type = bind_name y, %.loc5_40.2
+// CHECK:STDOUT:   %.loc5_27.1: type = splice_block %.loc5_27.3 [concrete = constants.%empty_struct_type] {
+// CHECK:STDOUT:     %.loc5_27.2: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:     %.loc5_27.3: type = converted %.loc5_27.2, constants.%empty_struct_type [concrete = constants.%empty_struct_type]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %empty_struct.loc5_44: %empty_struct_type = struct_value () [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %.loc5_44.2: %empty_struct_type = converted %.loc5_44.1, %empty_struct.loc5_44 [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %z: %empty_struct_type = bind_name z, %.loc5_44.2
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- package_scope.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %tuple.type: type = tuple_type (%empty_struct_type, %empty_struct_type) [concrete]
+// CHECK:STDOUT:   %empty_struct: %empty_struct_type = struct_value () [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .x = %x
+// CHECK:STDOUT:     .y = %y
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   name_binding_decl {
+// CHECK:STDOUT:     %x.patt: %empty_struct_type = binding_pattern x
+// CHECK:STDOUT:     %y.patt: %empty_struct_type = binding_pattern y
+// CHECK:STDOUT:     %.loc4_18: %tuple.type = tuple_pattern (%x.patt, %y.patt)
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc4_10.1: type = splice_block %.loc4_10.3 [concrete = constants.%empty_struct_type] {
+// CHECK:STDOUT:     %.loc4_10.2: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:     %.loc4_10.3: type = converted %.loc4_10.2, constants.%empty_struct_type [concrete = constants.%empty_struct_type]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %empty_struct.loc4_24: %empty_struct_type = struct_value () [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %.loc4_24: %empty_struct_type = converted @__global_init.%.loc4_24, %empty_struct.loc4_24 [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %x: %empty_struct_type = bind_name x, %.loc4_24
+// CHECK:STDOUT:   %.loc4_17.1: type = splice_block %.loc4_17.3 [concrete = constants.%empty_struct_type] {
+// CHECK:STDOUT:     %.loc4_17.2: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:     %.loc4_17.3: type = converted %.loc4_17.2, constants.%empty_struct_type [concrete = constants.%empty_struct_type]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %empty_struct.loc4_28: %empty_struct_type = struct_value () [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %.loc4_28: %empty_struct_type = converted @__global_init.%.loc4_28, %empty_struct.loc4_28 [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %y: %empty_struct_type = bind_name y, %.loc4_28
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @__global_init() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc4_24: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:   %.loc4_28: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:   %.loc4_29: %tuple.type = tuple_literal (%.loc4_24, %.loc4_28)
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_in_interface.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %I.type: type = facet_type <@I> [concrete]
+// CHECK:STDOUT:   %Self: %I.type = bind_symbolic_name Self, 0 [symbolic]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .I = %I.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @I {
+// CHECK:STDOUT:   %Self: %I.type = bind_symbolic_name Self, 0 [symbolic = constants.%Self]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   .x = <unexpected>.inst21.loc9_8
+// CHECK:STDOUT:   .y = <unexpected>.inst25.loc9_15
+// CHECK:STDOUT:   has_error
+// CHECK:STDOUT:   witness = ()
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_in_class.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [concrete]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %C.elem: type = unbound_element_type %C, %empty_struct_type [concrete]
+// CHECK:STDOUT:   %tuple.type: type = tuple_type (%C.elem, %C.elem) [concrete]
+// CHECK:STDOUT:   %struct_type.x.y: type = struct_type {.x: %empty_struct_type, .y: %empty_struct_type} [concrete]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.x.y [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [concrete = constants.%C] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT:   name_binding_decl {
+// CHECK:STDOUT:     %.loc9_20: %tuple.type = tuple_pattern (<unexpected>.inst20.loc9_9, <unexpected>.inst23.loc9_16)
+// CHECK:STDOUT:     %.loc9_3: %tuple.type = var_pattern %.loc9_20
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.var: ref %tuple.type = var <none>
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %struct_type.x.y [concrete = constants.%complete_type]
+// CHECK:STDOUT:   complete_type_witness = %complete_type
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%C
+// CHECK:STDOUT:   .x = <unexpected>.inst20.loc9_9
+// CHECK:STDOUT:   .y = <unexpected>.inst23.loc9_16
+// CHECK:STDOUT:   has_error
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_initializer_mismatch.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %tuple.type.b6b: type = tuple_type (%empty_struct_type, %empty_struct_type) [concrete]
+// CHECK:STDOUT:   %tuple.type.572: type = tuple_type (%empty_struct_type) [concrete]
+// CHECK:STDOUT:   %tuple.type.8d4: type = tuple_type (%empty_struct_type, %empty_struct_type, %empty_struct_type) [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .x = <unexpected>.inst17.loc8_6
+// CHECK:STDOUT:     .y = <unexpected>.inst21.loc8_13
+// CHECK:STDOUT:     .a = <unexpected>.inst31.loc14_6
+// CHECK:STDOUT:     .b = <unexpected>.inst35.loc14_13
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   name_binding_decl {
+// CHECK:STDOUT:     %x.patt: %empty_struct_type = binding_pattern x
+// CHECK:STDOUT:     %y.patt: %empty_struct_type = binding_pattern y
+// CHECK:STDOUT:     %.loc8: %tuple.type.b6b = tuple_pattern (%x.patt, %y.patt)
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   name_binding_decl {
+// CHECK:STDOUT:     %a.patt: %empty_struct_type = binding_pattern a
+// CHECK:STDOUT:     %b.patt: %empty_struct_type = binding_pattern b
+// CHECK:STDOUT:     %.loc14: %tuple.type.b6b = tuple_pattern (%a.patt, %b.patt)
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @__global_init() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %.loc8_24: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:   %.loc8_26: %tuple.type.572 = tuple_literal (%.loc8_24)
+// CHECK:STDOUT:   %.loc14_24: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:   %.loc14_28: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:   %.loc14_32: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:   %.loc14_33: %tuple.type.8d4 = tuple_literal (%.loc14_24, %.loc14_28, %.loc14_32)
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 4 - 0
toolchain/diagnostics/diagnostic_kind.def

@@ -252,6 +252,7 @@ CARBON_DIAGNOSTIC_KIND(ClassForwardDeclaredHere)
 CARBON_DIAGNOSTIC_KIND(ClassSpecificDeclOutsideClass)
 CARBON_DIAGNOSTIC_KIND(ClassSpecificDeclPrevious)
 CARBON_DIAGNOSTIC_KIND(ClassIncompleteWithinDefinition)
+CARBON_DIAGNOSTIC_KIND(ExpectedSymbolicBindingInFieldDecl)
 CARBON_DIAGNOSTIC_KIND(ImplWithoutBase)
 
 // Deduction.
@@ -447,6 +448,9 @@ CARBON_DIAGNOSTIC_KIND(AssociatedConstantWithDifferentValues)
 CARBON_DIAGNOSTIC_KIND(RewriteForAssociatedFunction)
 CARBON_DIAGNOSTIC_KIND(ResolveFacetTypeWithUndefinedInterface)
 
+// Pattern matching diagnostics.
+CARBON_DIAGNOSTIC_KIND(TuplePatternSizeDoesntMatchLiteral)
+
 // ============================================================================
 // Language server diagnostics
 // ============================================================================

+ 1 - 1
toolchain/parse/handle_choice.cpp

@@ -68,7 +68,7 @@ auto HandleChoiceAlternative(Context& context) -> void {
 
   if (context.PositionIs(Lex::TokenKind::OpenParen)) {
     context.AddLeafNode(NodeKind::IdentifierNameBeforeParams, *token);
-    context.PushState(State::PatternListAsTuple);
+    context.PushState(State::PatternListAsExplicit);
   } else {
     context.AddLeafNode(NodeKind::IdentifierNameNotBeforeParams, *token);
   }

+ 2 - 2
toolchain/parse/handle_decl_name_and_params.cpp

@@ -54,7 +54,7 @@ auto HandleDeclNameAndParams(Context& context) -> void {
       context.AddLeafNode(NodeKind::IdentifierNameBeforeParams, *identifier);
       state.state = State::DeclNameAndParamsAfterParams;
       context.PushState(state);
-      context.PushState(State::PatternListAsTuple);
+      context.PushState(State::PatternListAsExplicit);
       break;
 
     default:
@@ -77,7 +77,7 @@ auto HandleDeclNameAndParamsAfterImplicit(Context& context) -> void {
 
   state.state = State::DeclNameAndParamsAfterParams;
   context.PushState(state);
-  context.PushState(State::PatternListAsTuple);
+  context.PushState(State::PatternListAsExplicit);
 }
 
 auto HandleDeclNameAndParamsAfterParams(Context& context) -> void {

+ 49 - 27
toolchain/parse/handle_pattern_list.cpp

@@ -7,7 +7,7 @@
 
 namespace Carbon::Parse {
 
-// Handles PatternListElementAs(Implicit|Tuple).
+// Handles PatternListElementAs(Tuple|Explicit|Implicit).
 static auto HandlePatternListElement(Context& context, State pattern_state,
                                      State finish_state) -> void {
   auto state = context.PopState();
@@ -16,17 +16,22 @@ static auto HandlePatternListElement(Context& context, State pattern_state,
   context.PushStateForPattern(pattern_state, state.in_var_pattern);
 }
 
-auto HandlePatternListElementAsImplicit(Context& context) -> void {
-  HandlePatternListElement(context, State::BindingPattern,
-                           State::PatternListElementFinishAsImplicit);
-}
-
 auto HandlePatternListElementAsTuple(Context& context) -> void {
-  HandlePatternListElement(context, State::BindingPattern,
+  HandlePatternListElement(context, State::Pattern,
                            State::PatternListElementFinishAsTuple);
 }
 
-// Handles PatternListElementFinishAs(Implicit|Tuple).
+auto HandlePatternListElementAsExplicit(Context& context) -> void {
+  HandlePatternListElement(context, State::Pattern,
+                           State::PatternListElementFinishAsExplicit);
+}
+
+auto HandlePatternListElementAsImplicit(Context& context) -> void {
+  HandlePatternListElement(context, State::Pattern,
+                           State::PatternListElementFinishAsImplicit);
+}
+
+// Handles PatternListElementFinishAs(Tuple|Explicit|Implicit).
 static auto HandlePatternListElementFinish(Context& context,
                                            Lex::TokenKind close_token,
                                            State param_state) -> void {
@@ -43,17 +48,22 @@ static auto HandlePatternListElementFinish(Context& context,
   }
 }
 
-auto HandlePatternListElementFinishAsImplicit(Context& context) -> void {
-  HandlePatternListElementFinish(context, Lex::TokenKind::CloseSquareBracket,
-                                 State::PatternListElementAsImplicit);
-}
-
 auto HandlePatternListElementFinishAsTuple(Context& context) -> void {
   HandlePatternListElementFinish(context, Lex::TokenKind::CloseParen,
                                  State::PatternListElementAsTuple);
 }
 
-// Handles PatternListAs(Implicit|Tuple).
+auto HandlePatternListElementFinishAsExplicit(Context& context) -> void {
+  HandlePatternListElementFinish(context, Lex::TokenKind::CloseParen,
+                                 State::PatternListElementAsExplicit);
+}
+
+auto HandlePatternListElementFinishAsImplicit(Context& context) -> void {
+  HandlePatternListElementFinish(context, Lex::TokenKind::CloseSquareBracket,
+                                 State::PatternListElementAsImplicit);
+}
+
+// Handles PatternListAs(Tuple|Explicit|Implicit).
 static auto HandlePatternList(Context& context, NodeKind node_kind,
                               Lex::TokenKind open_token_kind,
                               Lex::TokenKind close_token_kind,
@@ -68,13 +78,6 @@ static auto HandlePatternList(Context& context, NodeKind node_kind,
   }
 }
 
-auto HandlePatternListAsImplicit(Context& context) -> void {
-  HandlePatternList(
-      context, NodeKind::ImplicitParamListStart,
-      Lex::TokenKind::OpenSquareBracket, Lex::TokenKind::CloseSquareBracket,
-      State::PatternListElementAsImplicit, State::PatternListFinishAsImplicit);
-}
-
 auto HandlePatternListAsTuple(Context& context) -> void {
   HandlePatternList(context, NodeKind::TuplePatternStart,
                     Lex::TokenKind::OpenParen, Lex::TokenKind::CloseParen,
@@ -82,7 +85,21 @@ auto HandlePatternListAsTuple(Context& context) -> void {
                     State::PatternListFinishAsTuple);
 }
 
-// Handles PatternListFinishAs(Implicit|Tuple).
+auto HandlePatternListAsExplicit(Context& context) -> void {
+  HandlePatternList(context, NodeKind::ExplicitParamListStart,
+                    Lex::TokenKind::OpenParen, Lex::TokenKind::CloseParen,
+                    State::PatternListElementAsExplicit,
+                    State::PatternListFinishAsExplicit);
+}
+
+auto HandlePatternListAsImplicit(Context& context) -> void {
+  HandlePatternList(
+      context, NodeKind::ImplicitParamListStart,
+      Lex::TokenKind::OpenSquareBracket, Lex::TokenKind::CloseSquareBracket,
+      State::PatternListElementAsImplicit, State::PatternListFinishAsImplicit);
+}
+
+// Handles PatternListFinishAs(Tuple|Explicit|Implicit).
 static auto HandlePatternListFinish(Context& context, NodeKind node_kind,
                                     Lex::TokenKind token_kind) -> void {
   auto state = context.PopState();
@@ -91,14 +108,19 @@ static auto HandlePatternListFinish(Context& context, NodeKind node_kind,
                   state.has_error);
 }
 
-auto HandlePatternListFinishAsImplicit(Context& context) -> void {
-  HandlePatternListFinish(context, NodeKind::ImplicitParamList,
-                          Lex::TokenKind::CloseSquareBracket);
-}
-
 auto HandlePatternListFinishAsTuple(Context& context) -> void {
   HandlePatternListFinish(context, NodeKind::TuplePattern,
                           Lex::TokenKind::CloseParen);
 }
 
+auto HandlePatternListFinishAsExplicit(Context& context) -> void {
+  HandlePatternListFinish(context, NodeKind::ExplicitParamList,
+                          Lex::TokenKind::CloseParen);
+}
+
+auto HandlePatternListFinishAsImplicit(Context& context) -> void {
+  HandlePatternListFinish(context, NodeKind::ImplicitParamList,
+                          Lex::TokenKind::CloseSquareBracket);
+}
+
 }  // namespace Carbon::Parse

+ 2 - 0
toolchain/parse/node_kind.def

@@ -152,6 +152,8 @@ CARBON_PARSE_NODE_KIND(TuplePatternStart)
 CARBON_PARSE_NODE_KIND(PatternListComma)
 CARBON_PARSE_NODE_KIND(TuplePattern)
 
+CARBON_PARSE_NODE_KIND(ExplicitParamListStart)
+CARBON_PARSE_NODE_KIND(ExplicitParamList)
 CARBON_PARSE_NODE_KIND(ImplicitParamListStart)
 CARBON_PARSE_NODE_KIND(ImplicitParamList)
 

+ 16 - 15
toolchain/parse/state.def

@@ -239,7 +239,7 @@ CARBON_PARSE_STATE(CodeBlockFinish)
 //
 // name ( ... )
 // ^~~~
-//   1. PatternListAsTuple
+//   1. PatternListAsExplicit
 //   2. DeclNameAndParamsAfterParams
 //
 // name ...
@@ -256,7 +256,7 @@ CARBON_PARSE_STATE(DeclNameAndParams)
 //
 // name [ ... ] ( ... )
 //             ^
-//   1. PatternListAsTuple
+//   1. PatternListAsExplicit
 //   2. DeclNameAndParamsAfterParams
 //
 // name [ ... ] ???
@@ -827,9 +827,9 @@ CARBON_PARSE_STATE(AliasFinish)
 //
 //  ...
 // ^
-//   1. BindingPattern
-//   2. PatternListElementFinishAs(Implicit|Tuple)
-CARBON_PARSE_STATE_VARIANTS2(PatternListElement, Implicit, Tuple)
+//   1. Pattern
+//   2. PatternListElementFinishAs(Tuple|Explicit|Implicit)
+CARBON_PARSE_STATE_VARIANTS3(PatternListElement, Tuple, Explicit, Implicit)
 
 // Finishes parsing a pattern in a comma-separated list, including the
 // optional trailing `,`. If there are more patterns, enqueues another
@@ -845,38 +845,39 @@ CARBON_PARSE_STATE_VARIANTS2(PatternListElement, Implicit, Tuple)
 //
 // ... , ...
 //     ^
-//   1. PatternListElementAs(Implicit|Tuple)
+//   1. PatternListElementAs(Tuple|Explicit|Implicit)
 //
 // ...
 //    ^
 //   (state done)
-CARBON_PARSE_STATE_VARIANTS2(PatternListElementFinish, Implicit, Tuple)
+CARBON_PARSE_STATE_VARIANTS3(PatternListElementFinish, Tuple, Explicit,
+                             Implicit)
 
 // Handles processing of a tuple pattern (parentheses) or implicit parameter
 // list (square brackets).
 //
-// ( )        (variant is Tuple)
+// ( )        (variant is Tuple or Explicit)
 // ^
 // [ ]        (variant is Implicit)
 // ^
-//   1. PatternListFinishAs(Tuple|Implicit)
+//   1. PatternListFinishAs(Tuple|Explicit|Implicit)
 //
-// ( ... )    (variant is Tuple)
+// ( ... )    (variant is Tuple or Explicit)
 // ^
 // [ ... ]    (variant is Implicit)
 // ^
-//   1. PatternListElementAs(Tuple|Implicit)
-//   2. PatternListFinishAs(Tuple|Implicit)
-CARBON_PARSE_STATE_VARIANTS2(PatternList, Implicit, Tuple)
+//   1. PatternListElementAs(Tuple|Explicit|Implicit)
+//   2. PatternListFinishAs(Tuple|Explicit|Implicit)
+CARBON_PARSE_STATE_VARIANTS3(PatternList, Tuple, Explicit, Implicit)
 
 // Handles processing of a parameter list `]` or `)`.
 //
-// ( ... )    (variant is Tuple)
+// ( ... )    (variant is Tuple or Explicit)
 //       ^
 // [ ... ]    (variant is Implicit)
 //       ^
 //   (state done)
-CARBON_PARSE_STATE_VARIANTS2(PatternListFinish, Implicit, Tuple)
+CARBON_PARSE_STATE_VARIANTS3(PatternListFinish, Tuple, Explicit, Implicit)
 
 // Handles the processing of a `(condition)` up through the expression.
 //

+ 2 - 2
toolchain/parse/testdata/alias/basic.carbon

@@ -60,8 +60,8 @@ fn F() {
 // CHECK:STDOUT:     {kind: 'ClassDefinition', text: '}', subtree_size: 9},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'F'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:         {kind: 'AliasIntroducer', text: 'alias'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameNotBeforeParams', text: 'f'},

+ 2 - 2
toolchain/parse/testdata/alias/fail_syntax.carbon

@@ -106,8 +106,8 @@ alias a = b
 // CHECK:STDOUT:     {kind: 'Alias', text: ';', has_error: yes, subtree_size: 5},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'F'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:         {kind: 'AliasIntroducer', text: 'alias'},
 // CHECK:STDOUT:         {kind: 'InvalidParse', text: ';', has_error: yes},

+ 2 - 2
toolchain/parse/testdata/array/fail_syntax.carbon

@@ -87,8 +87,8 @@ var x: [i32];
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:       {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'X'},
-// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:             {kind: 'ArrayExprStart', text: '['},
 // CHECK:STDOUT:             {kind: 'InvalidParse', text: ':', has_error: yes},
 // CHECK:STDOUT:           {kind: 'ArrayExprSemi', text: ':', has_error: yes, subtree_size: 3},

+ 2 - 2
toolchain/parse/testdata/auto/match_case.carbon

@@ -20,8 +20,8 @@ fn f() -> bool {
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'f'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:           {kind: 'BoolTypeLiteral', text: 'bool'},
 // CHECK:STDOUT:         {kind: 'ReturnType', text: '->', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 7},

+ 2 - 2
toolchain/parse/testdata/auto/return.carbon

@@ -17,8 +17,8 @@ fn f() -> auto {
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'f'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:           {kind: 'AutoTypeLiteral', text: 'auto'},
 // CHECK:STDOUT:         {kind: 'ReturnType', text: '->', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 7},

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

@@ -35,8 +35,8 @@ fn F() {
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'F'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:         {kind: 'VariableIntroducer', text: 'var'},
 // CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: 'x'},

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

@@ -27,8 +27,8 @@ fn F() {
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'F'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:           {kind: 'IdentifierNameExpr', text: 'a'},
 // CHECK:STDOUT:           {kind: 'IdentifierNameNotBeforeParams', text: ';', has_error: yes},

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

@@ -18,8 +18,8 @@ fn F() {
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'F'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:                       {kind: 'IdentifierNameExpr', text: 'a'},
 // CHECK:STDOUT:                       {kind: 'IdentifierNameNotBeforeParams', text: 'b'},

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

@@ -19,8 +19,8 @@ fn B() {}
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'A'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:     {kind: 'FunctionDefinition', text: '}', subtree_size: 6},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
@@ -30,8 +30,8 @@ fn B() {}
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'B'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:     {kind: 'FunctionDefinition', text: '}', subtree_size: 6},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},

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

@@ -34,8 +34,8 @@ fn F() {
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'F'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:         {kind: 'VariableIntroducer', text: 'var'},
 // CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: 'ints'},

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

@@ -17,11 +17,11 @@ fn F(n: i32) -> i32 {
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'F'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: 'n'},
 // CHECK:STDOUT:             {kind: 'IntTypeLiteral', text: 'i32'},
 // CHECK:STDOUT:           {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 5},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 5},
 // CHECK:STDOUT:           {kind: 'IntTypeLiteral', text: 'i32'},
 // CHECK:STDOUT:         {kind: 'ReturnType', text: '->', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 10},

+ 2 - 2
toolchain/parse/testdata/choice/fail_missing_definition_parameterized.carbon

@@ -19,11 +19,11 @@ choice MissingDefinition(T:! type);
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:         {kind: 'ChoiceIntroducer', text: 'choice'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'MissingDefinition'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: 'T'},
 // CHECK:STDOUT:             {kind: 'TypeTypeLiteral', text: 'type'},
 // CHECK:STDOUT:           {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 3},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 5},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', 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: ''},

+ 2 - 2
toolchain/parse/testdata/choice/local.carbon

@@ -20,8 +20,8 @@ fn F() {
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'F'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:           {kind: 'ChoiceIntroducer', text: 'choice'},
 // CHECK:STDOUT:           {kind: 'IdentifierNameNotBeforeParams', text: 'C'},

+ 4 - 4
toolchain/parse/testdata/choice/parameterized.carbon

@@ -18,18 +18,18 @@ choice OptionalElement(T:! type) {
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:         {kind: 'ChoiceIntroducer', text: 'choice'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'OptionalElement'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: 'T'},
 // CHECK:STDOUT:             {kind: 'TypeTypeLiteral', text: 'type'},
 // CHECK:STDOUT:           {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 3},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 5},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 5},
 // CHECK:STDOUT:       {kind: 'ChoiceDefinitionStart', text: '{', subtree_size: 8},
 // CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'Element'},
-// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:           {kind: 'IdentifierNameNotBeforeParams', text: 'value'},
 // CHECK:STDOUT:           {kind: 'IdentifierNameExpr', text: 'T'},
 // CHECK:STDOUT:         {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
-// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', subtree_size: 5},
+// CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', subtree_size: 5},
 // CHECK:STDOUT:       {kind: 'ChoiceAlternativeListComma', text: ','},
 // CHECK:STDOUT:       {kind: 'IdentifierNameNotBeforeParams', text: 'None'},
 // CHECK:STDOUT:       {kind: 'ChoiceAlternativeListComma', text: ','},

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

@@ -162,8 +162,8 @@ fn F() {
 // CHECK:STDOUT:     {kind: 'ClassDefinition', text: '}', subtree_size: 7},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'F'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:         {kind: 'AdaptIntroducer', text: 'adapt'},
 // CHECK:STDOUT:         {kind: 'IntTypeLiteral', text: 'i32'},

+ 2 - 2
toolchain/parse/testdata/class/base_misplaced.carbon

@@ -32,8 +32,8 @@ fn F() {
 // CHECK:STDOUT:     {kind: 'BaseDecl', text: ';', subtree_size: 4},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'F'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:         {kind: 'BaseIntroducer', text: 'base'},
 // CHECK:STDOUT:         {kind: 'BaseColon', text: ':'},

+ 2 - 2
toolchain/parse/testdata/class/basic.carbon

@@ -20,8 +20,8 @@ class Foo {
 // CHECK:STDOUT:       {kind: 'ClassDefinitionStart', text: '{', subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'Baz'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDecl', text: ';', subtree_size: 5},
 // CHECK:STDOUT:     {kind: 'ClassDefinition', text: '}', subtree_size: 9},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},

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

@@ -24,8 +24,8 @@ class Foo {
 // CHECK:STDOUT:       {kind: 'ClassDefinitionStart', text: '{', subtree_size: 3},
 // CHECK:STDOUT:           {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:           {kind: 'IdentifierNameBeforeParams', text: 'Make'},
-// CHECK:STDOUT:             {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:           {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:             {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:           {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:             {kind: 'SelfTypeNameExpr', text: 'Self'},
 // CHECK:STDOUT:           {kind: 'ReturnType', text: '->', subtree_size: 2},
 // CHECK:STDOUT:         {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 7},
@@ -45,8 +45,8 @@ class Foo {
 // CHECK:STDOUT:               {kind: 'SelfTypeNameExpr', text: 'Self'},
 // CHECK:STDOUT:             {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
 // CHECK:STDOUT:           {kind: 'ImplicitParamList', text: ']', subtree_size: 5},
-// CHECK:STDOUT:             {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:           {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:             {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:           {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:             {kind: 'IntTypeLiteral', text: 'i32'},
 // CHECK:STDOUT:           {kind: 'ReturnType', text: '->', subtree_size: 2},
 // CHECK:STDOUT:         {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 12},
@@ -65,8 +65,8 @@ class Foo {
 // CHECK:STDOUT:               {kind: 'LetBindingPattern', text: ':', subtree_size: 4},
 // CHECK:STDOUT:             {kind: 'Addr', text: 'addr', subtree_size: 5},
 // CHECK:STDOUT:           {kind: 'ImplicitParamList', text: ']', subtree_size: 7},
-// CHECK:STDOUT:             {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:           {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:             {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:           {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:         {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 12},
 // CHECK:STDOUT:               {kind: 'SelfValueNameExpr', text: 'self'},
 // CHECK:STDOUT:               {kind: 'IdentifierNameNotBeforeParams', text: 'x'},

+ 8 - 8
toolchain/parse/testdata/class/local.carbon

@@ -21,30 +21,30 @@ fn F() {
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'F'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:           {kind: 'ClassIntroducer', text: 'class'},
 // CHECK:STDOUT:           {kind: 'IdentifierNameNotBeforeParams', text: 'C'},
 // CHECK:STDOUT:         {kind: 'ClassDefinitionStart', text: '{', subtree_size: 3},
 // CHECK:STDOUT:             {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:             {kind: 'IdentifierNameBeforeParams', text: 'G'},
-// CHECK:STDOUT:               {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:             {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:               {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:             {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:           {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:         {kind: 'FunctionDefinition', text: '}', subtree_size: 6},
 // CHECK:STDOUT:           {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:           {kind: 'IdentifierNameBeforeParams', text: 'H'},
-// CHECK:STDOUT:             {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:           {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:             {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:           {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:         {kind: 'FunctionDecl', text: ';', subtree_size: 5},
 // CHECK:STDOUT:       {kind: 'ClassDefinition', text: '}', subtree_size: 15},
 // CHECK:STDOUT:           {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: 'C'},
 // CHECK:STDOUT:           {kind: 'NameQualifierWithoutParams', text: '.', subtree_size: 2},
 // CHECK:STDOUT:           {kind: 'IdentifierNameBeforeParams', text: 'H'},
-// CHECK:STDOUT:             {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:           {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:             {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:           {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:         {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 7},
 // CHECK:STDOUT:       {kind: 'FunctionDefinition', text: '}', subtree_size: 8},
 // CHECK:STDOUT:     {kind: 'FunctionDefinition', text: '}', subtree_size: 29},

+ 2 - 2
toolchain/parse/testdata/class/mismatched_introducer.carbon

@@ -23,8 +23,8 @@ abstract base class C;
 // CHECK:STDOUT:       {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:       {kind: 'BaseModifier', text: 'base'},
 // CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'F'},
-// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:     {kind: 'FunctionDecl', text: ';', subtree_size: 6},
 // CHECK:STDOUT:       {kind: 'ClassIntroducer', text: 'class'},
 // CHECK:STDOUT:       {kind: 'AbstractModifier', text: 'abstract'},

+ 2 - 2
toolchain/parse/testdata/for/fail_colon_instead_of_in.carbon

@@ -23,8 +23,8 @@ fn foo() {
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'foo'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:           {kind: 'ForHeaderStart', text: '('},
 // CHECK:STDOUT:             {kind: 'VariableIntroducer', text: 'var'},

+ 2 - 2
toolchain/parse/testdata/for/fail_missing_cond.carbon

@@ -34,8 +34,8 @@ fn F() {
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'F'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:           {kind: 'ForHeaderStart', text: 'for', has_error: yes},
 // CHECK:STDOUT:             {kind: 'StructLiteralStart', text: '{'},

+ 2 - 2
toolchain/parse/testdata/for/fail_missing_in.carbon

@@ -23,8 +23,8 @@ fn foo() {
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'foo'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:           {kind: 'ForHeaderStart', text: '('},
 // CHECK:STDOUT:             {kind: 'VariableIntroducer', text: 'var'},

+ 2 - 2
toolchain/parse/testdata/for/fail_missing_var.carbon

@@ -23,8 +23,8 @@ fn foo() {
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'foo'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:           {kind: 'ForHeaderStart', text: '('},
 // CHECK:STDOUT:           {kind: 'IdentifierNameExpr', text: 'y'},

+ 2 - 2
toolchain/parse/testdata/for/fail_returned_var.carbon

@@ -24,8 +24,8 @@ fn foo() -> i32 {
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'foo'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:           {kind: 'IntTypeLiteral', text: 'i32'},
 // CHECK:STDOUT:         {kind: 'ReturnType', text: '->', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 7},

+ 2 - 2
toolchain/parse/testdata/for/fail_square_brackets.carbon

@@ -34,8 +34,8 @@ fn F() {
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'F'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:           {kind: 'ForHeaderStart', text: 'for', has_error: yes},
 // CHECK:STDOUT:               {kind: 'ArrayExprStart', text: '['},

+ 2 - 2
toolchain/parse/testdata/for/nested.carbon

@@ -21,8 +21,8 @@ fn foo() {
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'foo'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:           {kind: 'ForHeaderStart', text: '('},
 // CHECK:STDOUT:             {kind: 'VariableIntroducer', text: 'var'},

+ 2 - 2
toolchain/parse/testdata/for/simple.carbon

@@ -19,8 +19,8 @@ fn foo() {
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'foo'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:           {kind: 'ForHeaderStart', text: '('},
 // CHECK:STDOUT:             {kind: 'VariableIntroducer', text: 'var'},

+ 2 - 2
toolchain/parse/testdata/function/declaration/addr.carbon

@@ -15,13 +15,13 @@ fn foo(addr a: i32*);
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:       {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'foo'},
-// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: 'a'},
 // CHECK:STDOUT:               {kind: 'IntTypeLiteral', text: 'i32'},
 // CHECK:STDOUT:             {kind: 'PostfixOperatorStar', text: '*', subtree_size: 2},
 // CHECK:STDOUT:           {kind: 'LetBindingPattern', text: ':', subtree_size: 4},
 // CHECK:STDOUT:         {kind: 'Addr', text: 'addr', subtree_size: 5},
-// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', subtree_size: 7},
+// CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', subtree_size: 7},
 // CHECK:STDOUT:     {kind: 'FunctionDecl', text: ';', subtree_size: 10},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
 // CHECK:STDOUT:   ]

+ 2 - 2
toolchain/parse/testdata/function/declaration/basic.carbon

@@ -15,8 +15,8 @@ fn F();
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:       {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'F'},
-// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:     {kind: 'FunctionDecl', text: ';', subtree_size: 5},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
 // CHECK:STDOUT:   ]

+ 8 - 8
toolchain/parse/testdata/function/declaration/extern.carbon

@@ -46,8 +46,8 @@ extern library foo fn G();
 // CHECK:STDOUT:       {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:       {kind: 'ExternModifier', text: 'extern'},
 // CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'G'},
-// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:     {kind: 'FunctionDecl', text: ';', subtree_size: 6},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
 // CHECK:STDOUT:   ]
@@ -59,8 +59,8 @@ extern library foo fn G();
 // CHECK:STDOUT:         {kind: 'LibrarySpecifier', text: 'library', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'ExternModifierWithLibrary', text: 'extern', subtree_size: 3},
 // CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'G'},
-// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:     {kind: 'FunctionDecl', text: ';', subtree_size: 8},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
 // CHECK:STDOUT:   ]
@@ -72,8 +72,8 @@ extern library foo fn G();
 // CHECK:STDOUT:         {kind: 'LibrarySpecifier', text: 'library', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'ExternModifierWithLibrary', text: 'extern', subtree_size: 3},
 // CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'G'},
-// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:     {kind: 'FunctionDecl', text: ';', subtree_size: 8},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
 // CHECK:STDOUT:   ]
@@ -85,8 +85,8 @@ extern library foo fn G();
 // CHECK:STDOUT:         {kind: 'LibrarySpecifier', text: 'library', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'ExternModifierWithLibrary', text: 'extern', subtree_size: 3},
 // CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'G'},
-// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:     {kind: 'FunctionDecl', text: ';', subtree_size: 8},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
 // CHECK:STDOUT:   ]

+ 2 - 2
toolchain/parse/testdata/function/declaration/fail_skip_indented_newline_until_outdent.carbon

@@ -25,8 +25,8 @@ fn F();
 // CHECK:STDOUT:     {kind: 'FunctionDecl', text: ')', has_error: yes, subtree_size: 3},
 // CHECK:STDOUT:       {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'F'},
-// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:     {kind: 'FunctionDecl', text: ';', subtree_size: 5},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
 // CHECK:STDOUT:   ]

+ 2 - 2
toolchain/parse/testdata/function/declaration/fail_skip_indented_newline_with_semi.carbon

@@ -25,8 +25,8 @@ fn F();
 // CHECK:STDOUT:     {kind: 'FunctionDecl', text: ';', has_error: yes, subtree_size: 3},
 // CHECK:STDOUT:       {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'F'},
-// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:     {kind: 'FunctionDecl', text: ';', subtree_size: 5},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
 // CHECK:STDOUT:   ]

+ 2 - 2
toolchain/parse/testdata/function/declaration/fail_skip_indented_newline_without_semi.carbon

@@ -25,8 +25,8 @@ fn F();
 // CHECK:STDOUT:     {kind: 'FunctionDecl', text: ')', has_error: yes, subtree_size: 3},
 // CHECK:STDOUT:       {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'F'},
-// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:     {kind: 'FunctionDecl', text: ';', subtree_size: 5},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
 // CHECK:STDOUT:   ]

+ 2 - 2
toolchain/parse/testdata/function/declaration/fail_skip_to_newline_without_semi.carbon

@@ -23,8 +23,8 @@ fn F();
 // CHECK:STDOUT:     {kind: 'FunctionDecl', text: ')', has_error: yes, subtree_size: 3},
 // CHECK:STDOUT:       {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'F'},
-// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:     {kind: 'FunctionDecl', text: ';', subtree_size: 5},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
 // CHECK:STDOUT:   ]

+ 2 - 2
toolchain/parse/testdata/function/declaration/fail_skip_without_semi_to_curly.carbon

@@ -22,8 +22,8 @@ fn F();
 // CHECK:STDOUT:     {kind: 'InvalidParseSubtree', text: '}', has_error: yes, subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'F'},
-// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:     {kind: 'FunctionDecl', text: ';', subtree_size: 5},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
 // CHECK:STDOUT:   ]

+ 2 - 2
toolchain/parse/testdata/function/declaration/fail_with_identifier_as_param.carbon

@@ -19,11 +19,11 @@ fn foo(bar);
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:       {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'foo'},
-// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:           {kind: 'IdentifierNameNotBeforeParams', text: 'bar'},
 // CHECK:STDOUT:           {kind: 'InvalidParse', text: ')', has_error: yes},
 // CHECK:STDOUT:         {kind: 'LetBindingPattern', text: 'bar', has_error: yes, subtree_size: 3},
-// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', has_error: yes, subtree_size: 5},
+// CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', has_error: yes, subtree_size: 5},
 // CHECK:STDOUT:     {kind: 'FunctionDecl', text: ';', subtree_size: 8},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
 // CHECK:STDOUT:   ]

+ 8 - 8
toolchain/parse/testdata/function/declaration/impl_fn.carbon

@@ -19,30 +19,30 @@ private impl default fn I();
 // CHECK:STDOUT:       {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:       {kind: 'ImplModifier', text: 'impl'},
 // CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'F'},
-// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:     {kind: 'FunctionDecl', text: ';', subtree_size: 6},
 // CHECK:STDOUT:       {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:       {kind: 'AbstractModifier', text: 'abstract'},
 // CHECK:STDOUT:       {kind: 'ImplModifier', text: 'impl'},
 // CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'G'},
-// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:     {kind: 'FunctionDecl', text: ';', subtree_size: 7},
 // CHECK:STDOUT:       {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:       {kind: 'ImplModifier', text: 'impl'},
 // CHECK:STDOUT:       {kind: 'AbstractModifier', text: 'abstract'},
 // CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'H'},
-// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:     {kind: 'FunctionDecl', text: ';', subtree_size: 7},
 // CHECK:STDOUT:       {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:       {kind: 'PrivateModifier', text: 'private'},
 // CHECK:STDOUT:       {kind: 'ImplModifier', text: 'impl'},
 // CHECK:STDOUT:       {kind: 'DefaultModifier', text: 'default'},
 // CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'I'},
-// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:     {kind: 'FunctionDecl', text: ';', subtree_size: 8},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
 // CHECK:STDOUT:   ]

+ 2 - 2
toolchain/parse/testdata/function/declaration/implicit_empty.carbon

@@ -17,8 +17,8 @@ fn foo[]();
 // CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'foo'},
 // CHECK:STDOUT:         {kind: 'ImplicitParamListStart', text: '['},
 // CHECK:STDOUT:       {kind: 'ImplicitParamList', text: ']', subtree_size: 2},
-// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:     {kind: 'FunctionDecl', text: ';', subtree_size: 7},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
 // CHECK:STDOUT:   ]

+ 2 - 2
toolchain/parse/testdata/function/declaration/implicit_params.carbon

@@ -24,8 +24,8 @@ fn foo[a: i32, b: i32]();
 // CHECK:STDOUT:           {kind: 'IntTypeLiteral', text: 'i32'},
 // CHECK:STDOUT:         {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
 // CHECK:STDOUT:       {kind: 'ImplicitParamList', text: ']', subtree_size: 9},
-// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:     {kind: 'FunctionDecl', text: ';', subtree_size: 14},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
 // CHECK:STDOUT:   ]

+ 2 - 2
toolchain/parse/testdata/function/declaration/params.carbon

@@ -15,7 +15,7 @@ fn foo(a: i32, b: i32);
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:       {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'foo'},
-// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:           {kind: 'IdentifierNameNotBeforeParams', text: 'a'},
 // CHECK:STDOUT:           {kind: 'IntTypeLiteral', text: 'i32'},
 // CHECK:STDOUT:         {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
@@ -23,7 +23,7 @@ fn foo(a: i32, b: i32);
 // CHECK:STDOUT:           {kind: 'IdentifierNameNotBeforeParams', text: 'b'},
 // CHECK:STDOUT:           {kind: 'IntTypeLiteral', text: 'i32'},
 // CHECK:STDOUT:         {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
-// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', subtree_size: 9},
+// CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', subtree_size: 9},
 // CHECK:STDOUT:     {kind: 'FunctionDecl', text: ';', subtree_size: 12},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
 // CHECK:STDOUT:   ]

+ 2 - 2
toolchain/parse/testdata/function/declaration/with_return_type.carbon

@@ -15,8 +15,8 @@ fn foo() -> u32;
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:       {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'foo'},
-// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:         {kind: 'UnsignedIntTypeLiteral', text: 'u32'},
 // CHECK:STDOUT:       {kind: 'ReturnType', text: '->', subtree_size: 2},
 // CHECK:STDOUT:     {kind: 'FunctionDecl', text: ';', subtree_size: 7},

+ 2 - 2
toolchain/parse/testdata/function/definition/basic.carbon

@@ -15,8 +15,8 @@ fn F() {}
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'F'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:     {kind: 'FunctionDefinition', text: '}', subtree_size: 6},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},

+ 4 - 4
toolchain/parse/testdata/function/definition/builtin.carbon

@@ -19,8 +19,8 @@ impl T as I {
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'F'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'BuiltinFunctionDefinitionStart', text: '=', subtree_size: 5},
 // CHECK:STDOUT:       {kind: 'BuiltinName', text: '"builtin"'},
 // CHECK:STDOUT:     {kind: 'BuiltinFunctionDefinition', text: ';', subtree_size: 7},
@@ -31,8 +31,8 @@ impl T as I {
 // CHECK:STDOUT:       {kind: 'ImplDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:           {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:           {kind: 'IdentifierNameBeforeParams', text: 'Op'},
-// CHECK:STDOUT:             {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:           {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:             {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:           {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:         {kind: 'BuiltinFunctionDefinitionStart', text: '=', subtree_size: 5},
 // CHECK:STDOUT:         {kind: 'BuiltinName', text: '"builtin"'},
 // CHECK:STDOUT:       {kind: 'BuiltinFunctionDefinition', text: ';', subtree_size: 7},

+ 12 - 12
toolchain/parse/testdata/function/definition/decl_statement.carbon

@@ -47,8 +47,8 @@ fn F() {
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'F'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:         {kind: 'AliasIntroducer', text: 'alias'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameNotBeforeParams', text: 'A'},
@@ -71,8 +71,8 @@ fn F() {
 // CHECK:STDOUT:       {kind: 'ClassDefinition', text: '}', subtree_size: 5},
 // CHECK:STDOUT:           {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:           {kind: 'IdentifierNameBeforeParams', text: 'F'},
-// CHECK:STDOUT:             {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:           {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:             {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:           {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:         {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:       {kind: 'FunctionDefinition', text: '}', subtree_size: 6},
 // CHECK:STDOUT:           {kind: 'NamedConstraintIntroducer', text: 'constraint'},
@@ -121,8 +121,8 @@ fn F() {
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'F'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:         {kind: 'AdaptIntroducer', text: 'adapt'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameExpr', text: 'A'},
@@ -147,20 +147,20 @@ fn F() {
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'DefaultModifier', text: 'default'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'F'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDecl', text: ';', subtree_size: 6},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'ImplModifier', text: 'impl'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'G'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDecl', text: ';', subtree_size: 6},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'VirtualModifier', text: 'virtual'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'H'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDecl', text: ';', subtree_size: 6},
 // CHECK:STDOUT:         {kind: 'VariableIntroducer', text: 'var'},
 // CHECK:STDOUT:         {kind: 'PrivateModifier', text: 'private'},

+ 4 - 4
toolchain/parse/testdata/function/definition/extern.carbon

@@ -22,8 +22,8 @@ extern library "foo" fn G() {}
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'ExternModifier', text: 'extern'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'G'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 6},
 // CHECK:STDOUT:     {kind: 'FunctionDefinition', text: '}', subtree_size: 7},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
@@ -36,8 +36,8 @@ extern library "foo" fn G() {}
 // CHECK:STDOUT:           {kind: 'LibrarySpecifier', text: 'library', subtree_size: 2},
 // CHECK:STDOUT:         {kind: 'ExternModifierWithLibrary', text: 'extern', subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'G'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 8},
 // CHECK:STDOUT:     {kind: 'FunctionDefinition', text: '}', subtree_size: 9},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},

+ 8 - 8
toolchain/parse/testdata/function/definition/fail_builtin.carbon

@@ -31,27 +31,27 @@ fn TestRecoveryFromSpuriousEquals();
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'NotString'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'BuiltinFunctionDefinitionStart', text: '=', subtree_size: 5},
 // CHECK:STDOUT:     {kind: 'BuiltinFunctionDefinition', text: ';', has_error: yes, subtree_size: 6},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'JunkAfterString'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'BuiltinFunctionDefinitionStart', text: '=', subtree_size: 5},
 // CHECK:STDOUT:       {kind: 'BuiltinName', text: '"hello"'},
 // CHECK:STDOUT:     {kind: 'BuiltinFunctionDefinition', text: ';', has_error: yes, subtree_size: 7},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'SpuriousEquals'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'BuiltinFunctionDefinitionStart', text: '=', subtree_size: 5},
 // CHECK:STDOUT:     {kind: 'BuiltinFunctionDefinition', text: '}', has_error: yes, subtree_size: 6},
 // CHECK:STDOUT:       {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'TestRecoveryFromSpuriousEquals'},
-// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:     {kind: 'FunctionDecl', text: ';', subtree_size: 5},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
 // CHECK:STDOUT:   ]

+ 2 - 2
toolchain/parse/testdata/function/definition/fail_identifier_in_statements.carbon

@@ -23,8 +23,8 @@ fn F() {
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'F'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:         {kind: 'IdentifierNameExpr', text: 'bar'},
 // CHECK:STDOUT:       {kind: 'ExprStatement', text: 'bar', has_error: yes, subtree_size: 2},

+ 4 - 4
toolchain/parse/testdata/function/definition/nested.carbon

@@ -18,16 +18,16 @@ fn F() {
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'F'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:           {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:           {kind: 'IdentifierNameBeforeParams', text: 'NamedParams'},
-// CHECK:STDOUT:             {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:             {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:               {kind: 'IdentifierNameNotBeforeParams', text: 'a'},
 // CHECK:STDOUT:               {kind: 'IntTypeLiteral', text: 'i32'},
 // CHECK:STDOUT:             {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
-// CHECK:STDOUT:           {kind: 'TuplePattern', text: ')', subtree_size: 5},
+// CHECK:STDOUT:           {kind: 'ExplicitParamList', text: ')', subtree_size: 5},
 // CHECK:STDOUT:         {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 8},
 // CHECK:STDOUT:       {kind: 'FunctionDefinition', text: '}', subtree_size: 9},
 // CHECK:STDOUT:           {kind: 'FunctionIntroducer', text: 'fn'},

+ 2 - 2
toolchain/parse/testdata/function/definition/with_params.carbon

@@ -17,7 +17,7 @@ fn foo(bar: i64, baz: i64) {
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'foo'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: 'bar'},
 // CHECK:STDOUT:             {kind: 'IntTypeLiteral', text: 'i64'},
 // CHECK:STDOUT:           {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
@@ -25,7 +25,7 @@ fn foo(bar: i64, baz: i64) {
 // CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: 'baz'},
 // CHECK:STDOUT:             {kind: 'IntTypeLiteral', text: 'i64'},
 // CHECK:STDOUT:           {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 9},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 9},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 12},
 // CHECK:STDOUT:             {kind: 'IdentifierNameExpr', text: 'foo'},
 // CHECK:STDOUT:           {kind: 'CallExprStart', text: '(', subtree_size: 2},

+ 2 - 2
toolchain/parse/testdata/function/definition/with_return_type.carbon

@@ -17,8 +17,8 @@ fn foo() -> f64 {
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'foo'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:           {kind: 'FloatTypeLiteral', text: 'f64'},
 // CHECK:STDOUT:         {kind: 'ReturnType', text: '->', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 7},

+ 4 - 4
toolchain/parse/testdata/generics/deduced_params/empty.carbon

@@ -19,15 +19,15 @@ interface Bar[]() {}
 // CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'Foo'},
 // CHECK:STDOUT:         {kind: 'ImplicitParamListStart', text: '['},
 // CHECK:STDOUT:       {kind: 'ImplicitParamList', text: ']', subtree_size: 2},
-// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:     {kind: 'ClassDecl', text: ';', subtree_size: 7},
 // CHECK:STDOUT:         {kind: 'InterfaceIntroducer', text: 'interface'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'Bar'},
 // CHECK:STDOUT:           {kind: 'ImplicitParamListStart', text: '['},
 // CHECK:STDOUT:         {kind: 'ImplicitParamList', text: ']', subtree_size: 2},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'InterfaceDefinitionStart', text: '{', subtree_size: 7},
 // CHECK:STDOUT:     {kind: 'InterfaceDefinition', text: '}', subtree_size: 8},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},

+ 4 - 4
toolchain/parse/testdata/generics/deduced_params/one.carbon

@@ -22,8 +22,8 @@ interface Bar[a: i32]() {}
 // CHECK:STDOUT:           {kind: 'IntTypeLiteral', text: 'i32'},
 // CHECK:STDOUT:         {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
 // CHECK:STDOUT:       {kind: 'ImplicitParamList', text: ']', subtree_size: 5},
-// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:     {kind: 'ClassDecl', text: ';', subtree_size: 10},
 // CHECK:STDOUT:         {kind: 'InterfaceIntroducer', text: 'interface'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'Bar'},
@@ -32,8 +32,8 @@ interface Bar[a: i32]() {}
 // CHECK:STDOUT:             {kind: 'IntTypeLiteral', text: 'i32'},
 // CHECK:STDOUT:           {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'ImplicitParamList', text: ']', subtree_size: 5},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'InterfaceDefinitionStart', text: '{', subtree_size: 10},
 // CHECK:STDOUT:     {kind: 'InterfaceDefinition', text: '}', subtree_size: 11},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},

+ 4 - 4
toolchain/parse/testdata/generics/deduced_params/one_suffix_comma.carbon

@@ -23,8 +23,8 @@ interface Bar[a: i32,]() {}
 // CHECK:STDOUT:         {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'PatternListComma', text: ','},
 // CHECK:STDOUT:       {kind: 'ImplicitParamList', text: ']', subtree_size: 6},
-// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:     {kind: 'ClassDecl', text: ';', subtree_size: 11},
 // CHECK:STDOUT:         {kind: 'InterfaceIntroducer', text: 'interface'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'Bar'},
@@ -34,8 +34,8 @@ interface Bar[a: i32,]() {}
 // CHECK:STDOUT:           {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
 // CHECK:STDOUT:           {kind: 'PatternListComma', text: ','},
 // CHECK:STDOUT:         {kind: 'ImplicitParamList', text: ']', subtree_size: 6},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'InterfaceDefinitionStart', text: '{', subtree_size: 11},
 // CHECK:STDOUT:     {kind: 'InterfaceDefinition', text: '}', subtree_size: 12},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},

+ 4 - 4
toolchain/parse/testdata/generics/deduced_params/six.carbon

@@ -42,8 +42,8 @@ interface Bar[a: i32, b: i32, c: i32, d: i32, e: i32, f: i32]() {}
 // CHECK:STDOUT:           {kind: 'IntTypeLiteral', text: 'i32'},
 // CHECK:STDOUT:         {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
 // CHECK:STDOUT:       {kind: 'ImplicitParamList', text: ']', subtree_size: 25},
-// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:     {kind: 'ClassDecl', text: ';', subtree_size: 30},
 // CHECK:STDOUT:         {kind: 'InterfaceIntroducer', text: 'interface'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'Bar'},
@@ -72,8 +72,8 @@ interface Bar[a: i32, b: i32, c: i32, d: i32, e: i32, f: i32]() {}
 // CHECK:STDOUT:             {kind: 'IntTypeLiteral', text: 'i32'},
 // CHECK:STDOUT:           {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'ImplicitParamList', text: ']', subtree_size: 25},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'InterfaceDefinitionStart', text: '{', subtree_size: 30},
 // CHECK:STDOUT:     {kind: 'InterfaceDefinition', text: '}', subtree_size: 31},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},

+ 4 - 4
toolchain/parse/testdata/generics/deduced_params/two.carbon

@@ -26,8 +26,8 @@ interface Bar[a: i32, b: i32]() {}
 // CHECK:STDOUT:           {kind: 'IntTypeLiteral', text: 'i32'},
 // CHECK:STDOUT:         {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
 // CHECK:STDOUT:       {kind: 'ImplicitParamList', text: ']', subtree_size: 9},
-// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:     {kind: 'ClassDecl', text: ';', subtree_size: 14},
 // CHECK:STDOUT:         {kind: 'InterfaceIntroducer', text: 'interface'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'Bar'},
@@ -40,8 +40,8 @@ interface Bar[a: i32, b: i32]() {}
 // CHECK:STDOUT:             {kind: 'IntTypeLiteral', text: 'i32'},
 // CHECK:STDOUT:           {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'ImplicitParamList', text: ']', subtree_size: 9},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'InterfaceDefinitionStart', text: '{', subtree_size: 14},
 // CHECK:STDOUT:     {kind: 'InterfaceDefinition', text: '}', subtree_size: 15},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},

+ 4 - 4
toolchain/parse/testdata/generics/deduced_params/two_suffix_comma.carbon

@@ -27,8 +27,8 @@ interface Bar[a: i32, b: i32,]() {}
 // CHECK:STDOUT:         {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'PatternListComma', text: ','},
 // CHECK:STDOUT:       {kind: 'ImplicitParamList', text: ']', subtree_size: 10},
-// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:     {kind: 'ClassDecl', text: ';', subtree_size: 15},
 // CHECK:STDOUT:         {kind: 'InterfaceIntroducer', text: 'interface'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'Bar'},
@@ -42,8 +42,8 @@ interface Bar[a: i32, b: i32,]() {}
 // CHECK:STDOUT:           {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
 // CHECK:STDOUT:           {kind: 'PatternListComma', text: ','},
 // CHECK:STDOUT:         {kind: 'ImplicitParamList', text: ']', subtree_size: 10},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'InterfaceDefinitionStart', text: '{', subtree_size: 15},
 // CHECK:STDOUT:     {kind: 'InterfaceDefinition', text: '}', subtree_size: 16},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},

+ 2 - 2
toolchain/parse/testdata/generics/generic_params/basic.carbon

@@ -15,11 +15,11 @@ fn foo(a:! i32);
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:       {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'foo'},
-// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:           {kind: 'IdentifierNameNotBeforeParams', text: 'a'},
 // CHECK:STDOUT:           {kind: 'IntTypeLiteral', text: 'i32'},
 // CHECK:STDOUT:         {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 3},
-// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', subtree_size: 5},
+// CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', subtree_size: 5},
 // CHECK:STDOUT:     {kind: 'FunctionDecl', text: ';', subtree_size: 8},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
 // CHECK:STDOUT:   ]

+ 10 - 10
toolchain/parse/testdata/generics/generic_params/fail_template_no_param.carbon

@@ -43,46 +43,46 @@ fn E(template:! i32);
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:       {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'A'},
-// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:           {kind: 'IdentifierNameNotBeforeParams', text: 'i32', has_error: yes},
 // CHECK:STDOUT:           {kind: 'InvalidParse', text: 'i32', has_error: yes},
 // CHECK:STDOUT:         {kind: 'LetBindingPattern', text: 'template', has_error: yes, subtree_size: 3},
-// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', has_error: yes, subtree_size: 5},
+// CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', has_error: yes, subtree_size: 5},
 // CHECK:STDOUT:     {kind: 'FunctionDecl', text: ';', subtree_size: 8},
 // CHECK:STDOUT:       {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'B'},
-// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: 'a'},
 // CHECK:STDOUT:           {kind: 'TemplateBindingName', text: 'template', has_error: yes, subtree_size: 2},
 // CHECK:STDOUT:           {kind: 'IntTypeLiteral', text: 'i32'},
 // CHECK:STDOUT:         {kind: 'LetBindingPattern', text: ':', has_error: yes, subtree_size: 4},
-// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', has_error: yes, subtree_size: 6},
+// CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', has_error: yes, subtree_size: 6},
 // CHECK:STDOUT:     {kind: 'FunctionDecl', text: ';', subtree_size: 9},
 // CHECK:STDOUT:       {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'C'},
-// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:           {kind: 'IdentifierNameNotBeforeParams', text: ')', has_error: yes},
 // CHECK:STDOUT:           {kind: 'InvalidParse', text: ')', has_error: yes},
 // CHECK:STDOUT:         {kind: 'LetBindingPattern', text: 'template', has_error: yes, subtree_size: 3},
-// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', has_error: yes, subtree_size: 5},
+// CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', has_error: yes, subtree_size: 5},
 // CHECK:STDOUT:     {kind: 'FunctionDecl', text: ';', subtree_size: 8},
 // CHECK:STDOUT:       {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'D'},
-// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: ':!', has_error: yes},
 // CHECK:STDOUT:           {kind: 'TemplateBindingName', text: 'template', has_error: yes, subtree_size: 2},
 // CHECK:STDOUT:           {kind: 'IntTypeLiteral', text: 'i32'},
 // CHECK:STDOUT:         {kind: 'CompileTimeBindingPattern', text: ':!', has_error: yes, subtree_size: 4},
-// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', has_error: yes, subtree_size: 6},
+// CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', has_error: yes, subtree_size: 6},
 // CHECK:STDOUT:     {kind: 'FunctionDecl', text: ';', subtree_size: 9},
 // CHECK:STDOUT:       {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'E'},
-// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: ':!', has_error: yes},
 // CHECK:STDOUT:           {kind: 'TemplateBindingName', text: 'template', has_error: yes, subtree_size: 2},
 // CHECK:STDOUT:           {kind: 'IntTypeLiteral', text: 'i32'},
 // CHECK:STDOUT:         {kind: 'CompileTimeBindingPattern', text: ':!', has_error: yes, subtree_size: 4},
-// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', has_error: yes, subtree_size: 6},
+// CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', has_error: yes, subtree_size: 6},
 // CHECK:STDOUT:     {kind: 'FunctionDecl', text: ';', subtree_size: 9},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
 // CHECK:STDOUT:   ]

+ 2 - 2
toolchain/parse/testdata/generics/generic_params/template.carbon

@@ -15,12 +15,12 @@ fn foo(template a:! i32);
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:       {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'foo'},
-// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: 'a'},
 // CHECK:STDOUT:           {kind: 'TemplateBindingName', text: 'template', subtree_size: 2},
 // CHECK:STDOUT:           {kind: 'IntTypeLiteral', text: 'i32'},
 // CHECK:STDOUT:         {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 4},
-// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', subtree_size: 6},
+// CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', subtree_size: 6},
 // CHECK:STDOUT:     {kind: 'FunctionDecl', text: ';', subtree_size: 9},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
 // CHECK:STDOUT:   ]

+ 4 - 4
toolchain/parse/testdata/generics/generic_params/template_addr.carbon

@@ -25,11 +25,11 @@ fn bar(addr template b:! i32);
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:       {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'foo'},
-// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:           {kind: 'IdentifierNameNotBeforeParams', text: 'addr', has_error: yes},
 // CHECK:STDOUT:           {kind: 'InvalidParse', text: 'addr', has_error: yes},
 // CHECK:STDOUT:         {kind: 'LetBindingPattern', text: 'template', has_error: yes, subtree_size: 3},
-// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', has_error: yes, subtree_size: 5},
+// CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', has_error: yes, subtree_size: 5},
 // CHECK:STDOUT:     {kind: 'FunctionDecl', text: ';', subtree_size: 8},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
 // CHECK:STDOUT:   ]
@@ -38,13 +38,13 @@ fn bar(addr template b:! i32);
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:       {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'bar'},
-// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:               {kind: 'IdentifierNameNotBeforeParams', text: 'b'},
 // CHECK:STDOUT:             {kind: 'TemplateBindingName', text: 'template', subtree_size: 2},
 // CHECK:STDOUT:             {kind: 'IntTypeLiteral', text: 'i32'},
 // CHECK:STDOUT:           {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 4},
 // CHECK:STDOUT:         {kind: 'Addr', text: 'addr', subtree_size: 5},
-// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', subtree_size: 7},
+// CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', subtree_size: 7},
 // CHECK:STDOUT:     {kind: 'FunctionDecl', text: ';', subtree_size: 10},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
 // CHECK:STDOUT:   ]

+ 2 - 2
toolchain/parse/testdata/generics/impl/basic.carbon

@@ -27,11 +27,11 @@ impl i32 as Interface {
 // CHECK:STDOUT:             {kind: 'SelfTypeNameExpr', text: 'Self'},
 // CHECK:STDOUT:           {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'ImplicitParamList', text: ']', subtree_size: 5},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: 'b'},
 // CHECK:STDOUT:             {kind: 'SelfTypeNameExpr', text: 'Self'},
 // CHECK:STDOUT:           {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 5},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 5},
 // CHECK:STDOUT:           {kind: 'SelfTypeNameExpr', text: 'Self'},
 // CHECK:STDOUT:         {kind: 'ReturnType', text: '->', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDecl', text: ';', subtree_size: 15},

+ 2 - 2
toolchain/parse/testdata/generics/impl/class.carbon

@@ -47,8 +47,8 @@ class C {
 // CHECK:STDOUT:         {kind: 'ImplDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:           {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:           {kind: 'IdentifierNameBeforeParams', text: 'F'},
-// CHECK:STDOUT:             {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:           {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:             {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:           {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:         {kind: 'FunctionDecl', text: ';', subtree_size: 5},
 // CHECK:STDOUT:       {kind: 'ImplDefinition', text: '}', subtree_size: 11},
 // CHECK:STDOUT:         {kind: 'ImplIntroducer', text: 'impl'},

+ 6 - 6
toolchain/parse/testdata/generics/impl/fail_out_of_line_member.carbon

@@ -44,8 +44,8 @@ fn C.(Self as Interface).F() {}
 // CHECK:STDOUT:       {kind: 'InterfaceDefinitionStart', text: '{', subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'F'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDecl', text: ';', subtree_size: 5},
 // CHECK:STDOUT:     {kind: 'InterfaceDefinition', text: '}', subtree_size: 9},
 // CHECK:STDOUT:         {kind: 'ImplIntroducer', text: 'impl'},
@@ -55,8 +55,8 @@ fn C.(Self as Interface).F() {}
 // CHECK:STDOUT:       {kind: 'ImplDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'F'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDecl', text: ';', subtree_size: 5},
 // CHECK:STDOUT:     {kind: 'ImplDefinition', text: '}', subtree_size: 11},
 // CHECK:STDOUT:       {kind: 'FunctionIntroducer', text: 'fn'},
@@ -72,8 +72,8 @@ fn C.(Self as Interface).F() {}
 // CHECK:STDOUT:         {kind: 'ImplDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:           {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:           {kind: 'IdentifierNameBeforeParams', text: 'F'},
-// CHECK:STDOUT:             {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:           {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:             {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:           {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:         {kind: 'FunctionDecl', text: ';', subtree_size: 5},
 // CHECK:STDOUT:       {kind: 'ImplDefinition', text: '}', subtree_size: 11},
 // CHECK:STDOUT:     {kind: 'ClassDefinition', text: '}', subtree_size: 15},

+ 4 - 4
toolchain/parse/testdata/generics/interface/basic.carbon

@@ -26,11 +26,11 @@ interface Foo {
 // CHECK:STDOUT:             {kind: 'SelfTypeNameExpr', text: 'Self'},
 // CHECK:STDOUT:           {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'ImplicitParamList', text: ']', subtree_size: 5},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: 'b'},
 // CHECK:STDOUT:             {kind: 'SelfTypeNameExpr', text: 'Self'},
 // CHECK:STDOUT:           {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 5},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 5},
 // CHECK:STDOUT:           {kind: 'SelfTypeNameExpr', text: 'Self'},
 // CHECK:STDOUT:         {kind: 'ReturnType', text: '->', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDecl', text: ';', subtree_size: 15},
@@ -41,11 +41,11 @@ interface Foo {
 // CHECK:STDOUT:             {kind: 'IdentifierNameExpr', text: 'foo'},
 // CHECK:STDOUT:           {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'ImplicitParamList', text: ']', subtree_size: 5},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: 'b'},
 // CHECK:STDOUT:             {kind: 'SelfTypeNameExpr', text: 'Self'},
 // CHECK:STDOUT:           {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 5},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 5},
 // CHECK:STDOUT:           {kind: 'SelfTypeNameExpr', text: 'Self'},
 // CHECK:STDOUT:         {kind: 'ReturnType', text: '->', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDecl', text: ';', subtree_size: 15},

+ 4 - 4
toolchain/parse/testdata/generics/interface/default_fn.carbon

@@ -30,11 +30,11 @@ interface Foo {
 // CHECK:STDOUT:             {kind: 'SelfTypeNameExpr', text: 'Self'},
 // CHECK:STDOUT:           {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'ImplicitParamList', text: ']', subtree_size: 5},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: 's'},
 // CHECK:STDOUT:             {kind: 'IntTypeLiteral', text: 'i32'},
 // CHECK:STDOUT:           {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 5},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 5},
 // CHECK:STDOUT:           {kind: 'SelfTypeNameExpr', text: 'Self'},
 // CHECK:STDOUT:         {kind: 'ReturnType', text: '->', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDecl', text: ';', subtree_size: 15},
@@ -46,8 +46,8 @@ interface Foo {
 // CHECK:STDOUT:               {kind: 'SelfTypeNameExpr', text: 'Self'},
 // CHECK:STDOUT:             {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
 // CHECK:STDOUT:           {kind: 'ImplicitParamList', text: ']', subtree_size: 5},
-// CHECK:STDOUT:             {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:           {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:             {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:           {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:             {kind: 'SelfTypeNameExpr', text: 'Self'},
 // CHECK:STDOUT:           {kind: 'ReturnType', text: '->', subtree_size: 2},
 // CHECK:STDOUT:         {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 13},

+ 4 - 4
toolchain/parse/testdata/generics/interface/fail_self_param_syntax.carbon

@@ -35,11 +35,11 @@ interface Foo {
 // CHECK:STDOUT:             {kind: 'InvalidParse', text: 'Self', has_error: yes},
 // CHECK:STDOUT:           {kind: 'LetBindingPattern', text: 'me', has_error: yes, subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'ImplicitParamList', text: ']', has_error: yes, subtree_size: 5},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: 'b'},
 // CHECK:STDOUT:             {kind: 'SelfTypeNameExpr', text: 'Self'},
 // CHECK:STDOUT:           {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 5},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 5},
 // CHECK:STDOUT:           {kind: 'SelfTypeNameExpr', text: 'Self'},
 // CHECK:STDOUT:         {kind: 'ReturnType', text: '->', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDecl', text: ';', subtree_size: 15},
@@ -50,11 +50,11 @@ interface Foo {
 // CHECK:STDOUT:             {kind: 'InvalidParse', text: 'Self', has_error: yes},
 // CHECK:STDOUT:           {kind: 'LetBindingPattern', text: 'Self', has_error: yes, subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'ImplicitParamList', text: ']', has_error: yes, subtree_size: 5},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: 'b'},
 // CHECK:STDOUT:             {kind: 'SelfTypeNameExpr', text: 'Self'},
 // CHECK:STDOUT:           {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 5},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 5},
 // CHECK:STDOUT:           {kind: 'SelfTypeNameExpr', text: 'Self'},
 // CHECK:STDOUT:         {kind: 'ReturnType', text: '->', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDecl', text: ';', subtree_size: 15},

+ 4 - 4
toolchain/parse/testdata/generics/interface/final_fn.carbon

@@ -28,11 +28,11 @@ interface Order {
 // CHECK:STDOUT:             {kind: 'SelfTypeNameExpr', text: 'Self'},
 // CHECK:STDOUT:           {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'ImplicitParamList', text: ']', subtree_size: 5},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: 'right'},
 // CHECK:STDOUT:             {kind: 'SelfTypeNameExpr', text: 'Self'},
 // CHECK:STDOUT:           {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 5},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 5},
 // CHECK:STDOUT:           {kind: 'BoolTypeLiteral', text: 'bool'},
 // CHECK:STDOUT:         {kind: 'ReturnType', text: '->', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDecl', text: ';', subtree_size: 15},
@@ -44,11 +44,11 @@ interface Order {
 // CHECK:STDOUT:               {kind: 'SelfTypeNameExpr', text: 'Self'},
 // CHECK:STDOUT:             {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
 // CHECK:STDOUT:           {kind: 'ImplicitParamList', text: ']', subtree_size: 5},
-// CHECK:STDOUT:             {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:             {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:               {kind: 'IdentifierNameNotBeforeParams', text: 'right'},
 // CHECK:STDOUT:               {kind: 'SelfTypeNameExpr', text: 'Self'},
 // CHECK:STDOUT:             {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
-// CHECK:STDOUT:           {kind: 'TuplePattern', text: ')', subtree_size: 5},
+// CHECK:STDOUT:           {kind: 'ExplicitParamList', text: ')', subtree_size: 5},
 // CHECK:STDOUT:             {kind: 'BoolTypeLiteral', text: 'bool'},
 // CHECK:STDOUT:           {kind: 'ReturnType', text: '->', subtree_size: 2},
 // CHECK:STDOUT:         {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 16},

+ 2 - 2
toolchain/parse/testdata/generics/interface/final_member_definition.carbon

@@ -28,11 +28,11 @@ interface Foo {
 // CHECK:STDOUT:               {kind: 'SelfTypeNameExpr', text: 'Self'},
 // CHECK:STDOUT:             {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
 // CHECK:STDOUT:           {kind: 'ImplicitParamList', text: ']', subtree_size: 5},
-// CHECK:STDOUT:             {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:             {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:               {kind: 'IdentifierNameNotBeforeParams', text: 'b'},
 // CHECK:STDOUT:               {kind: 'SelfTypeNameExpr', text: 'Self'},
 // CHECK:STDOUT:             {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
-// CHECK:STDOUT:           {kind: 'TuplePattern', text: ')', subtree_size: 5},
+// CHECK:STDOUT:           {kind: 'ExplicitParamList', text: ')', subtree_size: 5},
 // CHECK:STDOUT:             {kind: 'SelfTypeNameExpr', text: 'Self'},
 // CHECK:STDOUT:           {kind: 'ReturnType', text: '->', subtree_size: 2},
 // CHECK:STDOUT:         {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 16},

+ 2 - 2
toolchain/parse/testdata/generics/interface/non_instance_fn.carbon

@@ -20,8 +20,8 @@ interface Foo {
 // CHECK:STDOUT:       {kind: 'InterfaceDefinitionStart', text: '{', subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'FooFactory'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:           {kind: 'SelfTypeNameExpr', text: 'Self'},
 // CHECK:STDOUT:         {kind: 'ReturnType', text: '->', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDecl', text: ';', subtree_size: 7},

+ 4 - 4
toolchain/parse/testdata/generics/interface/self_pointer.carbon

@@ -28,11 +28,11 @@ interface Foo {
 // CHECK:STDOUT:             {kind: 'LetBindingPattern', text: ':', subtree_size: 4},
 // CHECK:STDOUT:           {kind: 'Addr', text: 'addr', subtree_size: 5},
 // CHECK:STDOUT:         {kind: 'ImplicitParamList', text: ']', subtree_size: 7},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: 'b'},
 // CHECK:STDOUT:             {kind: 'SelfTypeNameExpr', text: 'Self'},
 // CHECK:STDOUT:           {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 5},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 5},
 // CHECK:STDOUT:           {kind: 'SelfTypeNameExpr', text: 'Self'},
 // CHECK:STDOUT:         {kind: 'ReturnType', text: '->', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDecl', text: ';', subtree_size: 17},
@@ -45,11 +45,11 @@ interface Foo {
 // CHECK:STDOUT:             {kind: 'LetBindingPattern', text: ':', subtree_size: 4},
 // CHECK:STDOUT:           {kind: 'Addr', text: 'addr', subtree_size: 5},
 // CHECK:STDOUT:         {kind: 'ImplicitParamList', text: ']', subtree_size: 7},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: 'b'},
 // CHECK:STDOUT:             {kind: 'SelfTypeNameExpr', text: 'Self'},
 // CHECK:STDOUT:           {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 5},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 5},
 // CHECK:STDOUT:           {kind: 'SelfTypeNameExpr', text: 'Self'},
 // CHECK:STDOUT:         {kind: 'ReturnType', text: '->', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDecl', text: ';', subtree_size: 17},

+ 2 - 2
toolchain/parse/testdata/generics/named_constraint/basic.carbon

@@ -22,8 +22,8 @@ constraint ForwardDeclared;
 // CHECK:STDOUT:       {kind: 'NamedConstraintDefinitionStart', text: '{', subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'Baz'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDecl', text: ';', subtree_size: 5},
 // CHECK:STDOUT:     {kind: 'NamedConstraintDefinition', text: '}', subtree_size: 9},
 // CHECK:STDOUT:       {kind: 'NamedConstraintIntroducer', text: 'constraint'},

+ 2 - 2
toolchain/parse/testdata/generics/named_constraint/defined_method.carbon

@@ -25,11 +25,11 @@ constraint Foo {
 // CHECK:STDOUT:               {kind: 'SelfTypeNameExpr', text: 'Self'},
 // CHECK:STDOUT:             {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
 // CHECK:STDOUT:           {kind: 'ImplicitParamList', text: ']', subtree_size: 5},
-// CHECK:STDOUT:             {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:             {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:               {kind: 'IdentifierNameNotBeforeParams', text: 'b'},
 // CHECK:STDOUT:               {kind: 'SelfTypeNameExpr', text: 'Self'},
 // CHECK:STDOUT:             {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
-// CHECK:STDOUT:           {kind: 'TuplePattern', text: ')', subtree_size: 5},
+// CHECK:STDOUT:           {kind: 'ExplicitParamList', text: ')', subtree_size: 5},
 // CHECK:STDOUT:             {kind: 'SelfTypeNameExpr', text: 'Self'},
 // CHECK:STDOUT:           {kind: 'ReturnType', text: '->', subtree_size: 2},
 // CHECK:STDOUT:         {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 15},

+ 4 - 4
toolchain/parse/testdata/generics/params/empty.carbon

@@ -17,13 +17,13 @@ interface Bar() {}
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:       {kind: 'ClassIntroducer', text: 'class'},
 // CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'Foo'},
-// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:     {kind: 'ClassDecl', text: ';', subtree_size: 5},
 // CHECK:STDOUT:         {kind: 'InterfaceIntroducer', text: 'interface'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'Bar'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'InterfaceDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:     {kind: 'InterfaceDefinition', text: '}', subtree_size: 6},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},

+ 32 - 32
toolchain/parse/testdata/generics/params/name_qualifier.carbon

@@ -35,29 +35,29 @@ fn OuterGeneric(T:! type).InnerGeneric(U:! type).F(x: T, y: U) {}
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:         {kind: 'ClassIntroducer', text: 'class'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'GenericClass1'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: 'T'},
 // CHECK:STDOUT:             {kind: 'TypeTypeLiteral', text: 'type'},
 // CHECK:STDOUT:           {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 3},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 5},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 5},
 // CHECK:STDOUT:       {kind: 'ClassDefinitionStart', text: '{', subtree_size: 8},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'F'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDecl', text: ';', subtree_size: 5},
 // CHECK:STDOUT:     {kind: 'ClassDefinition', text: '}', subtree_size: 14},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:           {kind: 'IdentifierNameBeforeParams', text: 'GenericClass1'},
-// CHECK:STDOUT:             {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:             {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:               {kind: 'IdentifierNameNotBeforeParams', text: 'T'},
 // CHECK:STDOUT:               {kind: 'TypeTypeLiteral', text: 'type'},
 // CHECK:STDOUT:             {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 3},
-// CHECK:STDOUT:           {kind: 'TuplePattern', text: ')', subtree_size: 5},
+// CHECK:STDOUT:           {kind: 'ExplicitParamList', text: ')', subtree_size: 5},
 // CHECK:STDOUT:         {kind: 'NameQualifierWithParams', text: '.', subtree_size: 7},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'F'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 12},
 // CHECK:STDOUT:     {kind: 'FunctionDefinition', text: '}', subtree_size: 13},
 // CHECK:STDOUT:         {kind: 'ClassIntroducer', text: 'class'},
@@ -67,16 +67,16 @@ fn OuterGeneric(T:! type).InnerGeneric(U:! type).F(x: T, y: U) {}
 // CHECK:STDOUT:             {kind: 'TypeTypeLiteral', text: 'type'},
 // CHECK:STDOUT:           {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'ImplicitParamList', text: ']', subtree_size: 5},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: 'X'},
 // CHECK:STDOUT:             {kind: 'IdentifierNameExpr', text: 'T'},
 // CHECK:STDOUT:           {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 3},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 5},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 5},
 // CHECK:STDOUT:       {kind: 'ClassDefinitionStart', text: '{', subtree_size: 13},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'F'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDecl', text: ';', subtree_size: 5},
 // CHECK:STDOUT:     {kind: 'ClassDefinition', text: '}', subtree_size: 19},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
@@ -86,52 +86,52 @@ fn OuterGeneric(T:! type).InnerGeneric(U:! type).F(x: T, y: U) {}
 // CHECK:STDOUT:               {kind: 'TypeTypeLiteral', text: 'type'},
 // CHECK:STDOUT:             {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 3},
 // CHECK:STDOUT:           {kind: 'ImplicitParamList', text: ']', subtree_size: 5},
-// CHECK:STDOUT:             {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:             {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:               {kind: 'IdentifierNameNotBeforeParams', text: 'X'},
 // CHECK:STDOUT:               {kind: 'IdentifierNameExpr', text: 'T'},
 // CHECK:STDOUT:             {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 3},
-// CHECK:STDOUT:           {kind: 'TuplePattern', text: ')', subtree_size: 5},
+// CHECK:STDOUT:           {kind: 'ExplicitParamList', text: ')', subtree_size: 5},
 // CHECK:STDOUT:         {kind: 'NameQualifierWithParams', text: '.', subtree_size: 12},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'F'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 17},
 // CHECK:STDOUT:     {kind: 'FunctionDefinition', text: '}', subtree_size: 18},
 // CHECK:STDOUT:         {kind: 'ClassIntroducer', text: 'class'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'OuterGeneric'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: 'T'},
 // CHECK:STDOUT:             {kind: 'TypeTypeLiteral', text: 'type'},
 // CHECK:STDOUT:           {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 3},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 5},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 5},
 // CHECK:STDOUT:       {kind: 'ClassDefinitionStart', text: '{', subtree_size: 8},
 // CHECK:STDOUT:         {kind: 'ClassIntroducer', text: 'class'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'InnerGeneric'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: 'U'},
 // CHECK:STDOUT:             {kind: 'TypeTypeLiteral', text: 'type'},
 // CHECK:STDOUT:           {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 3},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 5},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 5},
 // CHECK:STDOUT:       {kind: 'ClassDecl', text: ';', subtree_size: 8},
 // CHECK:STDOUT:     {kind: 'ClassDefinition', text: '}', subtree_size: 17},
 // CHECK:STDOUT:         {kind: 'ClassIntroducer', text: 'class'},
 // CHECK:STDOUT:           {kind: 'IdentifierNameBeforeParams', text: 'OuterGeneric'},
-// CHECK:STDOUT:             {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:             {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:               {kind: 'IdentifierNameNotBeforeParams', text: 'T'},
 // CHECK:STDOUT:               {kind: 'TypeTypeLiteral', text: 'type'},
 // CHECK:STDOUT:             {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 3},
-// CHECK:STDOUT:           {kind: 'TuplePattern', text: ')', subtree_size: 5},
+// CHECK:STDOUT:           {kind: 'ExplicitParamList', text: ')', subtree_size: 5},
 // CHECK:STDOUT:         {kind: 'NameQualifierWithParams', text: '.', subtree_size: 7},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'InnerGeneric'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: 'U'},
 // CHECK:STDOUT:             {kind: 'TypeTypeLiteral', text: 'type'},
 // CHECK:STDOUT:           {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 3},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 5},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 5},
 // CHECK:STDOUT:       {kind: 'ClassDefinitionStart', text: '{', subtree_size: 15},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'F'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: 'x'},
 // CHECK:STDOUT:             {kind: 'IdentifierNameExpr', text: 'T'},
 // CHECK:STDOUT:           {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
@@ -139,26 +139,26 @@ fn OuterGeneric(T:! type).InnerGeneric(U:! type).F(x: T, y: U) {}
 // CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: 'y'},
 // CHECK:STDOUT:             {kind: 'IdentifierNameExpr', text: 'U'},
 // CHECK:STDOUT:           {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 9},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 9},
 // CHECK:STDOUT:       {kind: 'FunctionDecl', text: ';', subtree_size: 12},
 // CHECK:STDOUT:     {kind: 'ClassDefinition', text: '}', subtree_size: 28},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:           {kind: 'IdentifierNameBeforeParams', text: 'OuterGeneric'},
-// CHECK:STDOUT:             {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:             {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:               {kind: 'IdentifierNameNotBeforeParams', text: 'T'},
 // CHECK:STDOUT:               {kind: 'TypeTypeLiteral', text: 'type'},
 // CHECK:STDOUT:             {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 3},
-// CHECK:STDOUT:           {kind: 'TuplePattern', text: ')', subtree_size: 5},
+// CHECK:STDOUT:           {kind: 'ExplicitParamList', text: ')', subtree_size: 5},
 // CHECK:STDOUT:         {kind: 'NameQualifierWithParams', text: '.', subtree_size: 7},
 // CHECK:STDOUT:           {kind: 'IdentifierNameBeforeParams', text: 'InnerGeneric'},
-// CHECK:STDOUT:             {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:             {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:               {kind: 'IdentifierNameNotBeforeParams', text: 'U'},
 // CHECK:STDOUT:               {kind: 'TypeTypeLiteral', text: 'type'},
 // CHECK:STDOUT:             {kind: 'CompileTimeBindingPattern', text: ':!', subtree_size: 3},
-// CHECK:STDOUT:           {kind: 'TuplePattern', text: ')', subtree_size: 5},
+// CHECK:STDOUT:           {kind: 'ExplicitParamList', text: ')', subtree_size: 5},
 // CHECK:STDOUT:         {kind: 'NameQualifierWithParams', text: '.', subtree_size: 7},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'F'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: 'x'},
 // CHECK:STDOUT:             {kind: 'IdentifierNameExpr', text: 'T'},
 // CHECK:STDOUT:           {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
@@ -166,7 +166,7 @@ fn OuterGeneric(T:! type).InnerGeneric(U:! type).F(x: T, y: U) {}
 // CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: 'y'},
 // CHECK:STDOUT:             {kind: 'IdentifierNameExpr', text: 'U'},
 // CHECK:STDOUT:           {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 9},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 9},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 26},
 // CHECK:STDOUT:     {kind: 'FunctionDefinition', text: '}', subtree_size: 27},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},

+ 4 - 4
toolchain/parse/testdata/generics/params/one.carbon

@@ -17,19 +17,19 @@ interface Bar(a: i32) {}
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:       {kind: 'ClassIntroducer', text: 'class'},
 // CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'Foo'},
-// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:           {kind: 'IdentifierNameNotBeforeParams', text: 'a'},
 // CHECK:STDOUT:           {kind: 'IntTypeLiteral', text: 'i32'},
 // CHECK:STDOUT:         {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
-// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', subtree_size: 5},
+// CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', subtree_size: 5},
 // CHECK:STDOUT:     {kind: 'ClassDecl', text: ';', subtree_size: 8},
 // CHECK:STDOUT:         {kind: 'InterfaceIntroducer', text: 'interface'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'Bar'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: 'a'},
 // CHECK:STDOUT:             {kind: 'IntTypeLiteral', text: 'i32'},
 // CHECK:STDOUT:           {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 5},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 5},
 // CHECK:STDOUT:       {kind: 'InterfaceDefinitionStart', text: '{', subtree_size: 8},
 // CHECK:STDOUT:     {kind: 'InterfaceDefinition', text: '}', subtree_size: 9},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},

+ 4 - 4
toolchain/parse/testdata/generics/params/one_suffix_comma.carbon

@@ -17,21 +17,21 @@ interface Bar(a: i32,) {}
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:       {kind: 'ClassIntroducer', text: 'class'},
 // CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'Foo'},
-// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:           {kind: 'IdentifierNameNotBeforeParams', text: 'a'},
 // CHECK:STDOUT:           {kind: 'IntTypeLiteral', text: 'i32'},
 // CHECK:STDOUT:         {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'PatternListComma', text: ','},
-// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', subtree_size: 6},
+// CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', subtree_size: 6},
 // CHECK:STDOUT:     {kind: 'ClassDecl', text: ';', subtree_size: 9},
 // CHECK:STDOUT:         {kind: 'InterfaceIntroducer', text: 'interface'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'Bar'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: 'a'},
 // CHECK:STDOUT:             {kind: 'IntTypeLiteral', text: 'i32'},
 // CHECK:STDOUT:           {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
 // CHECK:STDOUT:           {kind: 'PatternListComma', text: ','},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 6},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 6},
 // CHECK:STDOUT:       {kind: 'InterfaceDefinitionStart', text: '{', subtree_size: 9},
 // CHECK:STDOUT:     {kind: 'InterfaceDefinition', text: '}', subtree_size: 10},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},

+ 4 - 4
toolchain/parse/testdata/generics/params/six.carbon

@@ -17,7 +17,7 @@ interface Bar(a: i32, b: i32, c: i32, d: i32, e: i32, f: i32) {}
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:       {kind: 'ClassIntroducer', text: 'class'},
 // CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'Foo'},
-// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:           {kind: 'IdentifierNameNotBeforeParams', text: 'a'},
 // CHECK:STDOUT:           {kind: 'IntTypeLiteral', text: 'i32'},
 // CHECK:STDOUT:         {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
@@ -41,11 +41,11 @@ interface Bar(a: i32, b: i32, c: i32, d: i32, e: i32, f: i32) {}
 // CHECK:STDOUT:           {kind: 'IdentifierNameNotBeforeParams', text: 'f'},
 // CHECK:STDOUT:           {kind: 'IntTypeLiteral', text: 'i32'},
 // CHECK:STDOUT:         {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
-// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', subtree_size: 25},
+// CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', subtree_size: 25},
 // CHECK:STDOUT:     {kind: 'ClassDecl', text: ';', subtree_size: 28},
 // CHECK:STDOUT:         {kind: 'InterfaceIntroducer', text: 'interface'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'Bar'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: 'a'},
 // CHECK:STDOUT:             {kind: 'IntTypeLiteral', text: 'i32'},
 // CHECK:STDOUT:           {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
@@ -69,7 +69,7 @@ interface Bar(a: i32, b: i32, c: i32, d: i32, e: i32, f: i32) {}
 // CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: 'f'},
 // CHECK:STDOUT:             {kind: 'IntTypeLiteral', text: 'i32'},
 // CHECK:STDOUT:           {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 25},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 25},
 // CHECK:STDOUT:       {kind: 'InterfaceDefinitionStart', text: '{', subtree_size: 28},
 // CHECK:STDOUT:     {kind: 'InterfaceDefinition', text: '}', subtree_size: 29},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},

+ 4 - 4
toolchain/parse/testdata/generics/params/two.carbon

@@ -17,7 +17,7 @@ interface Bar(a: i32, b: i32) {}
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:       {kind: 'ClassIntroducer', text: 'class'},
 // CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'Foo'},
-// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:           {kind: 'IdentifierNameNotBeforeParams', text: 'a'},
 // CHECK:STDOUT:           {kind: 'IntTypeLiteral', text: 'i32'},
 // CHECK:STDOUT:         {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
@@ -25,11 +25,11 @@ interface Bar(a: i32, b: i32) {}
 // CHECK:STDOUT:           {kind: 'IdentifierNameNotBeforeParams', text: 'b'},
 // CHECK:STDOUT:           {kind: 'IntTypeLiteral', text: 'i32'},
 // CHECK:STDOUT:         {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
-// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', subtree_size: 9},
+// CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', subtree_size: 9},
 // CHECK:STDOUT:     {kind: 'ClassDecl', text: ';', subtree_size: 12},
 // CHECK:STDOUT:         {kind: 'InterfaceIntroducer', text: 'interface'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'Bar'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: 'a'},
 // CHECK:STDOUT:             {kind: 'IntTypeLiteral', text: 'i32'},
 // CHECK:STDOUT:           {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
@@ -37,7 +37,7 @@ interface Bar(a: i32, b: i32) {}
 // CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: 'b'},
 // CHECK:STDOUT:             {kind: 'IntTypeLiteral', text: 'i32'},
 // CHECK:STDOUT:           {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 9},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 9},
 // CHECK:STDOUT:       {kind: 'InterfaceDefinitionStart', text: '{', subtree_size: 12},
 // CHECK:STDOUT:     {kind: 'InterfaceDefinition', text: '}', subtree_size: 13},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},

+ 4 - 4
toolchain/parse/testdata/generics/params/two_suffix_comma.carbon

@@ -17,7 +17,7 @@ interface Bar(a: i32, b: i32,) {}
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:       {kind: 'ClassIntroducer', text: 'class'},
 // CHECK:STDOUT:       {kind: 'IdentifierNameBeforeParams', text: 'Foo'},
-// CHECK:STDOUT:         {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:           {kind: 'IdentifierNameNotBeforeParams', text: 'a'},
 // CHECK:STDOUT:           {kind: 'IntTypeLiteral', text: 'i32'},
 // CHECK:STDOUT:         {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
@@ -26,11 +26,11 @@ interface Bar(a: i32, b: i32,) {}
 // CHECK:STDOUT:           {kind: 'IntTypeLiteral', text: 'i32'},
 // CHECK:STDOUT:         {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
 // CHECK:STDOUT:         {kind: 'PatternListComma', text: ','},
-// CHECK:STDOUT:       {kind: 'TuplePattern', text: ')', subtree_size: 10},
+// CHECK:STDOUT:       {kind: 'ExplicitParamList', text: ')', subtree_size: 10},
 // CHECK:STDOUT:     {kind: 'ClassDecl', text: ';', subtree_size: 13},
 // CHECK:STDOUT:         {kind: 'InterfaceIntroducer', text: 'interface'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'Bar'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
 // CHECK:STDOUT:             {kind: 'IdentifierNameNotBeforeParams', text: 'a'},
 // CHECK:STDOUT:             {kind: 'IntTypeLiteral', text: 'i32'},
 // CHECK:STDOUT:           {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
@@ -39,7 +39,7 @@ interface Bar(a: i32, b: i32,) {}
 // CHECK:STDOUT:             {kind: 'IntTypeLiteral', text: 'i32'},
 // CHECK:STDOUT:           {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
 // CHECK:STDOUT:           {kind: 'PatternListComma', text: ','},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 10},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 10},
 // CHECK:STDOUT:       {kind: 'InterfaceDefinitionStart', text: '{', subtree_size: 13},
 // CHECK:STDOUT:     {kind: 'InterfaceDefinition', text: '}', subtree_size: 14},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},

+ 2 - 2
toolchain/parse/testdata/if/basic.carbon

@@ -23,8 +23,8 @@ fn F() {
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'F'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:           {kind: 'IfConditionStart', text: '('},
 // CHECK:STDOUT:           {kind: 'IdentifierNameExpr', text: 'a'},

+ 2 - 2
toolchain/parse/testdata/if/else.carbon

@@ -28,8 +28,8 @@ fn F() {
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'F'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:           {kind: 'IfConditionStart', text: '('},
 // CHECK:STDOUT:           {kind: 'IdentifierNameExpr', text: 'a'},

+ 2 - 2
toolchain/parse/testdata/if/fail_else_unbraced.carbon

@@ -42,8 +42,8 @@ fn F() {
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},
 // CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
 // CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'F'},
-// CHECK:STDOUT:           {kind: 'TuplePatternStart', text: '('},
-// CHECK:STDOUT:         {kind: 'TuplePattern', text: ')', subtree_size: 2},
+// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
+// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
 // CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
 // CHECK:STDOUT:           {kind: 'IfConditionStart', text: '('},
 // CHECK:STDOUT:           {kind: 'IdentifierNameExpr', text: 'a'},

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů