Sfoglia il codice sorgente

Drop support for named tuple fields (#886)

Rationale: Based on the status of #478 and #505, Carbon won't have this feature for a while, and it will be simpler not to support it on spec in the meantime.
Geoff Romer 4 anni fa
parent
commit
bb28d37eed

+ 8 - 4
executable_semantics/ast/expression.cpp

@@ -31,8 +31,7 @@ auto ExpressionFromParenContents(
 auto TupleExpressionFromParenContents(
     Nonnull<Arena*> arena, SourceLocation source_loc,
     const ParenContents<Expression>& paren_contents) -> Nonnull<Expression*> {
-  return arena->New<TupleLiteral>(
-      source_loc, paren_contents.TupleElements<FieldInitializer>(source_loc));
+  return arena->New<TupleLiteral>(source_loc, paren_contents.elements);
 }
 
 static void PrintOp(llvm::raw_ostream& out, Operator op) {
@@ -85,11 +84,16 @@ void Expression::Print(llvm::raw_ostream& out) const {
       out << access.aggregate() << "." << access.field();
       break;
     }
-    case Expression::Kind::TupleLiteral:
+    case Expression::Kind::TupleLiteral: {
       out << "(";
-      PrintFields(out, cast<TupleLiteral>(*this).fields(), " = ");
+      llvm::ListSeparator sep;
+      for (Nonnull<const Expression*> field :
+           cast<TupleLiteral>(*this).fields()) {
+        out << sep << *field;
+      }
       out << ")";
       break;
+    }
     case Expression::Kind::StructLiteral:
       out << "{";
       PrintFields(out, cast<StructLiteral>(*this).fields(), " = ");

+ 7 - 6
executable_semantics/ast/expression.h

@@ -92,8 +92,7 @@ auto TupleExpressionFromParenContents(
     Nonnull<Arena*> arena, SourceLocation source_loc,
     const ParenContents<Expression>& paren_contents) -> Nonnull<Expression*>;
 
-// A FieldInitializer represents the initialization of a single tuple or
-// struct field.
+// A FieldInitializer represents the initialization of a single struct field.
 class FieldInitializer {
  public:
   FieldInitializer(std::string name, Nonnull<Expression*> expression)
@@ -247,7 +246,7 @@ class TupleLiteral : public Expression {
       : TupleLiteral(source_loc, {}) {}
 
   explicit TupleLiteral(SourceLocation source_loc,
-                        std::vector<FieldInitializer> fields)
+                        std::vector<Nonnull<Expression*>> fields)
       : Expression(Kind::TupleLiteral, source_loc),
         fields_(std::move(fields)) {}
 
@@ -255,11 +254,13 @@ class TupleLiteral : public Expression {
     return exp->kind() == Kind::TupleLiteral;
   }
 
-  auto fields() const -> llvm::ArrayRef<FieldInitializer> { return fields_; }
-  auto fields() -> llvm::MutableArrayRef<FieldInitializer> { return fields_; }
+  auto fields() const -> llvm::ArrayRef<Nonnull<const Expression*>> {
+    return fields_;
+  }
+  auto fields() -> llvm::ArrayRef<Nonnull<Expression*>> { return fields_; }
 
  private:
-  std::vector<FieldInitializer> fields_;
+  std::vector<Nonnull<Expression*>> fields_;
 };
 
 // A non-empty literal value of a struct type.

+ 15 - 29
executable_semantics/ast/expression_test.cpp

@@ -19,12 +19,8 @@ using llvm::cast;
 using testing::ElementsAre;
 using testing::IsEmpty;
 
-// Matches a FieldInitializer named `name` whose `expression` is an
-// `IntLiteral`
-MATCHER_P(IntFieldNamed, name, "") {
-  return arg.name() == std::string(name) &&
-         arg.expression().kind() == Expression::Kind::IntLiteral;
-}
+// Matches any `IntLiteral`.
+MATCHER(IntField, "") { return arg->kind() == Expression::Kind::IntLiteral; }
 
 static auto FakeSourceLoc(int line_num) -> SourceLocation {
   return SourceLocation("<test>", line_num);
@@ -63,8 +59,7 @@ TEST_F(ExpressionTest, UnaryNoCommaAsExpression) {
   // )
   // ```
   ParenContents<Expression> contents = {
-      .elements = {{.name = std::nullopt,
-                    .term = arena.New<IntLiteral>(FakeSourceLoc(2), 42)}},
+      .elements = {arena.New<IntLiteral>(FakeSourceLoc(2), 42)},
       .has_trailing_comma = false};
 
   Nonnull<const Expression*> expression =
@@ -75,22 +70,19 @@ TEST_F(ExpressionTest, UnaryNoCommaAsExpression) {
 
 TEST_F(ExpressionTest, UnaryNoCommaAsTuple) {
   ParenContents<Expression> contents = {
-      .elements = {{.name = std::nullopt,
-                    .term = arena.New<IntLiteral>(FakeSourceLoc(2), 42)}},
+      .elements = {arena.New<IntLiteral>(FakeSourceLoc(2), 42)},
       .has_trailing_comma = false};
 
   Nonnull<const Expression*> tuple =
       TupleExpressionFromParenContents(&arena, FakeSourceLoc(1), contents);
   EXPECT_EQ(tuple->source_loc(), FakeSourceLoc(1));
   ASSERT_EQ(tuple->kind(), Expression::Kind::TupleLiteral);
-  EXPECT_THAT(cast<TupleLiteral>(*tuple).fields(),
-              ElementsAre(IntFieldNamed("0")));
+  EXPECT_THAT(cast<TupleLiteral>(*tuple).fields(), ElementsAre(IntField()));
 }
 
 TEST_F(ExpressionTest, UnaryWithCommaAsExpression) {
   ParenContents<Expression> contents = {
-      .elements = {{.name = std::nullopt,
-                    .term = arena.New<IntLiteral>(FakeSourceLoc(2), 42)}},
+      .elements = {arena.New<IntLiteral>(FakeSourceLoc(2), 42)},
       .has_trailing_comma = true};
 
   Nonnull<const Expression*> expression =
@@ -98,29 +90,25 @@ TEST_F(ExpressionTest, UnaryWithCommaAsExpression) {
   EXPECT_EQ(expression->source_loc(), FakeSourceLoc(1));
   ASSERT_EQ(expression->kind(), Expression::Kind::TupleLiteral);
   EXPECT_THAT(cast<TupleLiteral>(*expression).fields(),
-              ElementsAre(IntFieldNamed("0")));
+              ElementsAre(IntField()));
 }
 
 TEST_F(ExpressionTest, UnaryWithCommaAsTuple) {
   ParenContents<Expression> contents = {
-      .elements = {{.name = std::nullopt,
-                    .term = arena.New<IntLiteral>(FakeSourceLoc(2), 42)}},
+      .elements = {arena.New<IntLiteral>(FakeSourceLoc(2), 42)},
       .has_trailing_comma = true};
 
   Nonnull<const Expression*> tuple =
       TupleExpressionFromParenContents(&arena, FakeSourceLoc(1), contents);
   EXPECT_EQ(tuple->source_loc(), FakeSourceLoc(1));
   ASSERT_EQ(tuple->kind(), Expression::Kind::TupleLiteral);
-  EXPECT_THAT(cast<TupleLiteral>(*tuple).fields(),
-              ElementsAre(IntFieldNamed("0")));
+  EXPECT_THAT(cast<TupleLiteral>(*tuple).fields(), ElementsAre(IntField()));
 }
 
 TEST_F(ExpressionTest, BinaryAsExpression) {
   ParenContents<Expression> contents = {
-      .elements = {{.name = std::nullopt,
-                    .term = arena.New<IntLiteral>(FakeSourceLoc(2), 42)},
-                   {.name = std::nullopt,
-                    .term = arena.New<IntLiteral>(FakeSourceLoc(3), 42)}},
+      .elements = {arena.New<IntLiteral>(FakeSourceLoc(2), 42),
+                   arena.New<IntLiteral>(FakeSourceLoc(3), 42)},
       .has_trailing_comma = true};
 
   Nonnull<const Expression*> expression =
@@ -128,15 +116,13 @@ TEST_F(ExpressionTest, BinaryAsExpression) {
   EXPECT_EQ(expression->source_loc(), FakeSourceLoc(1));
   ASSERT_EQ(expression->kind(), Expression::Kind::TupleLiteral);
   EXPECT_THAT(cast<TupleLiteral>(*expression).fields(),
-              ElementsAre(IntFieldNamed("0"), IntFieldNamed("1")));
+              ElementsAre(IntField(), IntField()));
 }
 
 TEST_F(ExpressionTest, BinaryAsTuple) {
   ParenContents<Expression> contents = {
-      .elements = {{.name = std::nullopt,
-                    .term = arena.New<IntLiteral>(FakeSourceLoc(2), 42)},
-                   {.name = std::nullopt,
-                    .term = arena.New<IntLiteral>(FakeSourceLoc(3), 42)}},
+      .elements = {arena.New<IntLiteral>(FakeSourceLoc(2), 42),
+                   arena.New<IntLiteral>(FakeSourceLoc(3), 42)},
       .has_trailing_comma = true};
 
   Nonnull<const Expression*> tuple =
@@ -144,7 +130,7 @@ TEST_F(ExpressionTest, BinaryAsTuple) {
   EXPECT_EQ(tuple->source_loc(), FakeSourceLoc(1));
   ASSERT_EQ(tuple->kind(), Expression::Kind::TupleLiteral);
   EXPECT_THAT(cast<TupleLiteral>(*tuple).fields(),
-              ElementsAre(IntFieldNamed("0"), IntFieldNamed("1")));
+              ElementsAre(IntField(), IntField()));
 }
 
 }  // namespace

+ 6 - 43
executable_semantics/ast/paren_contents.h

@@ -26,25 +26,12 @@ namespace Carbon {
 // either `Expression` or `Pattern`.
 template <typename Term>
 struct ParenContents {
-  struct Element {
-    std::optional<std::string> name;
-    Nonnull<Term*> term;
-  };
-
-  // If this object represents a single term, with no name and no trailing
-  // comma, this method returns that term. This typically means the parentheses
-  // can be interpreted as grouping.
+  // If this object represents a single term with no trailing comma, this
+  // method returns that term. This typically means the parentheses can be
+  // interpreted as grouping.
   auto SingleTerm() const -> std::optional<Nonnull<Term*>>;
 
-  // Converts `elements` to std::vector<TupleElement>. TupleElement must
-  // have a constructor that takes a std::string and a Nonnull<Term*>.
-  //
-  // TODO: Find a way to deduce TupleElement from Term.
-  template <typename TupleElement>
-  auto TupleElements(SourceLocation source_loc) const
-      -> std::vector<TupleElement>;
-
-  std::vector<Element> elements;
+  std::vector<Nonnull<Term*>> elements;
   bool has_trailing_comma;
 };
 
@@ -52,37 +39,13 @@ struct ParenContents {
 
 template <typename Term>
 auto ParenContents<Term>::SingleTerm() const -> std::optional<Nonnull<Term*>> {
-  if (elements.size() == 1 && !elements.front().name.has_value() &&
-      !has_trailing_comma) {
-    return elements.front().term;
+  if (elements.size() == 1 && !has_trailing_comma) {
+    return elements.front();
   } else {
     return std::nullopt;
   }
 }
 
-template <typename Term>
-template <typename TupleElement>
-auto ParenContents<Term>::TupleElements(SourceLocation source_loc) const
-    -> std::vector<TupleElement> {
-  std::vector<TupleElement> result;
-  int i = 0;
-  bool seen_named_member = false;
-  for (auto element : elements) {
-    if (element.name.has_value()) {
-      seen_named_member = true;
-      result.push_back(TupleElement(*element.name, element.term));
-    } else {
-      if (seen_named_member) {
-        FATAL_PROGRAM_ERROR(source_loc)
-            << "positional members must come before named members";
-      }
-      result.push_back(TupleElement(std::to_string(i), element.term));
-    }
-    ++i;
-  }
-  return result;
-}
-
 }  // namespace Carbon
 
 #endif  // EXECUTABLE_SEMANTICS_AST_PAREN_CONTENTS_H_

+ 4 - 8
executable_semantics/ast/pattern.cpp

@@ -36,8 +36,8 @@ void Pattern::Print(llvm::raw_ostream& out) const {
       const auto& tuple = cast<TuplePattern>(*this);
       out << "(";
       llvm::ListSeparator sep;
-      for (const TuplePattern::Field& field : tuple.Fields()) {
-        out << sep << field.name << " = " << *field.pattern;
+      for (Nonnull<const Pattern*> field : tuple.Fields()) {
+        out << sep << *field;
       }
       out << ")";
       break;
@@ -69,9 +69,7 @@ auto TuplePatternFromParenContents(Nonnull<Arena*> arena,
                                    SourceLocation source_loc,
                                    const ParenContents<Pattern>& paren_contents)
     -> Nonnull<TuplePattern*> {
-  return arena->New<TuplePattern>(
-      source_loc,
-      paren_contents.TupleElements<TuplePattern::Field>(source_loc));
+  return arena->New<TuplePattern>(source_loc, paren_contents.elements);
 }
 
 // Used by AlternativePattern for constructor initialization. Produces a helpful
@@ -100,9 +98,7 @@ auto ParenExpressionToParenPattern(Nonnull<Arena*> arena,
   ParenContents<Pattern> result = {
       .elements = {}, .has_trailing_comma = contents.has_trailing_comma};
   for (const auto& element : contents.elements) {
-    result.elements.push_back(
-        {.name = element.name,
-         .term = arena->New<ExpressionPattern>(element.term)});
+    result.elements.push_back(arena->New<ExpressionPattern>(element));
   }
   return result;
 }

+ 6 - 16
executable_semantics/ast/pattern.h

@@ -114,30 +114,20 @@ class BindingPattern : public Pattern {
 // A pattern that matches a tuple value field-wise.
 class TuplePattern : public Pattern {
  public:
-  // Represents a portion of a tuple pattern corresponding to a single field.
-  struct Field {
-    Field(std::string name, Nonnull<Pattern*> pattern)
-        : name(std::move(name)), pattern(pattern) {}
-
-    // The field name. Cannot be empty
-    std::string name;
-
-    // The pattern the field must match.
-    Nonnull<Pattern*> pattern;
-  };
-
-  TuplePattern(SourceLocation source_loc, std::vector<Field> fields)
+  TuplePattern(SourceLocation source_loc, std::vector<Nonnull<Pattern*>> fields)
       : Pattern(Kind::TuplePattern, source_loc), fields(std::move(fields)) {}
 
   static auto classof(const Pattern* pattern) -> bool {
     return pattern->kind() == Kind::TuplePattern;
   }
 
-  auto Fields() const -> llvm::ArrayRef<Field> { return fields; }
-  auto Fields() -> llvm::MutableArrayRef<Field> { return fields; }
+  auto Fields() const -> llvm::ArrayRef<Nonnull<const Pattern*>> {
+    return fields;
+  }
+  auto Fields() -> llvm::ArrayRef<Nonnull<Pattern*>> { return fields; }
 
  private:
-  std::vector<Field> fields;
+  std::vector<Nonnull<Pattern*>> fields;
 };
 
 // Converts paren_contents to a Pattern, interpreting the parentheses as

+ 15 - 28
executable_semantics/ast/pattern_test.cpp

@@ -19,11 +19,8 @@ using llvm::isa;
 using testing::ElementsAre;
 using testing::IsEmpty;
 
-// Matches a TuplePattern::Field named `name` whose `pattern` is an
-// `AutoPattern`.
-MATCHER_P(AutoFieldNamed, name, "") {
-  return arg.name == std::string(name) && isa<AutoPattern>(*arg.pattern);
-}
+// Matches any `AutoPattern`.
+MATCHER(AutoField, "") { return isa<AutoPattern>(*arg); }
 
 static auto FakeSourceLoc(int line_num) -> SourceLocation {
   return SourceLocation("<test>", line_num);
@@ -61,8 +58,7 @@ TEST_F(PatternTest, UnaryNoCommaAsPattern) {
   // )
   // ```
   ParenContents<Pattern> contents = {
-      .elements = {{.name = std::nullopt,
-                    .term = arena.New<AutoPattern>(FakeSourceLoc(2))}},
+      .elements = {arena.New<AutoPattern>(FakeSourceLoc(2))},
       .has_trailing_comma = false};
 
   Nonnull<const Pattern*> pattern =
@@ -73,48 +69,42 @@ TEST_F(PatternTest, UnaryNoCommaAsPattern) {
 
 TEST_F(PatternTest, UnaryNoCommaAsTuplePattern) {
   ParenContents<Pattern> contents = {
-      .elements = {{.name = std::nullopt,
-                    .term = arena.New<AutoPattern>(FakeSourceLoc(2))}},
+      .elements = {arena.New<AutoPattern>(FakeSourceLoc(2))},
       .has_trailing_comma = false};
 
   Nonnull<const TuplePattern*> tuple =
       TuplePatternFromParenContents(&arena, FakeSourceLoc(1), contents);
   EXPECT_EQ(tuple->source_loc(), FakeSourceLoc(1));
-  EXPECT_THAT(tuple->Fields(), ElementsAre(AutoFieldNamed("0")));
+  EXPECT_THAT(tuple->Fields(), ElementsAre(AutoField()));
 }
 
 TEST_F(PatternTest, UnaryWithCommaAsPattern) {
   ParenContents<Pattern> contents = {
-      .elements = {{.name = std::nullopt,
-                    .term = arena.New<AutoPattern>(FakeSourceLoc(2))}},
+      .elements = {arena.New<AutoPattern>(FakeSourceLoc(2))},
       .has_trailing_comma = true};
 
   Nonnull<const Pattern*> pattern =
       PatternFromParenContents(&arena, FakeSourceLoc(1), contents);
   EXPECT_EQ(pattern->source_loc(), FakeSourceLoc(1));
   ASSERT_TRUE(isa<TuplePattern>(*pattern));
-  EXPECT_THAT(cast<TuplePattern>(*pattern).Fields(),
-              ElementsAre(AutoFieldNamed("0")));
+  EXPECT_THAT(cast<TuplePattern>(*pattern).Fields(), ElementsAre(AutoField()));
 }
 
 TEST_F(PatternTest, UnaryWithCommaAsTuplePattern) {
   ParenContents<Pattern> contents = {
-      .elements = {{.name = std::nullopt,
-                    .term = arena.New<AutoPattern>(FakeSourceLoc(2))}},
+      .elements = {arena.New<AutoPattern>(FakeSourceLoc(2))},
       .has_trailing_comma = true};
 
   Nonnull<const TuplePattern*> tuple =
       TuplePatternFromParenContents(&arena, FakeSourceLoc(1), contents);
   EXPECT_EQ(tuple->source_loc(), FakeSourceLoc(1));
-  EXPECT_THAT(tuple->Fields(), ElementsAre(AutoFieldNamed("0")));
+  EXPECT_THAT(tuple->Fields(), ElementsAre(AutoField()));
 }
 
 TEST_F(PatternTest, BinaryAsPattern) {
   ParenContents<Pattern> contents = {
-      .elements = {{.name = std::nullopt,
-                    .term = arena.New<AutoPattern>(FakeSourceLoc(2))},
-                   {.name = std::nullopt,
-                    .term = arena.New<AutoPattern>(FakeSourceLoc(2))}},
+      .elements = {arena.New<AutoPattern>(FakeSourceLoc(2)),
+                   arena.New<AutoPattern>(FakeSourceLoc(2))},
       .has_trailing_comma = true};
 
   Nonnull<const Pattern*> pattern =
@@ -122,22 +112,19 @@ TEST_F(PatternTest, BinaryAsPattern) {
   EXPECT_EQ(pattern->source_loc(), FakeSourceLoc(1));
   ASSERT_TRUE(isa<TuplePattern>(*pattern));
   EXPECT_THAT(cast<TuplePattern>(*pattern).Fields(),
-              ElementsAre(AutoFieldNamed("0"), AutoFieldNamed("1")));
+              ElementsAre(AutoField(), AutoField()));
 }
 
 TEST_F(PatternTest, BinaryAsTuplePattern) {
   ParenContents<Pattern> contents = {
-      .elements = {{.name = std::nullopt,
-                    .term = arena.New<AutoPattern>(FakeSourceLoc(2))},
-                   {.name = std::nullopt,
-                    .term = arena.New<AutoPattern>(FakeSourceLoc(2))}},
+      .elements = {arena.New<AutoPattern>(FakeSourceLoc(2)),
+                   arena.New<AutoPattern>(FakeSourceLoc(2))},
       .has_trailing_comma = true};
 
   Nonnull<const TuplePattern*> tuple =
       TuplePatternFromParenContents(&arena, FakeSourceLoc(1), contents);
   EXPECT_EQ(tuple->source_loc(), FakeSourceLoc(1));
-  EXPECT_THAT(tuple->Fields(),
-              ElementsAre(AutoFieldNamed("0"), AutoFieldNamed("1")));
+  EXPECT_THAT(tuple->Fields(), ElementsAre(AutoField(), AutoField()));
 }
 
 }  // namespace

+ 5 - 6
executable_semantics/interpreter/exec_program.cpp

@@ -18,18 +18,17 @@ namespace Carbon {
 static void AddIntrinsics(Nonnull<Arena*> arena,
                           std::vector<Nonnull<Declaration*>>* declarations) {
   SourceLocation source_loc("<intrinsic>", 0);
-  std::vector<TuplePattern::Field> print_fields = {TuplePattern::Field(
-      "0", arena->New<BindingPattern>(
-               source_loc, "format_str",
-               arena->New<ExpressionPattern>(
-                   arena->New<StringTypeLiteral>(source_loc))))};
+  std::vector<Nonnull<Pattern*>> print_params = {arena->New<BindingPattern>(
+      source_loc, "format_str",
+      arena->New<ExpressionPattern>(
+          arena->New<StringTypeLiteral>(source_loc)))};
   auto print_return = arena->New<Return>(
       source_loc,
       arena->New<IntrinsicExpression>(IntrinsicExpression::Intrinsic::Print),
       false);
   auto print = arena->New<FunctionDeclaration>(arena->New<FunctionDefinition>(
       source_loc, "Print", std::vector<GenericBinding>(),
-      arena->New<TuplePattern>(source_loc, print_fields),
+      arena->New<TuplePattern>(source_loc, print_params),
       arena->New<ExpressionPattern>(arena->New<TupleLiteral>(source_loc)),
       /*is_omitted_return_type=*/false, print_return));
   declarations->insert(declarations->begin(), print);

+ 16 - 51
executable_semantics/interpreter/interpreter.cpp

@@ -201,20 +201,14 @@ auto Interpreter::CreateTuple(Nonnull<Action*> act,
   // -> { { `(v1,...,vn) :: C, E, F} :: S, H}
   const auto& tup_lit = cast<TupleLiteral>(*exp);
   CHECK(act->results().size() == tup_lit.fields().size());
-  std::vector<TupleElement> elements;
-  for (size_t i = 0; i < act->results().size(); ++i) {
-    elements.push_back(
-        {.name = tup_lit.fields()[i].name(), .value = act->results()[i]});
-  }
-
-  return arena->New<TupleValue>(std::move(elements));
+  return arena->New<TupleValue>(act->results());
 }
 
 auto Interpreter::CreateStruct(const std::vector<FieldInitializer>& fields,
                                const std::vector<Nonnull<const Value*>>& values)
     -> Nonnull<const Value*> {
   CHECK(fields.size() == values.size());
-  std::vector<TupleElement> elements;
+  std::vector<StructElement> elements;
   for (size_t i = 0; i < fields.size(); ++i) {
     elements.push_back({.name = fields[i].name(), .value = values[i]});
   }
@@ -247,15 +241,8 @@ auto Interpreter::PatternMatch(Nonnull<const Value*> p, Nonnull<const Value*> v,
           }
           Env values(arena);
           for (size_t i = 0; i < p_tup.Elements().size(); ++i) {
-            if (p_tup.Elements()[i].name != v_tup.Elements()[i].name) {
-              FATAL_PROGRAM_ERROR(source_loc)
-                  << "Tuple field name '" << v_tup.Elements()[i].name
-                  << "' does not match pattern field name '"
-                  << p_tup.Elements()[i].name << "'";
-            }
-            std::optional<Env> matches =
-                PatternMatch(p_tup.Elements()[i].value,
-                             v_tup.Elements()[i].value, source_loc);
+            std::optional<Env> matches = PatternMatch(
+                p_tup.Elements()[i], v_tup.Elements()[i], source_loc);
             if (!matches) {
               return std::nullopt;
             }
@@ -356,14 +343,9 @@ void Interpreter::PatternAssignment(Nonnull<const Value*> pat,
                 << "arity mismatch in tuple pattern assignment:\n  pattern: "
                 << pat_tup << "\n  value: " << val_tup;
           }
-          for (const TupleElement& pattern_element : pat_tup.Elements()) {
-            std::optional<Nonnull<const Value*>> value_field =
-                val_tup.FindField(pattern_element.name);
-            if (!value_field) {
-              FATAL_RUNTIME_ERROR(source_loc)
-                  << "field " << pattern_element.name << "not in " << *val;
-            }
-            PatternAssignment(pattern_element.value, *value_field, source_loc);
+          for (size_t i = 0; i < pat_tup.Elements().size(); ++i) {
+            PatternAssignment(pat_tup.Elements()[i], val_tup.Elements()[i],
+                              source_loc);
           }
           break;
         }
@@ -453,7 +435,7 @@ auto Interpreter::StepLvalue() -> Transition {
         // -> { { ek+1 :: (f1=v1,..., fk=vk, fk+1=[],...) :: C, E, F} :: S,
         // H}
         return Spawn{arena->New<LValAction>(
-            &cast<TupleLiteral>(*exp).fields()[act->pos()].expression())};
+            cast<TupleLiteral>(*exp).fields()[act->pos()])};
       } else {
         return Done{CreateTuple(act, exp)};
       }
@@ -497,19 +479,13 @@ auto Interpreter::StepExp() -> Transition {
       } else {
         //    { { v :: [][i] :: C, E, F} :: S, H}
         // -> { { v_i :: C, E, F} : S, H}
-        auto* tuple = dyn_cast<TupleValue>(act->results()[0]);
-        if (tuple == nullptr) {
+        const auto& tuple = cast<TupleValue>(*act->results()[0]);
+        int i = cast<IntValue>(*act->results()[1]).Val();
+        if (i < 0 || i >= static_cast<int>(tuple.Elements().size())) {
           FATAL_RUNTIME_ERROR_NO_LINE()
-              << "expected a tuple in field access, not " << *act->results()[0];
+              << "index " << i << " out of range in " << tuple;
         }
-        std::string f =
-            std::to_string(cast<IntValue>(*act->results()[1]).Val());
-        std::optional<Nonnull<const Value*>> field = tuple->FindField(f);
-        if (!field) {
-          FATAL_RUNTIME_ERROR_NO_LINE()
-              << "field " << f << " not in " << *tuple;
-        }
-        return Done{*field};
+        return Done{tuple.Elements()[i]};
       }
     }
     case Expression::Kind::TupleLiteral: {
@@ -520,7 +496,7 @@ auto Interpreter::StepExp() -> Transition {
         // -> { { ek+1 :: (f1=v1,..., fk=vk, fk+1=[],...) :: C, E, F} :: S,
         // H}
         return Spawn{arena->New<ExpressionAction>(
-            &cast<TupleLiteral>(*exp).fields()[act->pos()].expression())};
+            cast<TupleLiteral>(*exp).fields()[act->pos()])};
       } else {
         return Done{CreateTuple(act, exp)};
       }
@@ -603,11 +579,6 @@ auto Interpreter::StepExp() -> Transition {
         //    { { v2 :: v1([]) :: C, E, F} :: S, H}
         // -> { {C',E',F'} :: {C, E, F} :: S, H}
         switch (act->results()[0]->kind()) {
-          case Value::Kind::NominalClassType: {
-            Nonnull<const Value*> arg =
-                CopyVal(arena, act->results()[1], exp->source_loc());
-            return Done{arena->New<NominalClassValue>(act->results()[0], arg)};
-          }
           case Value::Kind::AlternativeConstructorValue: {
             const auto& alt =
                 cast<AlternativeConstructorValue>(*act->results()[0]);
@@ -716,15 +687,9 @@ auto Interpreter::StepPattern() -> Transition {
         //    H}
         // -> { { ek+1 :: (f1=v1,..., fk=vk, fk+1=[],...) :: C, E, F} :: S,
         // H}
-        return Spawn{
-            arena->New<PatternAction>(tuple.Fields()[act->pos()].pattern)};
+        return Spawn{arena->New<PatternAction>(tuple.Fields()[act->pos()])};
       } else {
-        std::vector<TupleElement> elements;
-        for (size_t i = 0; i < tuple.Fields().size(); ++i) {
-          elements.push_back(
-              {.name = tuple.Fields()[i].name, .value = act->results()[i]});
-        }
-        return Done{arena->New<TupleValue>(std::move(elements))};
+        return Done{arena->New<TupleValue>(act->results())};
       }
     }
     case Pattern::Kind::AlternativePattern: {

+ 26 - 68
executable_semantics/interpreter/type_checker.cpp

@@ -126,8 +126,8 @@ static auto IsConcreteType(Nonnull<const Value*> value) -> bool {
       // `auto` isn't a concrete type, it's a pattern that matches types.
       return false;
     case Value::Kind::TupleValue:
-      for (const TupleElement& field : cast<TupleValue>(*value).Elements()) {
-        if (!IsConcreteType(field.value)) {
+      for (Nonnull<const Value*> field : cast<TupleValue>(*value).Elements()) {
+        if (!IsConcreteType(field)) {
           return false;
         }
       }
@@ -192,17 +192,16 @@ static auto IsImplicitlyConvertible(Nonnull<const Value*> source,
     case Value::Kind::TupleValue:
       switch (destination->kind()) {
         case Value::Kind::TupleValue: {
-          const std::vector<TupleElement>& source_elements =
+          const std::vector<Nonnull<const Value*>>& source_elements =
               cast<TupleValue>(*source).Elements();
-          const std::vector<TupleElement>& destination_elements =
+          const std::vector<Nonnull<const Value*>>& destination_elements =
               cast<TupleValue>(*destination).Elements();
           if (source_elements.size() != destination_elements.size()) {
             return false;
           }
           for (size_t i = 0; i < source_elements.size(); ++i) {
-            if (source_elements[i].name != destination_elements[i].name ||
-                !IsImplicitlyConvertible(source_elements[i].value,
-                                         destination_elements[i].value)) {
+            if (!IsImplicitlyConvertible(source_elements[i],
+                                         destination_elements[i])) {
               return false;
             }
           }
@@ -264,14 +263,9 @@ static auto ArgumentDeduction(SourceLocation source_loc, TypeEnv deduced,
             << arg_tup.Elements().size();
       }
       for (size_t i = 0; i < param_tup.Elements().size(); ++i) {
-        if (param_tup.Elements()[i].name != arg_tup.Elements()[i].name) {
-          FATAL_COMPILATION_ERROR(source_loc)
-              << "mismatch in tuple names, " << param_tup.Elements()[i].name
-              << " != " << arg_tup.Elements()[i].name;
-        }
-        deduced = ArgumentDeduction(source_loc, deduced,
-                                    param_tup.Elements()[i].value,
-                                    arg_tup.Elements()[i].value);
+        deduced =
+            ArgumentDeduction(source_loc, deduced, param_tup.Elements()[i],
+                              arg_tup.Elements()[i]);
       }
       return deduced;
     }
@@ -372,10 +366,9 @@ auto TypeChecker::Substitute(TypeEnv dict, Nonnull<const Value*> type)
       }
     }
     case Value::Kind::TupleValue: {
-      std::vector<TupleElement> elts;
+      std::vector<Nonnull<const Value*>> elts;
       for (const auto& elt : cast<TupleValue>(*type).Elements()) {
-        auto t = Substitute(dict, elt.value);
-        elts.push_back({.name = elt.name, .value = t});
+        elts.push_back(Substitute(dict, elt));
       }
       return arena->New<TupleValue>(elts);
     }
@@ -439,17 +432,15 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e, TypeEnv types,
       const Value& aggregate_type = index.aggregate().static_type();
       switch (aggregate_type.kind()) {
         case Value::Kind::TupleValue: {
-          auto i =
+          const auto& tuple_type = cast<TupleValue>(aggregate_type);
+          int i =
               cast<IntValue>(*interpreter.InterpExp(values, &index.offset()))
                   .Val();
-          std::string f = std::to_string(i);
-          std::optional<Nonnull<const Value*>> field_t =
-              cast<TupleValue>(aggregate_type).FindField(f);
-          if (!field_t) {
+          if (i < 0 || i >= static_cast<int>(tuple_type.Elements().size())) {
             FATAL_COMPILATION_ERROR(e->source_loc())
-                << "field " << f << " is not in the tuple " << aggregate_type;
+                << "index " << i << " is out of range for type " << tuple_type;
           }
-          SetStaticType(&index, *field_t);
+          SetStaticType(&index, tuple_type.Elements()[i]);
           return TCResult(res.types);
         }
         default:
@@ -457,15 +448,12 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e, TypeEnv types,
       }
     }
     case Expression::Kind::TupleLiteral: {
-      std::vector<FieldInitializer> new_args;
-      std::vector<TupleElement> arg_types;
+      std::vector<Nonnull<const Value*>> arg_types;
       auto new_types = types;
       for (auto& arg : cast<TupleLiteral>(*e).fields()) {
-        auto arg_res = TypeCheckExp(&arg.expression(), new_types, values);
+        auto arg_res = TypeCheckExp(arg, new_types, values);
         new_types = arg_res.types;
-        new_args.push_back(FieldInitializer(arg.name(), &arg.expression()));
-        arg_types.push_back(
-            {.name = arg.name(), .value = &arg.expression().static_type()});
+        arg_types.push_back(&arg->static_type());
       }
       SetStaticType(e, arena->New<TupleValue>(std::move(arg_types)));
       return TCResult(new_types);
@@ -542,18 +530,6 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e, TypeEnv types,
               << "class " << t_class.Name() << " does not have a field named "
               << access.field();
         }
-        case Value::Kind::TupleValue: {
-          const auto& tup = cast<TupleValue>(aggregate_type);
-          for (const TupleElement& field : tup.Elements()) {
-            if (access.field() == field.name) {
-              SetStaticType(&access, field.value);
-              return TCResult(res.types);
-            }
-          }
-          FATAL_COMPILATION_ERROR(e->source_loc())
-              << "tuple " << tup << " does not have a field named "
-              << access.field();
-        }
         case Value::Kind::ChoiceType: {
           const auto& choice = cast<ChoiceType>(aggregate_type);
           for (const auto& vt : choice.Alternatives()) {
@@ -778,8 +754,7 @@ auto TypeChecker::TypeCheckPattern(
     }
     case Pattern::Kind::TuplePattern: {
       auto& tuple = cast<TuplePattern>(*p);
-      std::vector<TuplePattern::Field> new_fields;
-      std::vector<TupleElement> field_types;
+      std::vector<Nonnull<const Value*>> field_types;
       auto new_types = types;
       if (expected && (*expected)->kind() != Value::Kind::TupleValue) {
         FATAL_COMPILATION_ERROR(p->source_loc()) << "didn't expect a tuple";
@@ -790,24 +765,15 @@ auto TypeChecker::TypeCheckPattern(
             << "tuples of different length";
       }
       for (size_t i = 0; i < tuple.Fields().size(); ++i) {
-        TuplePattern::Field& field = tuple.Fields()[i];
+        Nonnull<Pattern*> field = tuple.Fields()[i];
         std::optional<Nonnull<const Value*>> expected_field_type;
         if (expected) {
-          const TupleElement& expected_element =
-              cast<TupleValue>(**expected).Elements()[i];
-          if (expected_element.name != field.name) {
-            FATAL_COMPILATION_ERROR(tuple.source_loc())
-                << "field names do not match, expected "
-                << expected_element.name << " but got " << field.name;
-          }
-          expected_field_type = expected_element.value;
+          expected_field_type = cast<TupleValue>(**expected).Elements()[i];
         }
-        auto field_result = TypeCheckPattern(field.pattern, new_types, values,
-                                             expected_field_type);
+        auto field_result =
+            TypeCheckPattern(field, new_types, values, expected_field_type);
         new_types = field_result.types;
-        new_fields.push_back(TuplePattern::Field(field.name, field.pattern));
-        field_types.push_back(
-            {.name = field.name, .value = &field.pattern->static_type()});
+        field_types.push_back(&field->static_type());
       }
       SetStaticType(&tuple, arena->New<TupleValue>(std::move(field_types)));
       return TCResult(new_types);
@@ -1222,15 +1188,7 @@ void TypeChecker::TopLevel(Nonnull<Declaration*> d, TypeCheckContext* tops) {
       auto st = TypeOfClassDef(&class_def, tops->types, tops->values);
       Address a = interpreter.AllocateValue(st);
       tops->values.Set(class_def.name(), a);  // Is this obsolete?
-      std::vector<TupleElement> field_types;
-      for (const auto& [field_name, field_value] :
-           cast<NominalClassType>(*st).Fields()) {
-        field_types.push_back({.name = field_name, .value = field_value});
-      }
-      auto fun_ty = arena->New<FunctionType>(
-          std::vector<GenericBinding>(),
-          arena->New<TupleValue>(std::move(field_types)), st);
-      tops->types.Set(class_def.name(), fun_ty);
+      tops->types.Set(class_def.name(), st);
       break;
     }
 

+ 41 - 50
executable_semantics/interpreter/value.cpp

@@ -46,17 +46,7 @@ auto FieldsEqual(const VarValues& ts1, const VarValues& ts2) -> bool {
 
 auto StructValue::FindField(const std::string& name) const
     -> std::optional<Nonnull<const Value*>> {
-  for (const TupleElement& element : elements_) {
-    if (element.name == name) {
-      return element.value;
-    }
-  }
-  return std::nullopt;
-}
-
-auto TupleValue::FindField(const std::string& name) const
-    -> std::optional<Nonnull<const Value*>> {
-  for (const TupleElement& element : elements) {
+  for (const StructElement& element : elements_) {
     if (element.name == name) {
       return element.value;
     }
@@ -80,20 +70,12 @@ auto GetMember(Nonnull<Arena*> arena, Nonnull<const Value*> v,
     }
     case Value::Kind::NominalClassValue: {
       std::optional<Nonnull<const Value*>> field =
-          cast<TupleValue>(*cast<NominalClassValue>(*v).Inits()).FindField(f);
+          cast<StructValue>(*cast<NominalClassValue>(*v).Inits()).FindField(f);
       if (field == std::nullopt) {
         FATAL_RUNTIME_ERROR(source_loc) << "member " << f << " not in " << *v;
       }
       return *field;
     }
-    case Value::Kind::TupleValue: {
-      std::optional<Nonnull<const Value*>> field =
-          cast<TupleValue>(*v).FindField(f);
-      if (!field) {
-        FATAL_RUNTIME_ERROR(source_loc) << "field " << f << " not in " << *v;
-      }
-      return *field;
-    }
     case Value::Kind::ChoiceType: {
       const auto& choice = cast<ChoiceType>(*v);
       if (!FindInVarValues(f, choice.Alternatives())) {
@@ -130,9 +112,10 @@ auto SetFieldImpl(Nonnull<Arena*> arena, Nonnull<const Value*> value,
   }
   switch (value->kind()) {
     case Value::Kind::StructValue: {
-      std::vector<TupleElement> elements = cast<StructValue>(*value).elements();
+      std::vector<StructElement> elements =
+          cast<StructValue>(*value).elements();
       auto it = std::find_if(elements.begin(), elements.end(),
-                             [path_begin](const TupleElement& element) {
+                             [path_begin](const StructElement& element) {
                                return element.name == *path_begin;
                              });
       if (it == elements.end()) {
@@ -148,17 +131,16 @@ auto SetFieldImpl(Nonnull<Arena*> arena, Nonnull<const Value*> value,
                           path_begin, path_end, field_value, source_loc);
     }
     case Value::Kind::TupleValue: {
-      std::vector<TupleElement> elements = cast<TupleValue>(*value).Elements();
-      auto it = std::find_if(elements.begin(), elements.end(),
-                             [path_begin](const TupleElement& element) {
-                               return element.name == *path_begin;
-                             });
-      if (it == elements.end()) {
+      std::vector<Nonnull<const Value*>> elements =
+          cast<TupleValue>(*value).Elements();
+      // TODO(geoffromer): update FieldPath to hold integers as well as strings.
+      int index = std::stoi(*path_begin);
+      if (index < 0 || static_cast<size_t>(index) >= elements.size()) {
         FATAL_RUNTIME_ERROR(source_loc)
-            << "field " << *path_begin << " not in " << *value;
+            << "index " << *path_begin << " out of range in " << *value;
       }
-      it->value = SetFieldImpl(arena, it->value, path_begin + 1, path_end,
-                               field_value, source_loc);
+      elements[index] = SetFieldImpl(arena, elements[index], path_begin + 1,
+                                     path_end, field_value, source_loc);
       return arena->New<TupleValue>(elements);
     }
     default:
@@ -203,7 +185,7 @@ void Value::Print(llvm::raw_ostream& out) const {
       const auto& struct_val = cast<StructValue>(*this);
       out << "{";
       llvm::ListSeparator sep;
-      for (const TupleElement& element : struct_val.elements()) {
+      for (const StructElement& element : struct_val.elements()) {
         out << sep << "." << element.name << " = " << *element.value;
       }
       out << "}";
@@ -217,8 +199,8 @@ void Value::Print(llvm::raw_ostream& out) const {
     case Value::Kind::TupleValue: {
       out << "(";
       llvm::ListSeparator sep;
-      for (const TupleElement& element : cast<TupleValue>(*this).Elements()) {
-        out << sep << element.name << " = " << *element.value;
+      for (Nonnull<const Value*> element : cast<TupleValue>(*this).Elements()) {
+        out << sep << *element;
       }
       out << ")";
       break;
@@ -313,11 +295,9 @@ auto CopyVal(Nonnull<Arena*> arena, Nonnull<const Value*> val,
              SourceLocation source_loc) -> Nonnull<const Value*> {
   switch (val->kind()) {
     case Value::Kind::TupleValue: {
-      std::vector<TupleElement> elements;
-      for (const TupleElement& element : cast<TupleValue>(*val).Elements()) {
-        elements.push_back(
-            {.name = element.name,
-             .value = CopyVal(arena, element.value, source_loc)});
+      std::vector<Nonnull<const Value*>> elements;
+      for (Nonnull<const Value*> element : cast<TupleValue>(*val).Elements()) {
+        elements.push_back(CopyVal(arena, element, source_loc));
       }
       return arena->New<TupleValue>(std::move(elements));
     }
@@ -327,8 +307,8 @@ auto CopyVal(Nonnull<Arena*> arena, Nonnull<const Value*> val,
       return arena->New<AlternativeValue>(alt.AltName(), alt.ChoiceName(), arg);
     }
     case Value::Kind::StructValue: {
-      std::vector<TupleElement> elements;
-      for (const TupleElement& element : cast<StructValue>(*val).elements()) {
+      std::vector<StructElement> elements;
+      for (const StructElement& element : cast<StructValue>(*val).elements()) {
         elements.push_back(
             {.name = element.name,
              .value = CopyVal(arena, element.value, source_loc)});
@@ -435,8 +415,7 @@ auto TypeEqual(Nonnull<const Value*> t1, Nonnull<const Value*> t2) -> bool {
         return false;
       }
       for (size_t i = 0; i < tup1.Elements().size(); ++i) {
-        if (tup1.Elements()[i].name != tup2.Elements()[i].name ||
-            !TypeEqual(tup1.Elements()[i].value, tup2.Elements()[i].value)) {
+        if (!TypeEqual(tup1.Elements()[i], tup2.Elements()[i])) {
           return false;
         }
       }
@@ -459,16 +438,16 @@ auto TypeEqual(Nonnull<const Value*> t1, Nonnull<const Value*> t2) -> bool {
 
 // Returns true if all the fields of the two tuples contain equal values
 // and returns false otherwise.
-static auto FieldsValueEqual(const std::vector<TupleElement>& ts1,
-                             const std::vector<TupleElement>& ts2,
+static auto FieldsValueEqual(const std::vector<StructElement>& ts1,
+                             const std::vector<StructElement>& ts2,
                              SourceLocation source_loc) -> bool {
   if (ts1.size() != ts2.size()) {
     return false;
   }
-  for (const TupleElement& element : ts1) {
+  for (const StructElement& element : ts1) {
     auto iter = std::find_if(
         ts2.begin(), ts2.end(),
-        [&](const TupleElement& e2) { return e2.name == element.name; });
+        [&](const StructElement& e2) { return e2.name == element.name; });
     if (iter == ts2.end()) {
       return false;
     }
@@ -502,9 +481,21 @@ auto ValueEqual(Nonnull<const Value*> v1, Nonnull<const Value*> v2,
       return body1.has_value() == body2.has_value() &&
              (!body1.has_value() || *body1 == *body2);
     }
-    case Value::Kind::TupleValue:
-      return FieldsValueEqual(cast<TupleValue>(*v1).Elements(),
-                              cast<TupleValue>(*v2).Elements(), source_loc);
+    case Value::Kind::TupleValue: {
+      const std::vector<Nonnull<const Value*>>& elements1 =
+          cast<TupleValue>(*v1).Elements();
+      const std::vector<Nonnull<const Value*>>& elements2 =
+          cast<TupleValue>(*v2).Elements();
+      if (elements1.size() != elements2.size()) {
+        return false;
+      }
+      for (size_t i = 0; i < elements1.size(); ++i) {
+        if (!ValueEqual(elements1[i], elements2[i], source_loc)) {
+          return false;
+        }
+      }
+      return true;
+    }
     case Value::Kind::StructValue:
       return FieldsValueEqual(cast<StructValue>(*v1).elements(),
                               cast<StructValue>(*v2).elements(), source_loc);

+ 14 - 17
executable_semantics/interpreter/value.h

@@ -94,12 +94,11 @@ auto FindInVarValues(const std::string& field, const VarValues& inits)
     -> std::optional<Nonnull<const Value*>>;
 auto FieldsEqual(const VarValues& ts1, const VarValues& ts2) -> bool;
 
-// A TupleElement represents the value of a single tuple or struct field.
+// A StructElement represents the value of a single struct field.
 //
-// TODO(geoffromer): Rename this, and look for ways to eliminate duplication
-// among TupleElement, VarValues::value_type, FieldInitializer,
-// TuplePattern::Field, and any similar types.
-struct TupleElement {
+// TODO(geoffromer): Look for ways to eliminate duplication among StructElement,
+// VarValues::value_type, FieldInitializer, and any similar types.
+struct StructElement {
   // The field name.
   std::string name;
 
@@ -188,7 +187,7 @@ class BoolValue : public Value {
 // StructType instances.
 class StructValue : public Value {
  public:
-  explicit StructValue(std::vector<TupleElement> elements)
+  explicit StructValue(std::vector<StructElement> elements)
       : Value(Kind::StructValue), elements_(std::move(elements)) {
     CHECK(!elements_.empty())
         << "`{}` is represented as a StructType, not a StructValue.";
@@ -198,7 +197,7 @@ class StructValue : public Value {
     return value->kind() == Kind::StructValue;
   }
 
-  auto elements() const -> const std::vector<TupleElement>& {
+  auto elements() const -> const std::vector<StructElement>& {
     return elements_;
   }
 
@@ -208,7 +207,7 @@ class StructValue : public Value {
       -> std::optional<Nonnull<const Value*>>;
 
  private:
-  std::vector<TupleElement> elements_;
+  std::vector<StructElement> elements_;
 };
 
 // A value of a nominal class type.
@@ -278,26 +277,24 @@ class TupleValue : public Value {
  public:
   // An empty tuple, also known as the unit type.
   static auto Empty() -> Nonnull<const TupleValue*> {
-    static const TupleValue empty = TupleValue(std::vector<TupleElement>());
+    static const TupleValue empty =
+        TupleValue(std::vector<Nonnull<const Value*>>());
     return Nonnull<const TupleValue*>(&empty);
   }
 
-  explicit TupleValue(std::vector<TupleElement> elements)
+  explicit TupleValue(std::vector<Nonnull<const Value*>> elements)
       : Value(Kind::TupleValue), elements(std::move(elements)) {}
 
   static auto classof(const Value* value) -> bool {
     return value->kind() == Kind::TupleValue;
   }
 
-  auto Elements() const -> const std::vector<TupleElement>& { return elements; }
-
-  // Returns the value of the field named `name` in this tuple, or
-  // nullopt if there is no such field.
-  auto FindField(const std::string& name) const
-      -> std::optional<Nonnull<const Value*>>;
+  auto Elements() const -> const std::vector<Nonnull<const Value*>>& {
+    return elements;
+  }
 
  private:
-  std::vector<TupleElement> elements;
+  std::vector<Nonnull<const Value*>> elements;
 };
 
 // A binding placeholder value.

+ 8 - 23
executable_semantics/syntax/parser.ypp

@@ -127,14 +127,12 @@
 %type <Nonnull<BindingPattern*>> variable_declaration
 %type <Nonnull<Member*>> member
 %type <std::vector<Nonnull<Member*>>> member_list
-%type <ParenContents<Expression>::Element> paren_expression_element
 %type <ParenContents<Expression>> paren_expression_base
 %type <ParenContents<Expression>> paren_expression_contents
 %type <Nonnull<Pattern*>> paren_pattern
 %type <Nonnull<TuplePattern*>> tuple_pattern
 %type <Nonnull<TuplePattern*>> maybe_empty_tuple_pattern
 %type <ParenContents<Pattern>> paren_pattern_base
-%type <ParenContents<Pattern>::Element> paren_pattern_element
 %type <ParenContents<Pattern>> paren_pattern_contents
 %type <BisonWrap<ChoiceDeclaration::Alternative>> alternative
 %type <std::vector<ChoiceDeclaration::Alternative>> alternative_list
@@ -396,12 +394,6 @@ paren_expression: paren_expression_base
 tuple: paren_expression_base
     { $$ = TupleExpressionFromParenContents(arena, context.source_loc(), $1); }
 ;
-paren_expression_element:
-  expression
-    { $$ = {.name = std::nullopt, .term = $1}; }
-| designator EQUAL expression
-    { $$ = {.name = $1, .term = $3}; }
-;
 paren_expression_base:
   LEFT_PARENTHESIS RIGHT_PARENTHESIS
     { $$ = {.elements = {}, .has_trailing_comma = false}; }
@@ -414,9 +406,9 @@ paren_expression_base:
     }
 ;
 paren_expression_contents:
-  paren_expression_element
+  expression
     { $$ = {.elements = {$1}, .has_trailing_comma = false}; }
-| paren_expression_contents COMMA paren_expression_element
+| paren_expression_contents COMMA expression
     {
       $$ = $1;
       $$.elements.push_back($3);
@@ -501,31 +493,24 @@ paren_pattern_base:
 // is very different from the corresponding expression rule because is has to
 // enforce that requirement.
 paren_pattern_contents:
-  paren_pattern_element
+  non_expression_pattern
     { $$ = {.elements = {$1}, .has_trailing_comma = false}; }
-| paren_expression_contents COMMA paren_pattern_element
+| paren_expression_contents COMMA non_expression_pattern
     {
       $$ = ParenExpressionToParenPattern(arena, $1);
       $$.elements.push_back($3);
     }
-| paren_pattern_contents COMMA paren_expression_element
+| paren_pattern_contents COMMA expression
     {
       $$ = $1;
-      $$.elements.push_back({.name = $3.name,
-                             .term = arena->New<ExpressionPattern>($3.term)});
+      $$.elements.push_back(arena->New<ExpressionPattern>($3));
     }
-| paren_pattern_contents COMMA paren_pattern_element
+| paren_pattern_contents COMMA non_expression_pattern
     {
       $$ = $1;
       $$.elements.push_back($3);
     }
 ;
-paren_pattern_element:
-  non_expression_pattern
-    { $$ = {.name = std::nullopt, .term = $1}; }
-| designator EQUAL non_expression_pattern
-    { $$ = {.name = $1, .term = $3}; }
-;
 tuple_pattern: paren_pattern_base
     { $$ = TuplePatternFromParenContents(arena, context.source_loc(), $1); }
 ;
@@ -536,7 +521,7 @@ maybe_empty_tuple_pattern:
   LEFT_PARENTHESIS RIGHT_PARENTHESIS
     {
       $$ = arena->New<TuplePattern>(context.source_loc(),
-                                    std::vector<TuplePattern::Field>());
+                                    std::vector<Nonnull<Pattern*>>());
     }
 | tuple_pattern
     { $$ = $1; }

+ 1 - 1
executable_semantics/testdata/basic_syntax/record.carbon

@@ -12,7 +12,7 @@
 package ExecutableSemanticsTest api;
 
 fn main() -> i32 {
-  var t2: (.x = i32, .y = i32) = (.x = 2, .y = 5);
+  var t2: {.x: i32, .y: i32} = {.x = 2, .y = 5};
   t2.y = 3;
   return t2.y - t2.x - 1; // 3 - 2 - 1
 }

+ 1 - 1
executable_semantics/testdata/function/fail_call_with_tuple.carbon

@@ -7,7 +7,7 @@
 // RUN: not executable_semantics --trace %s 2>&1 | \
 // RUN:   FileCheck --match-full-lines --allow-unused-prefixes %s
 // AUTOUPDATE: executable_semantics %s
-// CHECK: COMPILATION ERROR: {{.*}}/executable_semantics/testdata/function/fail_call_with_tuple.carbon:19: type error in call: '(0 = (0 = i32, 1 = i32))' is not implicitly convertible to '(0 = i32, 1 = i32)'
+// CHECK: COMPILATION ERROR: {{.*}}/executable_semantics/testdata/function/fail_call_with_tuple.carbon:19: type error in call: '((i32, i32))' is not implicitly convertible to '(i32, i32)'
 
 package ExecutableSemanticsTest api;
 

+ 0 - 20
executable_semantics/testdata/function/fail_named_params_order.carbon

@@ -1,20 +0,0 @@
-// 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
-//
-// RUN: not executable_semantics %s 2>&1 | \
-// RUN:   FileCheck --match-full-lines --allow-unused-prefixes=false %s
-// RUN: not executable_semantics --trace %s 2>&1 | \
-// RUN:   FileCheck --match-full-lines --allow-unused-prefixes %s
-// AUTOUPDATE: executable_semantics %s
-// CHECK: PROGRAM ERROR: {{.*}}/executable_semantics/testdata/function/fail_named_params_order.carbon:14: positional members must come before named members
-
-package ExecutableSemanticsTest api;
-
-fn f(x: i32, .d = y: i32, z: i32, .e = a: i32) -> i32 {
-  return (x + y) - (z + a);
-}
-
-fn main() -> i32 {
-  return 0;
-}

+ 0 - 20
executable_semantics/testdata/function/named_params.carbon

@@ -1,20 +0,0 @@
-// 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
-//
-// RUN: executable_semantics %s 2>&1 | \
-// RUN:   FileCheck --match-full-lines --allow-unused-prefixes=false %s
-// RUN: executable_semantics --trace %s 2>&1 | \
-// RUN:   FileCheck --match-full-lines --allow-unused-prefixes %s
-// AUTOUPDATE: executable_semantics %s
-// CHECK: result: 0
-
-package ExecutableSemanticsTest api;
-
-fn f(x: i32, .d = y: i32) -> i32 {
-  return x + y;
-}
-
-fn main() -> i32 {
-  return f(1, .d = 2) - 3;
-}

+ 2 - 2
executable_semantics/testdata/tuple/fail_equality_type.carbon

@@ -5,8 +5,8 @@
 // RUN: not executable_semantics %s 2>&1 2>&1 | FileCheck %s
 // AUTOUPDATE: executable_semantics %s
 // CHECK: COMPILATION ERROR: {{.*}}/executable_semantics/testdata/tuple/fail_equality_type.carbon:16: type error in ==
-// CHECK: expected: (0 = i32, 1 = i32)
-// CHECK: actual: (0 = i32)
+// CHECK: expected: (i32, i32)
+// CHECK: actual: (i32)
 
 package ExecutableSemanticsTest api;
 

+ 0 - 19
executable_semantics/testdata/tuple/fail_name_order.carbon

@@ -1,19 +0,0 @@
-// 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
-//
-// RUN: not executable_semantics %s 2>&1 | \
-// RUN:   FileCheck --match-full-lines --allow-unused-prefixes=false %s
-// RUN: not executable_semantics --trace %s 2>&1 | \
-// RUN:   FileCheck --match-full-lines --allow-unused-prefixes %s
-// AUTOUPDATE: executable_semantics %s
-// CHECK: COMPILATION ERROR: {{.*}}/executable_semantics/testdata/tuple/fail_name_order.carbon:17: type error in name binding: '(y = i32, x = i32)' is not implicitly convertible to '(x = i32, y = i32)'
-
-package ExecutableSemanticsTest api;
-
-// Test the that field order matters for tuples.
-
-fn main() -> i32 {
-  var t: (.x = i32, .y = i32) = (.y = 2, .x = 3);
-  return 0;
-}

+ 0 - 17
executable_semantics/testdata/tuple/fail_positional_order.carbon

@@ -1,17 +0,0 @@
-// 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
-//
-// RUN: not executable_semantics %s 2>&1 | \
-// RUN:   FileCheck --match-full-lines --allow-unused-prefixes=false %s
-// RUN: not executable_semantics --trace %s 2>&1 | \
-// RUN:   FileCheck --match-full-lines --allow-unused-prefixes %s
-// AUTOUPDATE: executable_semantics %s
-// CHECK: PROGRAM ERROR: {{.*}}/executable_semantics/testdata/tuple/fail_positional_order.carbon:15: positional members must come before named members
-
-package ExecutableSemanticsTest api;
-
-fn main() -> i32 {
-  var t: auto = (.x = 2, 3);
-  return 0;
-}

+ 0 - 23
executable_semantics/testdata/tuple/match_with_named.carbon

@@ -1,23 +0,0 @@
-// 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
-//
-// RUN: executable_semantics %s 2>&1 | \
-// RUN:   FileCheck --match-full-lines --allow-unused-prefixes=false %s
-// RUN: executable_semantics --trace %s 2>&1 | \
-// RUN:   FileCheck --match-full-lines --allow-unused-prefixes %s
-// AUTOUPDATE: executable_semantics %s
-// CHECK: result: 0
-
-package ExecutableSemanticsTest api;
-
-// Test matching with a mixture of positional and named fields.
-
-fn main() -> i32 {
-  var t: auto = (2, .x = 5);
-  match (t) {
-    case (a: auto, .x = b: auto) =>
-      return a - b + 3;
-  }
-  return 1;
-}

+ 0 - 17
executable_semantics/testdata/tuple/named.carbon

@@ -1,17 +0,0 @@
-// 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
-//
-// RUN: executable_semantics %s 2>&1 | \
-// RUN:   FileCheck --match-full-lines --allow-unused-prefixes=false %s
-// RUN: executable_semantics --trace %s 2>&1 | \
-// RUN:   FileCheck --match-full-lines --allow-unused-prefixes %s
-// AUTOUPDATE: executable_semantics %s
-// CHECK: result: 0
-
-package ExecutableSemanticsTest api;
-
-fn main() -> i32 {
-  var t: (i32, .x = i32) = (3, .x = 2);
-  return t.x + 1 - t[0];
-}