Преглед изворни кода

Factor out a `Pattern` sum type from `Expression` (#685)

`Pattern` is intended to pilot some changes I would like to apply to all our sum types:
- The alternatives are expressed as derived classes rather than members of a `std::variant`.
- The alternatives are classes in the [style guide sense](https://google.github.io/styleguide/cppguide.html#Structs_vs._Classes), meaning they can have invariants, but can't have public data members.
- Creating an object is expressed using a constructor rather than a factory function.
- Accessing an alternative is expressed as a cast (using LLVM's RTTI system) rather than `std::get` or a `Get` method.

Co-authored-by: Jon Meow <46229924+jonmeow@users.noreply.github.com>
Geoff Romer пре 4 година
родитељ
комит
6ac3adfa53
31 измењених фајлова са 1298 додато и 571 уклоњено
  1. 45 0
      executable_semantics/ast/BUILD
  2. 4 6
      executable_semantics/ast/declaration.cpp
  3. 7 4
      executable_semantics/ast/declaration.h
  4. 21 49
      executable_semantics/ast/expression.cpp
  5. 22 23
      executable_semantics/ast/expression.h
  6. 137 0
      executable_semantics/ast/expression_test.cpp
  7. 5 4
      executable_semantics/ast/function_definition.h
  8. 4 4
      executable_semantics/ast/member.cpp
  9. 7 4
      executable_semantics/ast/member.h
  10. 106 0
      executable_semantics/ast/pattern.cpp
  11. 199 0
      executable_semantics/ast/pattern.h
  12. 130 0
      executable_semantics/ast/pattern_test.cpp
  13. 2 2
      executable_semantics/ast/statement.cpp
  14. 5 4
      executable_semantics/ast/statement.h
  15. 2 0
      executable_semantics/interpreter/BUILD
  16. 13 0
      executable_semantics/interpreter/action.cpp
  17. 12 1
      executable_semantics/interpreter/action.h
  18. 116 30
      executable_semantics/interpreter/interpreter.cpp
  19. 3 0
      executable_semantics/interpreter/interpreter.h
  20. 242 174
      executable_semantics/interpreter/typecheck.cpp
  21. 12 6
      executable_semantics/interpreter/typecheck.h
  22. 0 16
      executable_semantics/syntax/BUILD
  23. 0 22
      executable_semantics/syntax/paren_contents.cpp
  24. 62 27
      executable_semantics/syntax/paren_contents.h
  25. 0 114
      executable_semantics/syntax/paren_contents_test.cpp
  26. 138 60
      executable_semantics/syntax/parser.ypp
  27. 0 1
      executable_semantics/test_list.bzl
  28. 3 1
      executable_semantics/testdata/fun6_fail_type.golden
  29. 0 17
      executable_semantics/testdata/match_type.carbon
  30. 0 1
      executable_semantics/testdata/match_type.golden
  31. 1 1
      executable_semantics/testdata/pattern_variable_fail.golden

+ 45 - 0
executable_semantics/ast/BUILD

@@ -16,6 +16,7 @@ cc_library(
     deps = [
         ":function_definition",
         ":member",
+        ":pattern",
         ":struct_definition",
         "//common:ostream",
         "//executable_semantics/interpreter:address",
@@ -33,10 +34,26 @@ cc_library(
         "//common:ostream",
         "//executable_semantics/common:arena",
         "//executable_semantics/common:error",
+        "//executable_semantics/syntax:paren_contents",
         "@llvm-project//llvm:Support",
     ],
 )
 
+cc_test(
+    name = "expression_test",
+    srcs = ["expression_test.cpp"],
+    env = {
+        # TODO(#580): Remove this when leaks are fixed.
+        "ASAN_OPTIONS": "detect_leaks=0",
+    },
+    deps = [
+        ":expression",
+        "//executable_semantics/syntax:paren_contents",
+        "@llvm-project//llvm:gtest",
+        "@llvm-project//llvm:gtest_main",
+    ],
+)
+
 cc_library(
     name = "function_definition",
     srcs = ["function_definition.cpp"],
@@ -52,10 +69,37 @@ cc_library(
     name = "member",
     srcs = ["member.cpp"],
     hdrs = ["member.h"],
+    deps = [
+        ":pattern",
+        "//common:ostream",
+    ],
+)
+
+cc_library(
+    name = "pattern",
+    srcs = ["pattern.cpp"],
+    hdrs = ["pattern.h"],
     deps = [
         ":expression",
         "//common:ostream",
+        "//executable_semantics/common:error",
+        "@llvm-project//llvm:Support",
+    ],
+)
+
+cc_test(
+    name = "pattern_test",
+    srcs = ["pattern_test.cpp"],
+    env = {
+        # TODO(#580): Remove this when leaks are fixed.
+        "ASAN_OPTIONS": "detect_leaks=0",
+    },
+    deps = [
+        ":pattern",
+        "//executable_semantics/syntax:paren_contents",
         "@llvm-project//llvm:Support",
+        "@llvm-project//llvm:gtest",
+        "@llvm-project//llvm:gtest_main",
     ],
 )
 
@@ -65,6 +109,7 @@ cc_library(
     hdrs = ["statement.h"],
     deps = [
         ":expression",
+        ":pattern",
         "//common:check",
         "//common:ostream",
         "@llvm-project//llvm:Support",

+ 4 - 6
executable_semantics/ast/declaration.cpp

@@ -35,14 +35,13 @@ auto Declaration::MakeChoiceDeclaration(
   return d;
 }
 
-auto Declaration::MakeVariableDeclaration(int source_location, std::string name,
-                                          const Expression* type,
+auto Declaration::MakeVariableDeclaration(int source_location,
+                                          const BindingPattern* binding,
                                           const Expression* initializer)
     -> const Declaration {
   Declaration d;
   d.value = VariableDeclaration({.source_location = source_location,
-                                 .name = std::move(name),
-                                 .type = type,
+                                 .binding = binding,
                                  .initializer = initializer});
   return d;
 }
@@ -91,8 +90,7 @@ void Declaration::Print(llvm::raw_ostream& out) const {
 
     case DeclarationKind::VariableDeclaration: {
       const auto& var = GetVariableDeclaration();
-      out << "var " << *var.type << " : " << var.name << " = "
-          << *var.initializer << "\n";
+      out << "var " << *var.binding << " = " << *var.initializer << "\n";
       break;
     }
   }

+ 7 - 4
executable_semantics/ast/declaration.h

@@ -11,6 +11,7 @@
 #include "common/ostream.h"
 #include "executable_semantics/ast/function_definition.h"
 #include "executable_semantics/ast/member.h"
+#include "executable_semantics/ast/pattern.h"
 #include "executable_semantics/ast/struct_definition.h"
 #include "executable_semantics/interpreter/address.h"
 #include "executable_semantics/interpreter/dictionary.h"
@@ -58,8 +59,10 @@ struct ChoiceDeclaration {
 struct VariableDeclaration {
   static constexpr DeclarationKind Kind = DeclarationKind::VariableDeclaration;
   int source_location;
-  std::string name;
-  const Expression* type;
+  // TODO: split this into a non-optional name and a type, initialized by
+  // a constructor that takes a BindingPattern and handles errors like a
+  // missing name.
+  const BindingPattern* binding;
   const Expression* initializer;
 };
 
@@ -74,8 +77,8 @@ class Declaration {
       int line_num, std::string name,
       std::list<std::pair<std::string, const Expression*>> alternatives)
       -> const Declaration;
-  static auto MakeVariableDeclaration(int source_location, std::string name,
-                                      const Expression* type,
+  static auto MakeVariableDeclaration(int source_location,
+                                      const BindingPattern* binding,
                                       const Expression* initializer)
       -> const Declaration;
 

+ 21 - 49
executable_semantics/ast/expression.cpp

@@ -4,6 +4,8 @@
 
 #include "executable_semantics/ast/expression.h"
 
+#include <optional>
+
 #include "executable_semantics/common/arena.h"
 #include "executable_semantics/common/error.h"
 #include "llvm/ADT/StringExtras.h"
@@ -11,6 +13,24 @@
 
 namespace Carbon {
 
+auto ExpressionFromParenContents(
+    int line_num, const ParenContents<Expression>& paren_contents)
+    -> const Expression* {
+  std::optional<const Expression*> single_term = paren_contents.SingleTerm();
+  if (single_term.has_value()) {
+    return *single_term;
+  } else {
+    return TupleExpressionFromParenContents(line_num, paren_contents);
+  }
+}
+
+auto TupleExpressionFromParenContents(
+    int line_num, const ParenContents<Expression>& paren_contents)
+    -> const Expression* {
+  return Expression::MakeTupleLiteral(
+      line_num, paren_contents.TupleElements<FieldInitializer>(line_num));
+}
+
 auto Expression::GetIdentifierExpression() const
     -> const IdentifierExpression& {
   return std::get<IdentifierExpression>(value);
@@ -25,10 +45,6 @@ auto Expression::GetIndexExpression() const -> const IndexExpression& {
   return std::get<IndexExpression>(value);
 }
 
-auto Expression::GetBindingExpression() const -> const BindingExpression& {
-  return std::get<BindingExpression>(value);
-}
-
 auto Expression::GetIntLiteral() const -> int {
   return std::get<IntLiteral>(value).value;
 }
@@ -75,13 +91,6 @@ auto Expression::MakeBoolTypeLiteral(int line_num) -> const Expression* {
   return t;
 }
 
-auto Expression::MakeAutoTypeLiteral(int line_num) -> const Expression* {
-  auto* t = global_arena->New<Expression>();
-  t->line_num = line_num;
-  t->value = AutoTypeLiteral();
-  return t;
-}
-
 // Returns a Continuation type AST node at the given source location.
 auto Expression::MakeContinuationTypeLiteral(int line_num)
     -> const Expression* {
@@ -108,16 +117,6 @@ auto Expression::MakeIdentifierExpression(int line_num, std::string var)
   return v;
 }
 
-auto Expression::MakeBindingExpression(int line_num,
-                                       std::optional<std::string> var,
-                                       const Expression* type)
-    -> const Expression* {
-  auto* v = global_arena->New<Expression>();
-  v->line_num = line_num;
-  v->value = BindingExpression({.name = std::move(var), .type = type});
-  return v;
-}
-
 auto Expression::MakeIntLiteral(int line_num, int i) -> const Expression* {
   auto* e = global_arena->New<Expression>();
   e->line_num = line_num;
@@ -166,21 +165,7 @@ auto Expression::MakeTupleLiteral(int line_num,
     -> const Expression* {
   auto* e = global_arena->New<Expression>();
   e->line_num = line_num;
-  int i = 0;
-  bool seen_named_member = false;
-  for (auto& arg : args) {
-    if (arg.name == "") {
-      if (seen_named_member) {
-        FATAL_USER_ERROR(line_num)
-            << "positional members must come before named members";
-      }
-      arg.name = std::to_string(i);
-      ++i;
-    } else {
-      seen_named_member = true;
-    }
-  }
-  e->value = TupleLiteral({.fields = args});
+  e->value = TupleLiteral({.fields = std::move(args)});
   return e;
 }
 
@@ -269,16 +254,6 @@ void Expression::Print(llvm::raw_ostream& out) const {
     case ExpressionKind::IdentifierExpression:
       out << GetIdentifierExpression().name;
       break;
-    case ExpressionKind::BindingExpression: {
-      const BindingExpression& binding = GetBindingExpression();
-      if (binding.name.has_value()) {
-        out << *binding.name;
-      } else {
-        out << "_";
-      }
-      out << ": " << *binding.type;
-      break;
-    }
     case ExpressionKind::CallExpression:
       out << *GetCallExpression().function;
       if (GetCallExpression().argument->tag() == ExpressionKind::TupleLiteral) {
@@ -296,9 +271,6 @@ void Expression::Print(llvm::raw_ostream& out) const {
     case ExpressionKind::TypeTypeLiteral:
       out << "Type";
       break;
-    case ExpressionKind::AutoTypeLiteral:
-      out << "auto";
-      break;
     case ExpressionKind::ContinuationTypeLiteral:
       out << "Continuation";
       break;

+ 22 - 23
executable_semantics/ast/expression.h

@@ -11,15 +11,33 @@
 #include <vector>
 
 #include "common/ostream.h"
+#include "executable_semantics/syntax/paren_contents.h"
 #include "llvm/Support/Compiler.h"
 
 namespace Carbon {
 
 struct Expression;
+class Pattern;
+
+// Converts paren_contents to an Expression, interpreting the parentheses as
+// grouping if their contents permit that interpretation, or as forming a
+// tuple otherwise.
+auto ExpressionFromParenContents(
+    int line_num, const ParenContents<Expression>& paren_contents)
+    -> const Expression*;
+
+// Converts paren_contents to an Expression, interpreting the parentheses as
+// forming a tuple.
+auto TupleExpressionFromParenContents(
+    int line_num, const ParenContents<Expression>& paren_contents)
+    -> const Expression*;
 
 // A FieldInitializer represents the initialization of a single tuple field.
 struct FieldInitializer {
-  // The field name. For a positional field, this may be empty.
+  FieldInitializer(std::string name, const Expression* expression)
+      : name(std::move(name)), expression(expression) {}
+
+  // The field name. Cannot be empty.
   std::string name;
 
   // The expression that initializes the field.
@@ -27,7 +45,6 @@ struct FieldInitializer {
 };
 
 enum class ExpressionKind {
-  AutoTypeLiteral,
   BoolTypeLiteral,
   BoolLiteral,
   CallExpression,
@@ -37,7 +54,6 @@ enum class ExpressionKind {
   IntTypeLiteral,
   ContinuationTypeLiteral,  // The type of a continuation value.
   IntLiteral,
-  BindingExpression,
   PrimitiveOperatorExpression,
   TupleLiteral,
   TypeTypeLiteral,
@@ -76,13 +92,6 @@ struct IndexExpression {
   const Expression* offset;
 };
 
-struct BindingExpression {
-  static constexpr ExpressionKind Kind = ExpressionKind::BindingExpression;
-  // nullopt represents the `_` placeholder.
-  std::optional<std::string> name;
-  const Expression* type;
-};
-
 struct IntLiteral {
   static constexpr ExpressionKind Kind = ExpressionKind::IntLiteral;
   int value;
@@ -117,10 +126,6 @@ struct FunctionTypeLiteral {
   const Expression* return_type;
 };
 
-struct AutoTypeLiteral {
-  static constexpr ExpressionKind Kind = ExpressionKind::AutoTypeLiteral;
-};
-
 struct BoolTypeLiteral {
   static constexpr ExpressionKind Kind = ExpressionKind::BoolTypeLiteral;
 };
@@ -141,10 +146,6 @@ struct TypeTypeLiteral {
 struct Expression {
   static auto MakeIdentifierExpression(int line_num, std::string var)
       -> const Expression*;
-  static auto MakeBindingExpression(int line_num,
-                                    std::optional<std::string> var,
-                                    const Expression* type)
-      -> const Expression*;
   static auto MakeIntLiteral(int line_num, int i) -> const Expression*;
   static auto MakeBoolLiteral(int line_num, bool b) -> const Expression*;
   static auto MakePrimitiveOperatorExpression(
@@ -164,13 +165,11 @@ struct Expression {
   static auto MakeFunctionTypeLiteral(int line_num, const Expression* param,
                                       const Expression* ret)
       -> const Expression*;
-  static auto MakeAutoTypeLiteral(int line_num) -> const Expression*;
   static auto MakeContinuationTypeLiteral(int line_num) -> const Expression*;
 
   auto GetIdentifierExpression() const -> const IdentifierExpression&;
   auto GetFieldAccessExpression() const -> const FieldAccessExpression&;
   auto GetIndexExpression() const -> const IndexExpression&;
-  auto GetBindingExpression() const -> const BindingExpression&;
   auto GetIntLiteral() const -> int;
   auto GetBoolLiteral() const -> bool;
   auto GetTupleLiteral() const -> const TupleLiteral&;
@@ -190,10 +189,10 @@ struct Expression {
 
  private:
   std::variant<IdentifierExpression, FieldAccessExpression, IndexExpression,
-               BindingExpression, IntLiteral, BoolLiteral, TupleLiteral,
+               IntLiteral, BoolLiteral, TupleLiteral,
                PrimitiveOperatorExpression, CallExpression, FunctionTypeLiteral,
-               AutoTypeLiteral, BoolTypeLiteral, IntTypeLiteral,
-               ContinuationTypeLiteral, TypeTypeLiteral>
+               BoolTypeLiteral, IntTypeLiteral, ContinuationTypeLiteral,
+               TypeTypeLiteral>
       value;
 };
 

+ 137 - 0
executable_semantics/ast/expression_test.cpp

@@ -0,0 +1,137 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#include "executable_semantics/ast/expression.h"
+
+#include <string>
+
+#include "executable_semantics/syntax/paren_contents.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace Carbon {
+namespace {
+
+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->tag() == ExpressionKind::IntLiteral;
+}
+
+TEST(ExpressionTest, EmptyAsExpression) {
+  ParenContents<Expression> contents = {.elements = {},
+                                        .has_trailing_comma = false};
+  const Expression* expression =
+      ExpressionFromParenContents(/*line_num=*/1, contents);
+  EXPECT_EQ(expression->line_num, 1);
+  ASSERT_EQ(expression->tag(), ExpressionKind::TupleLiteral);
+  EXPECT_THAT(expression->GetTupleLiteral().fields, IsEmpty());
+}
+
+TEST(ExpressionTest, EmptyAsTuple) {
+  ParenContents<Expression> contents = {.elements = {},
+                                        .has_trailing_comma = false};
+  const Expression* tuple =
+      TupleExpressionFromParenContents(/*line_num=*/1, contents);
+  EXPECT_EQ(tuple->line_num, 1);
+  ASSERT_EQ(tuple->tag(), ExpressionKind::TupleLiteral);
+  EXPECT_THAT(tuple->GetTupleLiteral().fields, IsEmpty());
+}
+
+TEST(ExpressionTest, UnaryNoCommaAsExpression) {
+  // Equivalent to a code fragment like
+  // ```
+  // (
+  //   42
+  // )
+  // ```
+  ParenContents<Expression> contents = {
+      .elements = {{.name = std::nullopt,
+                    .term = Expression::MakeIntLiteral(/*line_num=*/2, 42)}},
+      .has_trailing_comma = false};
+
+  const Expression* expression =
+      ExpressionFromParenContents(/*line_num=*/1, contents);
+  EXPECT_EQ(expression->line_num, 2);
+  ASSERT_EQ(expression->tag(), ExpressionKind::IntLiteral);
+}
+
+TEST(ExpressionTest, UnaryNoCommaAsTuple) {
+  ParenContents<Expression> contents = {
+      .elements = {{.name = std::nullopt,
+                    .term = Expression::MakeIntLiteral(/*line_num=*/2, 42)}},
+      .has_trailing_comma = false};
+
+  const Expression* tuple =
+      TupleExpressionFromParenContents(/*line_num=*/1, contents);
+  EXPECT_EQ(tuple->line_num, 1);
+  ASSERT_EQ(tuple->tag(), ExpressionKind::TupleLiteral);
+  EXPECT_THAT(tuple->GetTupleLiteral().fields, ElementsAre(IntFieldNamed("0")));
+}
+
+TEST(ExpressionTest, UnaryWithCommaAsExpression) {
+  ParenContents<Expression> contents = {
+      .elements = {{.name = std::nullopt,
+                    .term = Expression::MakeIntLiteral(/*line_num=*/2, 42)}},
+      .has_trailing_comma = true};
+
+  const Expression* expression =
+      ExpressionFromParenContents(/*line_num=*/1, contents);
+  EXPECT_EQ(expression->line_num, 1);
+  ASSERT_EQ(expression->tag(), ExpressionKind::TupleLiteral);
+  EXPECT_THAT(expression->GetTupleLiteral().fields,
+              ElementsAre(IntFieldNamed("0")));
+}
+
+TEST(ExpressionTest, UnaryWithCommaAsTuple) {
+  ParenContents<Expression> contents = {
+      .elements = {{.name = std::nullopt,
+                    .term = Expression::MakeIntLiteral(/*line_num=*/2, 42)}},
+      .has_trailing_comma = true};
+
+  const Expression* tuple =
+      TupleExpressionFromParenContents(/*line_num=*/1, contents);
+  EXPECT_EQ(tuple->line_num, 1);
+  ASSERT_EQ(tuple->tag(), ExpressionKind::TupleLiteral);
+  EXPECT_THAT(tuple->GetTupleLiteral().fields, ElementsAre(IntFieldNamed("0")));
+}
+
+TEST(ExpressionTest, BinaryAsExpression) {
+  ParenContents<Expression> contents = {
+      .elements = {{.name = std::nullopt,
+                    .term = Expression::MakeIntLiteral(/*line_num=*/2, 42)},
+                   {.name = std::nullopt,
+                    .term = Expression::MakeIntLiteral(/*line_num=*/3, 42)}},
+      .has_trailing_comma = true};
+
+  const Expression* expression =
+      ExpressionFromParenContents(/*line_num=*/1, contents);
+  EXPECT_EQ(expression->line_num, 1);
+  ASSERT_EQ(expression->tag(), ExpressionKind::TupleLiteral);
+  EXPECT_THAT(expression->GetTupleLiteral().fields,
+              ElementsAre(IntFieldNamed("0"), IntFieldNamed("1")));
+}
+
+TEST(ExpressionTest, BinaryAsTuple) {
+  ParenContents<Expression> contents = {
+      .elements = {{.name = std::nullopt,
+                    .term = Expression::MakeIntLiteral(/*line_num=*/2, 42)},
+                   {.name = std::nullopt,
+                    .term = Expression::MakeIntLiteral(/*line_num=*/3, 42)}},
+      .has_trailing_comma = true};
+
+  const Expression* tuple =
+      TupleExpressionFromParenContents(/*line_num=*/1, contents);
+  EXPECT_EQ(tuple->line_num, 1);
+  ASSERT_EQ(tuple->tag(), ExpressionKind::TupleLiteral);
+  EXPECT_THAT(tuple->GetTupleLiteral().fields,
+              ElementsAre(IntFieldNamed("0"), IntFieldNamed("1")));
+}
+
+}  // namespace
+}  // namespace Carbon

+ 5 - 4
executable_semantics/ast/function_definition.h

@@ -7,6 +7,7 @@
 
 #include "common/ostream.h"
 #include "executable_semantics/ast/expression.h"
+#include "executable_semantics/ast/pattern.h"
 #include "executable_semantics/ast/statement.h"
 #include "llvm/Support/Compiler.h"
 
@@ -23,8 +24,8 @@ struct FunctionDefinition {
   FunctionDefinition() = default;
   FunctionDefinition(int line_num, std::string name,
                      std::vector<GenericBinding> deduced_params,
-                     const Expression* param_pattern,
-                     const Expression* return_type, const Statement* body)
+                     const TuplePattern* param_pattern,
+                     const Pattern* return_type, const Statement* body)
       : line_num(line_num),
         name(std::move(name)),
         deduced_parameters(deduced_params),
@@ -39,8 +40,8 @@ struct FunctionDefinition {
   int line_num;
   std::string name;
   std::vector<GenericBinding> deduced_parameters;
-  const Expression* param_pattern;
-  const Expression* return_type;
+  const TuplePattern* param_pattern;
+  const Pattern* return_type;
   const Statement* body;
 };
 

+ 4 - 4
executable_semantics/ast/member.cpp

@@ -6,11 +6,11 @@
 
 namespace Carbon {
 
-auto Member::MakeFieldMember(int line_num, std::string name,
-                             const Expression* type) -> Member* {
+auto Member::MakeFieldMember(int line_num, const BindingPattern* binding)
+    -> Member* {
   auto m = new Member();
   m->line_num = line_num;
-  m->value = FieldMember({.name = std::move(name), .type = type});
+  m->value = FieldMember({.binding = binding});
   return m;
 }
 
@@ -22,7 +22,7 @@ void Member::Print(llvm::raw_ostream& out) const {
   switch (tag()) {
     case MemberKind::FieldMember:
       const auto& field = GetFieldMember();
-      out << "var " << field.name << " : " << *field.type << ";\n";
+      out << "var " << field.binding << ";\n";
       break;
   }
 }

+ 7 - 4
executable_semantics/ast/member.h

@@ -9,6 +9,7 @@
 
 #include "common/ostream.h"
 #include "executable_semantics/ast/expression.h"
+#include "executable_semantics/ast/pattern.h"
 #include "llvm/Support/Compiler.h"
 
 namespace Carbon {
@@ -17,13 +18,15 @@ enum class MemberKind { FieldMember };
 
 struct FieldMember {
   static constexpr MemberKind Kind = MemberKind::FieldMember;
-  std::string name;
-  const Expression* type;
+  // TODO: split this into a non-optional name and a type, initialized by
+  // a constructor that takes a BindingPattern and handles errors like a
+  // missing name.
+  const BindingPattern* binding;
 };
 
 struct Member {
-  static auto MakeFieldMember(int line_num, std::string name,
-                              const Expression* type) -> Member*;
+  static auto MakeFieldMember(int line_num, const BindingPattern* binding)
+      -> Member*;
 
   auto GetFieldMember() const -> const FieldMember&;
 

+ 106 - 0
executable_semantics/ast/pattern.cpp

@@ -0,0 +1,106 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#include "executable_semantics/ast/pattern.h"
+
+#include <string>
+
+#include "common/ostream.h"
+#include "executable_semantics/ast/expression.h"
+#include "executable_semantics/common/error.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/Casting.h"
+
+namespace Carbon {
+
+using llvm::cast;
+
+void Pattern::Print(llvm::raw_ostream& out) const {
+  switch (Tag()) {
+    case Kind::AutoPattern:
+      out << "auto";
+      break;
+    case Kind::BindingPattern: {
+      const auto& binding = cast<BindingPattern>(*this);
+      if (binding.Name().has_value()) {
+        out << *binding.Name();
+      } else {
+        out << "_";
+      }
+      out << ": " << *binding.Type();
+      break;
+    }
+    case Kind::TuplePattern: {
+      const auto& tuple = cast<TuplePattern>(*this);
+      out << "(";
+      llvm::ListSeparator sep;
+      for (const TuplePattern::Field& field : tuple.Fields()) {
+        out << sep << field.name << " = " << field.pattern;
+      }
+      out << ")";
+      break;
+    }
+    case Kind::AlternativePattern: {
+      const auto& alternative = cast<AlternativePattern>(*this);
+      out << alternative.ChoiceType() << "." << alternative.AlternativeName()
+          << alternative.Arguments();
+      break;
+    }
+    case Kind::ExpressionPattern:
+      out << cast<ExpressionPattern>(*this).Expression();
+      break;
+  }
+}
+
+TuplePattern::TuplePattern(const Expression* tuple_literal)
+    : Pattern(Kind::TuplePattern, tuple_literal->line_num) {
+  const auto& tuple = tuple_literal->GetTupleLiteral();
+  for (const FieldInitializer& init : tuple.fields) {
+    fields.push_back(Field(init.name, new ExpressionPattern(init.expression)));
+  }
+}
+
+auto PatternFromParenContents(int line_num,
+                              const ParenContents<Pattern>& paren_contents)
+    -> const Pattern* {
+  std::optional<const Pattern*> single_term = paren_contents.SingleTerm();
+  if (single_term.has_value()) {
+    return *single_term;
+  } else {
+    return TuplePatternFromParenContents(line_num, paren_contents);
+  }
+}
+
+auto TuplePatternFromParenContents(int line_num,
+                                   const ParenContents<Pattern>& paren_contents)
+    -> const TuplePattern* {
+  return new TuplePattern(
+      line_num, paren_contents.TupleElements<TuplePattern::Field>(line_num));
+}
+
+AlternativePattern::AlternativePattern(int line_num,
+                                       const Expression* alternative,
+                                       const TuplePattern* arguments)
+    : Pattern(Kind::AlternativePattern, line_num), arguments(arguments) {
+  if (alternative->tag() != ExpressionKind::FieldAccessExpression) {
+    FATAL_USER_ERROR(alternative->line_num)
+        << "Alternative pattern must have the form of a field access.";
+  }
+  const auto& field_access = alternative->GetFieldAccessExpression();
+  choice_type = field_access.aggregate;
+  alternative_name = field_access.field;
+}
+
+auto ParenExpressionToParenPattern(const ParenContents<Expression>& contents)
+    -> ParenContents<Pattern> {
+  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 = new ExpressionPattern(element.term)});
+  }
+  return result;
+}
+
+}  // namespace Carbon

+ 199 - 0
executable_semantics/ast/pattern.h

@@ -0,0 +1,199 @@
+// 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
+
+#ifndef EXECUTABLE_SEMANTICS_AST_PATTERN_H_
+#define EXECUTABLE_SEMANTICS_AST_PATTERN_H_
+
+#include <optional>
+#include <string>
+#include <vector>
+
+#include "common/ostream.h"
+#include "executable_semantics/ast/expression.h"
+
+namespace Carbon {
+
+// Abstract base class of all AST nodes representing patterns.
+//
+// Pattern and its derived classes support LLVM-style RTTI, including
+// llvm::isa, llvm::cast, and llvm::dyn_cast. To support this, every
+// class derived from Pattern must provide a `classof` operation, and
+// every concrete derived class must have a corresponding enumerator
+// in `Kind`; see https://llvm.org/docs/HowToSetUpLLVMStyleRTTI.html for
+// details.
+class Pattern {
+ public:
+  enum class Kind {
+    AutoPattern,
+    BindingPattern,
+    TuplePattern,
+    AlternativePattern,
+    ExpressionPattern,
+  };
+
+  Pattern(const Pattern&) = delete;
+  Pattern& operator=(const Pattern&) = delete;
+
+  // Returns the enumerator corresponding to the most-derived type of this
+  // object.
+  auto Tag() const -> Kind { return tag; }
+
+  auto LineNumber() const -> int { return line_num; }
+
+  void Print(llvm::raw_ostream& out) const;
+
+ protected:
+  // Constructs a Pattern representing syntax at the given line number.
+  // `tag` must be the enumerator corresponding to the most-derived type being
+  // constructed.
+  Pattern(Kind tag, int line_num) : tag(tag), line_num(line_num) {}
+
+ private:
+  const Kind tag;
+  int line_num;
+};
+
+// A pattern consisting of the `auto` keyword.
+class AutoPattern : public Pattern {
+ public:
+  explicit AutoPattern(int line_num) : Pattern(Kind::AutoPattern, line_num) {}
+
+  static auto classof(const Pattern* pattern) -> bool {
+    return pattern->Tag() == Kind::AutoPattern;
+  }
+};
+
+// A pattern that matches a value of a specified type, and optionally binds
+// a name to it.
+class BindingPattern : public Pattern {
+ public:
+  BindingPattern(int line_num, std::optional<std::string> name,
+                 const Pattern* type)
+      : Pattern(Kind::BindingPattern, line_num),
+        name(std::move(name)),
+        type(type) {}
+
+  static auto classof(const Pattern* pattern) -> bool {
+    return pattern->Tag() == Kind::BindingPattern;
+  }
+
+  // The name this pattern binds, if any.
+  auto Name() const -> const std::optional<std::string>& { return name; }
+
+  // The pattern specifying the type of values that this pattern matches.
+  auto Type() const -> const Pattern* { return type; }
+
+ private:
+  std::optional<std::string> name;
+  const Pattern* type;
+};
+
+// 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, const Pattern* pattern)
+        : name(std::move(name)), pattern(pattern) {}
+
+    // The field name. Cannot be empty
+    std::string name;
+
+    // The pattern the field must match.
+    const Pattern* pattern;
+  };
+
+  TuplePattern(int line_num, std::vector<Field> fields)
+      : Pattern(Kind::TuplePattern, line_num), fields(std::move(fields)) {}
+
+  // Converts tuple_literal to a TuplePattern, by wrapping each field in an
+  // ExpressionPattern.
+  //
+  // REQUIRES: tuple_literal->Tag() == ExpressionKind::TupleLiteral
+  explicit TuplePattern(const Expression* tuple_literal);
+
+  static auto classof(const Pattern* pattern) -> bool {
+    return pattern->Tag() == Kind::TuplePattern;
+  }
+
+  auto Fields() const -> const std::vector<Field>& { return fields; }
+
+ private:
+  std::vector<Field> fields;
+};
+
+// Converts paren_contents to a Pattern, interpreting the parentheses as
+// grouping if their contents permit that interpretation, or as forming a
+// tuple otherwise.
+auto PatternFromParenContents(int line_num,
+                              const ParenContents<Pattern>& paren_contents)
+    -> const Pattern*;
+
+// Converts paren_contents to a TuplePattern, interpreting the parentheses as
+// forming a tuple.
+auto TuplePatternFromParenContents(int line_num,
+                                   const ParenContents<Pattern>& paren_contents)
+    -> const TuplePattern*;
+
+// Converts `contents` to ParenContents<Pattern> by replacing each Expression
+// with an ExpressionPattern.
+auto ParenExpressionToParenPattern(const ParenContents<Expression>& contents)
+    -> ParenContents<Pattern>;
+
+// A pattern that matches an alternative of a choice type.
+class AlternativePattern : public Pattern {
+ public:
+  // Constructs an AlternativePattern that matches a value of the type
+  // specified by choice_type if it represents an alternative named
+  // alternative_name, and its arguments match `arguments`.
+  AlternativePattern(int line_num, const Expression* choice_type,
+                     std::string alternative_name,
+                     const TuplePattern* arguments)
+      : Pattern(Kind::AlternativePattern, line_num),
+        choice_type(choice_type),
+        alternative_name(std::move(alternative_name)),
+        arguments(arguments) {}
+
+  // Constructs an AlternativePattern that matches the alternative specified
+  // by `alternative`, if its arguments match `arguments`.
+  AlternativePattern(int line_num, const Expression* alternative,
+                     const TuplePattern* arguments);
+
+  static auto classof(const Pattern* pattern) -> bool {
+    return pattern->Tag() == Kind::AlternativePattern;
+  }
+
+  auto ChoiceType() const -> const Expression* { return choice_type; }
+  auto AlternativeName() const -> const std::string& {
+    return alternative_name;
+  }
+  auto Arguments() const -> const TuplePattern* { return arguments; }
+
+ private:
+  const Expression* choice_type;
+  std::string alternative_name;
+  const TuplePattern* arguments;
+};
+
+// A pattern that matches a value if it is equal to the value of a given
+// expression.
+class ExpressionPattern : public Pattern {
+ public:
+  ExpressionPattern(const Expression* expression)
+      : Pattern(Kind::ExpressionPattern, expression->line_num),
+        expression(expression) {}
+
+  static auto classof(const Pattern* pattern) -> bool {
+    return pattern->Tag() == Kind::ExpressionPattern;
+  }
+
+  auto Expression() const -> const Expression* { return expression; }
+
+ private:
+  const Carbon::Expression* expression;
+};
+
+}  // namespace Carbon
+
+#endif  // EXECUTABLE_SEMANTICS_AST_PATTERN_H_

+ 130 - 0
executable_semantics/ast/pattern_test.cpp

@@ -0,0 +1,130 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#include "executable_semantics/ast/pattern.h"
+
+#include "executable_semantics/ast/expression.h"
+#include "executable_semantics/syntax/paren_contents.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "llvm/Support/Casting.h"
+
+namespace Carbon {
+namespace {
+
+using llvm::cast;
+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);
+}
+
+TEST(PatternTest, EmptyAsPattern) {
+  ParenContents<Pattern> contents = {.elements = {},
+                                     .has_trailing_comma = false};
+  const Pattern* pattern = PatternFromParenContents(/*line_num=*/1, contents);
+  EXPECT_EQ(pattern->LineNumber(), 1);
+  ASSERT_TRUE(isa<TuplePattern>(pattern));
+  EXPECT_THAT(cast<TuplePattern>(pattern)->Fields(), IsEmpty());
+}
+
+TEST(PatternTest, EmptyAsTuplePattern) {
+  ParenContents<Pattern> contents = {.elements = {},
+                                     .has_trailing_comma = false};
+  const TuplePattern* tuple =
+      TuplePatternFromParenContents(/*line_num=*/1, contents);
+  EXPECT_EQ(tuple->LineNumber(), 1);
+  EXPECT_THAT(tuple->Fields(), IsEmpty());
+}
+
+TEST(PatternTest, UnaryNoCommaAsPattern) {
+  // Equivalent to a code fragment like
+  // ```
+  // (
+  //   auto
+  // )
+  // ```
+  ParenContents<Pattern> contents = {
+      .elements = {{.name = std::nullopt,
+                    .term = new AutoPattern(/*line_num=*/2)}},
+      .has_trailing_comma = false};
+
+  const Pattern* pattern = PatternFromParenContents(/*line_num=*/1, contents);
+  EXPECT_EQ(pattern->LineNumber(), 2);
+  ASSERT_TRUE(isa<AutoPattern>(pattern));
+}
+
+TEST(PatternTest, UnaryNoCommaAsTuplePattern) {
+  ParenContents<Pattern> contents = {
+      .elements = {{.name = std::nullopt,
+                    .term = new AutoPattern(/*line_num=*/2)}},
+      .has_trailing_comma = false};
+
+  const TuplePattern* tuple =
+      TuplePatternFromParenContents(/*line_num=*/1, contents);
+  EXPECT_EQ(tuple->LineNumber(), 1);
+  EXPECT_THAT(tuple->Fields(), ElementsAre(AutoFieldNamed("0")));
+}
+
+TEST(PatternTest, UnaryWithCommaAsPattern) {
+  ParenContents<Pattern> contents = {
+      .elements = {{.name = std::nullopt,
+                    .term = new AutoPattern(/*line_num=*/2)}},
+      .has_trailing_comma = true};
+
+  const Pattern* pattern = PatternFromParenContents(/*line_num=*/1, contents);
+  EXPECT_EQ(pattern->LineNumber(), 1);
+  ASSERT_TRUE(isa<TuplePattern>(pattern));
+  EXPECT_THAT(cast<TuplePattern>(pattern)->Fields(),
+              ElementsAre(AutoFieldNamed("0")));
+}
+
+TEST(PatternTest, UnaryWithCommaAsTuplePattern) {
+  ParenContents<Pattern> contents = {
+      .elements = {{.name = std::nullopt,
+                    .term = new AutoPattern(/*line_num=*/2)}},
+      .has_trailing_comma = true};
+
+  const TuplePattern* tuple =
+      TuplePatternFromParenContents(/*line_num=*/1, contents);
+  EXPECT_EQ(tuple->LineNumber(), 1);
+  EXPECT_THAT(tuple->Fields(), ElementsAre(AutoFieldNamed("0")));
+}
+
+TEST(PatternTest, BinaryAsPattern) {
+  ParenContents<Pattern> contents = {
+      .elements = {{.name = std::nullopt,
+                    .term = new AutoPattern(/*line_num=*/2)},
+                   {.name = std::nullopt,
+                    .term = new AutoPattern(/*line_num=*/3)}},
+      .has_trailing_comma = true};
+
+  const Pattern* pattern = PatternFromParenContents(/*line_num=*/1, contents);
+  EXPECT_EQ(pattern->LineNumber(), 1);
+  ASSERT_TRUE(isa<TuplePattern>(pattern));
+  EXPECT_THAT(cast<TuplePattern>(pattern)->Fields(),
+              ElementsAre(AutoFieldNamed("0"), AutoFieldNamed("1")));
+}
+
+TEST(PatternTest, BinaryAsTuplePattern) {
+  ParenContents<Pattern> contents = {
+      .elements = {{.name = std::nullopt,
+                    .term = new AutoPattern(/*line_num=*/2)},
+                   {.name = std::nullopt,
+                    .term = new AutoPattern(/*line_num=*/3)}},
+      .has_trailing_comma = true};
+
+  const TuplePattern* tuple =
+      TuplePatternFromParenContents(/*line_num=*/1, contents);
+  EXPECT_EQ(tuple->LineNumber(), 1);
+  EXPECT_THAT(tuple->Fields(),
+              ElementsAre(AutoFieldNamed("0"), AutoFieldNamed("1")));
+}
+
+}  // namespace
+}  // namespace Carbon

+ 2 - 2
executable_semantics/ast/statement.cpp

@@ -76,7 +76,7 @@ auto Statement::MakeAssign(int line_num, const Expression* lhs,
   return s;
 }
 
-auto Statement::MakeVariableDefinition(int line_num, const Expression* pat,
+auto Statement::MakeVariableDefinition(int line_num, const Pattern* pat,
                                        const Expression* init)
     -> const Statement* {
   auto* s = new Statement();
@@ -142,7 +142,7 @@ auto Statement::MakeBlock(int line_num, const Statement* stmt)
 
 auto Statement::MakeMatch(
     int line_num, const Expression* exp,
-    std::list<std::pair<const Expression*, const Statement*>>* clauses)
+    std::list<std::pair<const Pattern*, const Statement*>>* clauses)
     -> const Statement* {
   auto* s = new Statement();
   s->line_num = line_num;

+ 5 - 4
executable_semantics/ast/statement.h

@@ -9,6 +9,7 @@
 
 #include "common/ostream.h"
 #include "executable_semantics/ast/expression.h"
+#include "executable_semantics/ast/pattern.h"
 #include "llvm/Support/Compiler.h"
 
 namespace Carbon {
@@ -45,7 +46,7 @@ struct Assign {
 
 struct VariableDefinition {
   static constexpr StatementKind Kind = StatementKind::VariableDefinition;
-  const Expression* pat;
+  const Pattern* pat;
   const Expression* init;
 };
 
@@ -89,7 +90,7 @@ struct Continue {
 struct Match {
   static constexpr StatementKind Kind = StatementKind::Match;
   const Expression* exp;
-  std::list<std::pair<const Expression*, const Statement*>>* clauses;
+  std::list<std::pair<const Pattern*, const Statement*>>* clauses;
 };
 
 struct Continuation {
@@ -113,7 +114,7 @@ struct Statement {
       -> const Statement*;
   static auto MakeAssign(int line_num, const Expression* lhs,
                          const Expression* rhs) -> const Statement*;
-  static auto MakeVariableDefinition(int line_num, const Expression* pat,
+  static auto MakeVariableDefinition(int line_num, const Pattern* pat,
                                      const Expression* init)
       -> const Statement*;
   static auto MakeIf(int line_num, const Expression* cond,
@@ -129,7 +130,7 @@ struct Statement {
   static auto MakeContinue(int line_num) -> const Statement*;
   static auto MakeMatch(
       int line_num, const Expression* exp,
-      std::list<std::pair<const Expression*, const Statement*>>* clauses)
+      std::list<std::pair<const Pattern*, const Statement*>>* clauses)
       -> const Statement*;
   // Returns an AST node for a continuation statement give its line number and
   // contituent parts.

+ 2 - 0
executable_semantics/interpreter/BUILD

@@ -98,6 +98,7 @@ cc_library(
         "//executable_semantics/ast:expression",
         "//executable_semantics/ast:function_definition",
         "//executable_semantics/common:tracing_flag",
+        "@llvm-project//llvm:Support",
     ],
 )
 
@@ -113,6 +114,7 @@ cc_library(
         "//executable_semantics/ast:function_definition",
         "//executable_semantics/ast:statement",
         "//executable_semantics/common:tracing_flag",
+        "@llvm-project//llvm:Support",
     ],
 )
 

+ 13 - 0
executable_semantics/interpreter/action.cpp

@@ -29,6 +29,12 @@ auto Action::MakeExpressionAction(const Expression* e) -> Action* {
   return act;
 }
 
+auto Action::MakePatternAction(const Pattern* p) -> Action* {
+  auto* act = new Action();
+  act->value = PatternAction({.pattern = p});
+  return act;
+}
+
 auto Action::MakeStatementAction(const Statement* s) -> Action* {
   auto* act = new Action();
   act->value = StatementAction({.stmt = s});
@@ -49,6 +55,10 @@ auto Action::GetExpressionAction() const -> const ExpressionAction& {
   return std::get<ExpressionAction>(value);
 }
 
+auto Action::GetPatternAction() const -> const PatternAction& {
+  return std::get<PatternAction>(value);
+}
+
 auto Action::GetStatementAction() const -> const StatementAction& {
   return std::get<StatementAction>(value);
 }
@@ -65,6 +75,9 @@ void Action::Print(llvm::raw_ostream& out) const {
     case ActionKind::ExpressionAction:
       out << *GetExpressionAction().exp;
       break;
+    case ActionKind::PatternAction:
+      out << *GetPatternAction().pattern;
+      break;
     case ActionKind::StatementAction:
       GetStatementAction().stmt->PrintDepth(1, out);
       break;

+ 12 - 1
executable_semantics/interpreter/action.h

@@ -9,6 +9,7 @@
 
 #include "common/ostream.h"
 #include "executable_semantics/ast/expression.h"
+#include "executable_semantics/ast/pattern.h"
 #include "executable_semantics/ast/statement.h"
 #include "executable_semantics/interpreter/stack.h"
 #include "executable_semantics/interpreter/value.h"
@@ -19,6 +20,7 @@ namespace Carbon {
 enum class ActionKind {
   LValAction,
   ExpressionAction,
+  PatternAction,
   StatementAction,
   ValAction,
 };
@@ -33,6 +35,11 @@ struct ExpressionAction {
   const Expression* exp;
 };
 
+struct PatternAction {
+  static constexpr ActionKind Kind = ActionKind::PatternAction;
+  const Pattern* pattern;
+};
+
 struct StatementAction {
   static constexpr ActionKind Kind = ActionKind::StatementAction;
   const Statement* stmt;
@@ -46,6 +53,7 @@ struct ValAction {
 struct Action {
   static auto MakeLValAction(const Expression* e) -> Action*;
   static auto MakeExpressionAction(const Expression* e) -> Action*;
+  static auto MakePatternAction(const Pattern* p) -> Action*;
   static auto MakeStatementAction(const Statement* s) -> Action*;
   static auto MakeValAction(const Value* v) -> Action*;
 
@@ -53,6 +61,7 @@ struct Action {
 
   auto GetLValAction() const -> const LValAction&;
   auto GetExpressionAction() const -> const ExpressionAction&;
+  auto GetPatternAction() const -> const PatternAction&;
   auto GetStatementAction() const -> const StatementAction&;
   auto GetValAction() const -> const ValAction&;
 
@@ -75,7 +84,9 @@ struct Action {
   std::vector<const Value*> results;
 
  private:
-  std::variant<LValAction, ExpressionAction, StatementAction, ValAction> value;
+  std::variant<LValAction, ExpressionAction, PatternAction, StatementAction,
+               ValAction>
+      value;
 };
 
 }  // namespace Carbon

+ 116 - 30
executable_semantics/interpreter/interpreter.cpp

@@ -20,6 +20,9 @@
 #include "executable_semantics/interpreter/frame.h"
 #include "executable_semantics/interpreter/stack.h"
 #include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/Casting.h"
+
+using llvm::cast;
 
 namespace Carbon {
 
@@ -114,7 +117,7 @@ void InitEnv(const Declaration& d, Env* env) {
             state->heap.AllocateValue(Value::MakeVariableType(deduced.name));
         new_env.Set(deduced.name, a);
       }
-      auto pt = InterpExp(new_env, func_def.param_pattern);
+      auto pt = InterpPattern(new_env, func_def.param_pattern);
       auto f = Value::MakeFunctionValue(func_def.name, pt, func_def.body);
       Address a = state->heap.AllocateValue(f);
       env->Set(func_def.name, a);
@@ -128,9 +131,11 @@ void InitEnv(const Declaration& d, Env* env) {
       for (const Member* m : struct_def.members) {
         switch (m->tag()) {
           case MemberKind::FieldMember: {
-            const auto& field = m->GetFieldMember();
-            auto t = InterpExp(Env(), field.type);
-            fields.push_back(make_pair(field.name, t));
+            const BindingPattern* binding = m->GetFieldMember().binding;
+            const Expression* type_expression =
+                cast<ExpressionPattern>(binding->Type())->Expression();
+            auto type = InterpExp(Env(), type_expression);
+            fields.push_back(make_pair(*binding->Name(), type));
             break;
           }
         }
@@ -161,7 +166,7 @@ void InitEnv(const Declaration& d, Env* env) {
       // result of evaluating the initializer.
       auto v = InterpExp(*env, var.initializer);
       Address a = state->heap.AllocateValue(v);
-      env->Set(var.name, a);
+      env->Set(*var.binding->Name(), a);
       break;
     }
   }
@@ -502,9 +507,7 @@ void StepLvalue() {
     case ExpressionKind::BoolTypeLiteral:
     case ExpressionKind::TypeTypeLiteral:
     case ExpressionKind::FunctionTypeLiteral:
-    case ExpressionKind::AutoTypeLiteral:
-    case ExpressionKind::ContinuationTypeLiteral:
-    case ExpressionKind::BindingExpression: {
+    case ExpressionKind::ContinuationTypeLiteral: {
       FATAL_RUNTIME_ERROR_NO_LINE()
           << "Can't treat expression as lvalue: " << *exp;
     }
@@ -521,19 +524,6 @@ void StepExp() {
     llvm::outs() << "--- step exp " << *exp << " --->\n";
   }
   switch (exp->tag()) {
-    case ExpressionKind::BindingExpression: {
-      if (act->pos == 0) {
-        frame->todo.Push(
-            Action::MakeExpressionAction(exp->GetBindingExpression().type));
-        act->pos++;
-      } else {
-        auto v = Value::MakeBindingPlaceholderValue(
-            exp->GetBindingExpression().name, act->results[0]);
-        frame->todo.Pop(1);
-        frame->todo.Push(Action::MakeValAction(v));
-      }
-      break;
-    }
     case ExpressionKind::IndexExpression: {
       if (act->pos == 0) {
         //    { { e[i] :: C, E, F} :: S, H}
@@ -696,13 +686,6 @@ void StepExp() {
       frame->todo.Push(Action::MakeValAction(v));
       break;
     }
-    case ExpressionKind::AutoTypeLiteral: {
-      CHECK(act->pos == 0);
-      const Value* v = Value::MakeAutoType();
-      frame->todo.Pop(1);
-      frame->todo.Push(Action::MakeValAction(v));
-      break;
-    }
     case ExpressionKind::TypeTypeLiteral: {
       CHECK(act->pos == 0);
       const Value* v = Value::MakeTypeType();
@@ -741,6 +724,91 @@ void StepExp() {
   }  // switch (exp->tag)
 }
 
+void StepPattern() {
+  Frame* frame = state->stack.Top();
+  Action* act = frame->todo.Top();
+  const Pattern* pattern = act->GetPatternAction().pattern;
+  if (tracing_output) {
+    llvm::outs() << "--- step pattern " << *pattern << " --->\n";
+  }
+  switch (pattern->Tag()) {
+    case Pattern::Kind::AutoPattern: {
+      CHECK(act->pos == 0);
+      const Value* v = Value::MakeAutoType();
+      frame->todo.Pop(1);
+      frame->todo.Push(Action::MakeValAction(v));
+      break;
+    }
+    case Pattern::Kind::BindingPattern: {
+      const auto& binding = cast<BindingPattern>(*pattern);
+      if (act->pos == 0) {
+        frame->todo.Push(Action::MakePatternAction(binding.Type()));
+        act->pos++;
+      } else {
+        auto v =
+            Value::MakeBindingPlaceholderValue(binding.Name(), act->results[0]);
+        frame->todo.Pop(1);
+        frame->todo.Push(Action::MakeValAction(v));
+      }
+      break;
+    }
+    case Pattern::Kind::TuplePattern: {
+      const auto& tuple = cast<TuplePattern>(*pattern);
+      if (act->pos == 0) {
+        if (tuple.Fields().empty()) {
+          frame->todo.Pop(1);
+          frame->todo.Push(Action::MakeValAction(Value::MakeTupleValue({})));
+        } else {
+          const Pattern* p1 = tuple.Fields()[0].pattern;
+          frame->todo.Push(Action::MakePatternAction(p1));
+          act->pos++;
+        }
+      } else if (act->pos != static_cast<int>(tuple.Fields().size())) {
+        //    { { vk :: (f1=v1,..., fk=[],fk+1=ek+1,...) :: C, E, F} :: S,
+        //    H}
+        // -> { { ek+1 :: (f1=v1,..., fk=vk, fk+1=[],...) :: C, E, F} :: S,
+        // H}
+        const Pattern* elt = tuple.Fields()[act->pos].pattern;
+        frame->todo.Push(Action::MakePatternAction(elt));
+        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]});
+        }
+        const Value* tuple_value = Value::MakeTupleValue(std::move(elements));
+        frame->todo.Pop(1);
+        frame->todo.Push(Action::MakeValAction(tuple_value));
+      }
+      break;
+    }
+    case Pattern::Kind::AlternativePattern: {
+      const auto& alternative = cast<AlternativePattern>(*pattern);
+      if (act->pos == 0) {
+        frame->todo.Push(
+            Action::MakeExpressionAction(alternative.ChoiceType()));
+        act->pos++;
+      } else if (act->pos == 1) {
+        frame->todo.Push(Action::MakePatternAction(alternative.Arguments()));
+        act->pos++;
+      } else {
+        CHECK(act->pos == 2);
+        const auto& choice_type = act->results[0]->GetChoiceType();
+        frame->todo.Pop(1);
+        frame->todo.Push(Action::MakeValAction(Value::MakeAlternativeValue(
+            alternative.AlternativeName(), choice_type.name, act->results[1])));
+      }
+      break;
+    }
+    case Pattern::Kind::ExpressionPattern:
+      frame->todo.Pop(1);
+      frame->todo.Push(Action::MakeExpressionAction(
+          cast<ExpressionPattern>(pattern)->Expression()));
+      break;
+  }
+}
+
 auto IsWhileAct(Action* act) -> bool {
   switch (act->tag()) {
     case ActionKind::StatementAction:
@@ -810,7 +878,7 @@ void StepStmt() {
           // start interpreting the pattern of the clause
           //    { {v :: (match ([]) ...) :: C, E, F} :: S, H}
           // -> { {pi :: (match ([]) ...) :: C, E, F} :: S, H}
-          frame->todo.Push(Action::MakeExpressionAction(c->first));
+          frame->todo.Push(Action::MakePatternAction(c->first));
           act->pos++;
         } else {  // try to match
           auto v = act->results[0];
@@ -916,7 +984,7 @@ void StepStmt() {
         act->pos++;
       } else if (act->pos == 1) {
         frame->todo.Push(
-            Action::MakeExpressionAction(stmt->GetVariableDefinition().pat));
+            Action::MakePatternAction(stmt->GetVariableDefinition().pat));
         act->pos++;
       } else if (act->pos == 2) {
         //    { { v :: (x = []) :: C, E, F} :: S, H}
@@ -1097,6 +1165,9 @@ void Step() {
     case ActionKind::ExpressionAction:
       StepExp();
       break;
+    case ActionKind::PatternAction:
+      StepPattern();
+      break;
     case ActionKind::StatementAction:
       StepStmt();
       break;
@@ -1150,4 +1221,19 @@ auto InterpExp(Env values, const Expression* e) -> const Value* {
   return v;
 }
 
+// Interpret a pattern at compile-time.
+auto InterpPattern(Env values, const Pattern* p) -> const Value* {
+  auto todo = Stack(Action::MakePatternAction(p));
+  auto* scope = new Scope(values, std::list<std::string>());
+  auto* frame = new Frame("InterpPattern", Stack(scope), todo);
+  state->stack = Stack(frame);
+
+  while (state->stack.Count() > 1 || state->stack.Top()->todo.Count() > 1 ||
+         state->stack.Top()->todo.Top()->tag() != ActionKind::ValAction) {
+    Step();
+  }
+  const Value* v = state->stack.Top()->todo.Top()->GetValAction().val;
+  return v;
+}
+
 }  // namespace Carbon

+ 3 - 0
executable_semantics/interpreter/interpreter.h

@@ -11,6 +11,8 @@
 
 #include "common/ostream.h"
 #include "executable_semantics/ast/declaration.h"
+#include "executable_semantics/ast/expression.h"
+#include "executable_semantics/ast/pattern.h"
 #include "executable_semantics/interpreter/frame.h"
 #include "executable_semantics/interpreter/heap.h"
 #include "executable_semantics/interpreter/stack.h"
@@ -35,6 +37,7 @@ void PrintEnv(Env values);
 
 auto InterpProgram(std::list<Declaration>* fs) -> int;
 auto InterpExp(Env values, const Expression* e) -> const Value*;
+auto InterpPattern(Env values, const Pattern* p) -> const Value*;
 
 }  // namespace Carbon
 

+ 242 - 174
executable_semantics/interpreter/typecheck.cpp

@@ -10,10 +10,16 @@
 #include <set>
 #include <vector>
 
+#include "common/ostream.h"
 #include "executable_semantics/ast/function_definition.h"
 #include "executable_semantics/common/error.h"
 #include "executable_semantics/common/tracing_flag.h"
 #include "executable_semantics/interpreter/interpreter.h"
+#include "executable_semantics/interpreter/value.h"
+#include "llvm/Support/Casting.h"
+
+using llvm::cast;
+using llvm::dyn_cast;
 
 namespace Carbon {
 
@@ -53,8 +59,8 @@ auto ReifyType(const Value* t, int line_num) -> const Expression* {
     case ValKind::TupleValue: {
       std::vector<FieldInitializer> args;
       for (const TupleElement& field : t->GetTupleValue().elements) {
-        args.push_back({.name = field.name,
-                        .expression = ReifyType(field.value, line_num)});
+        args.push_back(
+            FieldInitializer(field.name, ReifyType(field.value, line_num)));
       }
       return Expression::MakeTupleLiteral(0, args);
     }
@@ -230,58 +236,14 @@ auto Substitute(TypeEnv dict, const Value* type) -> const Value* {
 // types maps variable names to the type of their run-time value.
 // values maps variable names to their compile-time values. It is not
 //    directly used in this function but is passed to InterExp.
-// expected is the type that this expression is expected to have.
-//    This parameter is non-null when the expression is in a pattern context
-//    and it is used to implement `auto`, otherwise it is null.
-// context says what kind of position this expression is nested in,
-//    whether it's a position that expects a value, a pattern, or a type.
-auto TypeCheckExp(const Expression* e, TypeEnv types, Env values,
-                  const Value* expected, TCContext context) -> TCResult {
+auto TypeCheckExp(const Expression* e, TypeEnv types, Env values)
+    -> TCExpression {
   if (tracing_output) {
-    switch (context) {
-      case TCContext::ValueContext:
-        llvm::outs() << "checking expression ";
-        break;
-      case TCContext::PatternContext:
-        llvm::outs() << "checking pattern, ";
-        if (expected) {
-          llvm::outs() << "expecting " << *expected;
-        }
-        llvm::outs() << ", ";
-        break;
-      case TCContext::TypeContext:
-        llvm::outs() << "checking type ";
-        break;
-    }
-    llvm::outs() << *e << "\n";
+    llvm::outs() << "checking expression " << *e << "\n";
   }
   switch (e->tag()) {
-    case ExpressionKind::BindingExpression: {
-      if (context != TCContext::PatternContext) {
-        FATAL_COMPILATION_ERROR(e->line_num)
-            << "pattern variables are only allowed in pattern context";
-      }
-      auto t = InterpExp(values, e->GetBindingExpression().type);
-      if (t->tag() == ValKind::AutoType) {
-        if (expected == nullptr) {
-          FATAL_COMPILATION_ERROR(e->line_num) << " auto not allowed here";
-        } else {
-          t = expected;
-        }
-      } else if (expected) {
-        ExpectType(e->line_num, "pattern variable", t, expected);
-      }
-      const std::optional<std::string>& name = e->GetBindingExpression().name;
-      auto new_e = Expression::MakeBindingExpression(e->line_num, name,
-                                                     ReifyType(t, e->line_num));
-      if (name.has_value()) {
-        types.Set(*name, t);
-      }
-      return TCResult(new_e, t, types);
-    }
     case ExpressionKind::IndexExpression: {
-      auto res = TypeCheckExp(e->GetIndexExpression().aggregate, types, values,
-                              nullptr, TCContext::ValueContext);
+      auto res = TypeCheckExp(e->GetIndexExpression().aggregate, types, values);
       auto t = res.type;
       switch (t->tag()) {
         case ValKind::TupleValue: {
@@ -295,7 +257,7 @@ auto TypeCheckExp(const Expression* e, TypeEnv types, Env values,
           }
           auto new_e = Expression::MakeIndexExpression(
               e->line_num, res.exp, Expression::MakeIntLiteral(e->line_num, i));
-          return TCResult(new_e, field_t, res.types);
+          return TCExpression(new_e, field_t, res.types);
         }
         default:
           FATAL_COMPILATION_ERROR(e->line_num) << "expected a tuple";
@@ -305,39 +267,21 @@ auto TypeCheckExp(const Expression* e, TypeEnv types, Env values,
       std::vector<FieldInitializer> new_args;
       std::vector<TupleElement> arg_types;
       auto new_types = types;
-      if (expected && expected->tag() != ValKind::TupleValue) {
-        FATAL_COMPILATION_ERROR(e->line_num) << "didn't expect a tuple";
-      }
-      if (expected && e->GetTupleLiteral().fields.size() !=
-                          expected->GetTupleValue().elements.size()) {
-        FATAL_COMPILATION_ERROR(e->line_num) << "tuples of different length";
-      }
       int i = 0;
       for (auto arg = e->GetTupleLiteral().fields.begin();
            arg != e->GetTupleLiteral().fields.end(); ++arg, ++i) {
-        const Value* arg_expected = nullptr;
-        if (expected && expected->tag() == ValKind::TupleValue) {
-          if (expected->GetTupleValue().elements[i].name != arg->name) {
-            FATAL_COMPILATION_ERROR(e->line_num)
-                << "field names do not match, expected "
-                << expected->GetTupleValue().elements[i].name << " but got "
-                << arg->name;
-          }
-          arg_expected = expected->GetTupleValue().elements[i].value;
-        }
-        auto arg_res = TypeCheckExp(arg->expression, new_types, values,
-                                    arg_expected, context);
+        auto arg_res = TypeCheckExp(arg->expression, new_types, values);
         new_types = arg_res.types;
-        new_args.push_back({.name = arg->name, .expression = arg_res.exp});
+        new_args.push_back(FieldInitializer(arg->name, arg_res.exp));
         arg_types.push_back({.name = arg->name, .value = arg_res.type});
       }
       auto tuple_e = Expression::MakeTupleLiteral(e->line_num, new_args);
       auto tuple_t = Value::MakeTupleValue(std::move(arg_types));
-      return TCResult(tuple_e, tuple_t, new_types);
+      return TCExpression(tuple_e, tuple_t, new_types);
     }
     case ExpressionKind::FieldAccessExpression: {
-      auto res = TypeCheckExp(e->GetFieldAccessExpression().aggregate, types,
-                              values, nullptr, TCContext::ValueContext);
+      auto res =
+          TypeCheckExp(e->GetFieldAccessExpression().aggregate, types, values);
       auto t = res.type;
       switch (t->tag()) {
         case ValKind::StructType:
@@ -346,7 +290,7 @@ auto TypeCheckExp(const Expression* e, TypeEnv types, Env values,
             if (e->GetFieldAccessExpression().field == field.first) {
               const Expression* new_e = Expression::MakeFieldAccessExpression(
                   e->line_num, res.exp, e->GetFieldAccessExpression().field);
-              return TCResult(new_e, field.second, res.types);
+              return TCExpression(new_e, field.second, res.types);
             }
           }
           // Search for a method
@@ -354,7 +298,7 @@ auto TypeCheckExp(const Expression* e, TypeEnv types, Env values,
             if (e->GetFieldAccessExpression().field == method.first) {
               const Expression* new_e = Expression::MakeFieldAccessExpression(
                   e->line_num, res.exp, e->GetFieldAccessExpression().field);
-              return TCResult(new_e, method.second, res.types);
+              return TCExpression(new_e, method.second, res.types);
             }
           }
           FATAL_COMPILATION_ERROR(e->line_num)
@@ -366,7 +310,7 @@ auto TypeCheckExp(const Expression* e, TypeEnv types, Env values,
             if (e->GetFieldAccessExpression().field == field.name) {
               auto new_e = Expression::MakeFieldAccessExpression(
                   e->line_num, res.exp, e->GetFieldAccessExpression().field);
-              return TCResult(new_e, field.value, res.types);
+              return TCExpression(new_e, field.value, res.types);
             }
           }
           FATAL_COMPILATION_ERROR(e->line_num)
@@ -380,7 +324,7 @@ auto TypeCheckExp(const Expression* e, TypeEnv types, Env values,
               const Expression* new_e = Expression::MakeFieldAccessExpression(
                   e->line_num, res.exp, e->GetFieldAccessExpression().field);
               auto fun_ty = Value::MakeFunctionType({}, vt->second, t);
-              return TCResult(new_e, fun_ty, res.types);
+              return TCExpression(new_e, fun_ty, res.types);
             }
           }
           FATAL_COMPILATION_ERROR(e->line_num)
@@ -398,24 +342,23 @@ auto TypeCheckExp(const Expression* e, TypeEnv types, Env values,
       std::optional<const Value*> type =
           types.Get(e->GetIdentifierExpression().name);
       if (type) {
-        return TCResult(e, *type, types);
+        return TCExpression(e, *type, types);
       } else {
         FATAL_COMPILATION_ERROR(e->line_num)
             << "could not find `" << e->GetIdentifierExpression().name << "`";
       }
     }
     case ExpressionKind::IntLiteral:
-      return TCResult(e, Value::MakeIntType(), types);
+      return TCExpression(e, Value::MakeIntType(), types);
     case ExpressionKind::BoolLiteral:
-      return TCResult(e, Value::MakeBoolType(), types);
+      return TCExpression(e, Value::MakeBoolType(), types);
     case ExpressionKind::PrimitiveOperatorExpression: {
       std::vector<const Expression*> es;
       std::vector<const Value*> ts;
       auto new_types = types;
       for (const Expression* argument :
            e->GetPrimitiveOperatorExpression().arguments) {
-        auto res = TypeCheckExp(argument, types, values, nullptr,
-                                TCContext::ValueContext);
+        auto res = TypeCheckExp(argument, types, values);
         new_types = res.types;
         es.push_back(res.exp);
         ts.push_back(res.type);
@@ -425,55 +368,54 @@ auto TypeCheckExp(const Expression* e, TypeEnv types, Env values,
       switch (e->GetPrimitiveOperatorExpression().op) {
         case Operator::Neg:
           ExpectType(e->line_num, "negation", Value::MakeIntType(), ts[0]);
-          return TCResult(new_e, Value::MakeIntType(), new_types);
+          return TCExpression(new_e, Value::MakeIntType(), new_types);
         case Operator::Add:
           ExpectType(e->line_num, "addition(1)", Value::MakeIntType(), ts[0]);
           ExpectType(e->line_num, "addition(2)", Value::MakeIntType(), ts[1]);
-          return TCResult(new_e, Value::MakeIntType(), new_types);
+          return TCExpression(new_e, Value::MakeIntType(), new_types);
         case Operator::Sub:
           ExpectType(e->line_num, "subtraction(1)", Value::MakeIntType(),
                      ts[0]);
           ExpectType(e->line_num, "subtraction(2)", Value::MakeIntType(),
                      ts[1]);
-          return TCResult(new_e, Value::MakeIntType(), new_types);
+          return TCExpression(new_e, Value::MakeIntType(), new_types);
         case Operator::Mul:
           ExpectType(e->line_num, "multiplication(1)", Value::MakeIntType(),
                      ts[0]);
           ExpectType(e->line_num, "multiplication(2)", Value::MakeIntType(),
                      ts[1]);
-          return TCResult(new_e, Value::MakeIntType(), new_types);
+          return TCExpression(new_e, Value::MakeIntType(), new_types);
         case Operator::And:
           ExpectType(e->line_num, "&&(1)", Value::MakeBoolType(), ts[0]);
           ExpectType(e->line_num, "&&(2)", Value::MakeBoolType(), ts[1]);
-          return TCResult(new_e, Value::MakeBoolType(), new_types);
+          return TCExpression(new_e, Value::MakeBoolType(), new_types);
         case Operator::Or:
           ExpectType(e->line_num, "||(1)", Value::MakeBoolType(), ts[0]);
           ExpectType(e->line_num, "||(2)", Value::MakeBoolType(), ts[1]);
-          return TCResult(new_e, Value::MakeBoolType(), new_types);
+          return TCExpression(new_e, Value::MakeBoolType(), new_types);
         case Operator::Not:
           ExpectType(e->line_num, "!", Value::MakeBoolType(), ts[0]);
-          return TCResult(new_e, Value::MakeBoolType(), new_types);
+          return TCExpression(new_e, Value::MakeBoolType(), new_types);
         case Operator::Eq:
           ExpectType(e->line_num, "==", ts[0], ts[1]);
-          return TCResult(new_e, Value::MakeBoolType(), new_types);
+          return TCExpression(new_e, Value::MakeBoolType(), new_types);
         case Operator::Deref:
           ExpectPointerType(e->line_num, "*", ts[0]);
-          return TCResult(new_e, ts[0]->GetPointerType().type, new_types);
+          return TCExpression(new_e, ts[0]->GetPointerType().type, new_types);
         case Operator::Ptr:
           ExpectType(e->line_num, "*", Value::MakeTypeType(), ts[0]);
-          return TCResult(new_e, Value::MakeTypeType(), new_types);
+          return TCExpression(new_e, Value::MakeTypeType(), new_types);
       }
       break;
     }
     case ExpressionKind::CallExpression: {
-      auto fun_res = TypeCheckExp(e->GetCallExpression().function, types,
-                                  values, nullptr, TCContext::ValueContext);
+      auto fun_res =
+          TypeCheckExp(e->GetCallExpression().function, types, values);
       switch (fun_res.type->tag()) {
         case ValKind::FunctionType: {
           auto fun_t = fun_res.type;
-          auto arg_res =
-              TypeCheckExp(e->GetCallExpression().argument, fun_res.types,
-                           values, fun_t->GetFunctionType().param, context);
+          auto arg_res = TypeCheckExp(e->GetCallExpression().argument,
+                                      fun_res.types, values);
           auto parameter_type = fun_t->GetFunctionType().param;
           auto return_type = fun_t->GetFunctionType().ret;
           if (fun_t->GetFunctionType().deduced.size() > 0) {
@@ -497,7 +439,7 @@ auto TypeCheckExp(const Expression* e, TypeEnv types, Env values,
           }
           auto new_e = Expression::MakeCallExpression(e->line_num, fun_res.exp,
                                                       arg_res.exp);
-          return TCResult(new_e, return_type, arg_res.types);
+          return TCExpression(new_e, return_type, arg_res.types);
         }
         default: {
           FATAL_COMPILATION_ERROR(e->line_num)
@@ -508,48 +450,157 @@ auto TypeCheckExp(const Expression* e, TypeEnv types, Env values,
       break;
     }
     case ExpressionKind::FunctionTypeLiteral: {
-      switch (context) {
-        case TCContext::ValueContext:
-        case TCContext::TypeContext: {
-          auto pt = InterpExp(values, e->GetFunctionTypeLiteral().parameter);
-          auto rt = InterpExp(values, e->GetFunctionTypeLiteral().return_type);
-          auto new_e = Expression::MakeFunctionTypeLiteral(
-              e->line_num, ReifyType(pt, e->line_num),
-              ReifyType(rt, e->line_num));
-          return TCResult(new_e, Value::MakeTypeType(), types);
-        }
-        case TCContext::PatternContext: {
-          auto param_res = TypeCheckExp(e->GetFunctionTypeLiteral().parameter,
-                                        types, values, nullptr, context);
-          auto ret_res =
-              TypeCheckExp(e->GetFunctionTypeLiteral().return_type,
-                           param_res.types, values, nullptr, context);
-          auto new_e = Expression::MakeFunctionTypeLiteral(
-              e->line_num, ReifyType(param_res.type, e->line_num),
-              ReifyType(ret_res.type, e->line_num));
-          return TCResult(new_e, Value::MakeTypeType(), ret_res.types);
-        }
-      }
+      auto pt = InterpExp(values, e->GetFunctionTypeLiteral().parameter);
+      auto rt = InterpExp(values, e->GetFunctionTypeLiteral().return_type);
+      auto new_e = Expression::MakeFunctionTypeLiteral(
+          e->line_num, ReifyType(pt, e->line_num), ReifyType(rt, e->line_num));
+      return TCExpression(new_e, Value::MakeTypeType(), types);
     }
     case ExpressionKind::IntTypeLiteral:
-      return TCResult(e, Value::MakeTypeType(), types);
+      return TCExpression(e, Value::MakeTypeType(), types);
     case ExpressionKind::BoolTypeLiteral:
-      return TCResult(e, Value::MakeTypeType(), types);
+      return TCExpression(e, Value::MakeTypeType(), types);
     case ExpressionKind::TypeTypeLiteral:
-      return TCResult(e, Value::MakeTypeType(), types);
-    case ExpressionKind::AutoTypeLiteral:
-      return TCResult(e, Value::MakeTypeType(), types);
+      return TCExpression(e, Value::MakeTypeType(), types);
     case ExpressionKind::ContinuationTypeLiteral:
-      return TCResult(e, Value::MakeTypeType(), types);
+      return TCExpression(e, Value::MakeTypeType(), types);
   }
 }
 
-auto TypecheckCase(const Value* expected, const Expression* pat,
+// Equivalent to TypeCheckExp, but operates on Patterns instead of Expressions.
+// `expected` is the type that this pattern is expected to have, if the
+// surrounding context gives us that information. Otherwise, it is null.
+auto TypeCheckPattern(const Pattern* p, TypeEnv types, Env values,
+                      const Value* expected) -> TCPattern {
+  if (tracing_output) {
+    llvm::outs() << "checking pattern, ";
+    if (expected) {
+      llvm::outs() << "expecting " << *expected;
+    }
+    llvm::outs() << ", " << *p << "\n";
+  }
+  switch (p->Tag()) {
+    case Pattern::Kind::AutoPattern: {
+      return {.pattern = p, .type = Value::MakeTypeType(), .types = types};
+    }
+    case Pattern::Kind::BindingPattern: {
+      const auto& binding = cast<BindingPattern>(*p);
+      const Value* type;
+      switch (binding.Type()->Tag()) {
+        case Pattern::Kind::AutoPattern: {
+          if (expected == nullptr) {
+            FATAL_COMPILATION_ERROR(binding.LineNumber())
+                << "auto not allowed here";
+          } else {
+            type = expected;
+          }
+          break;
+        }
+        case Pattern::Kind::ExpressionPattern: {
+          type = InterpExp(
+              values, cast<ExpressionPattern>(binding.Type())->Expression());
+          CHECK(type->tag() != ValKind::AutoType);
+          if (expected != nullptr) {
+            ExpectType(binding.LineNumber(), "pattern variable", type,
+                       expected);
+          }
+          break;
+        }
+        case Pattern::Kind::TuplePattern:
+        case Pattern::Kind::BindingPattern:
+        case Pattern::Kind::AlternativePattern:
+          FATAL_COMPILATION_ERROR(binding.LineNumber())
+              << "Unsupported type pattern";
+      }
+      auto new_p = new BindingPattern(
+          binding.LineNumber(), binding.Name(),
+          new ExpressionPattern(ReifyType(type, binding.LineNumber())));
+      if (binding.Name().has_value()) {
+        types.Set(*binding.Name(), type);
+      }
+      return {.pattern = new_p, .type = type, .types = types};
+    }
+    case Pattern::Kind::TuplePattern: {
+      const auto& tuple = cast<TuplePattern>(*p);
+      std::vector<TuplePattern::Field> new_fields;
+      std::vector<TupleElement> field_types;
+      auto new_types = types;
+      if (expected && expected->tag() != ValKind::TupleValue) {
+        FATAL_COMPILATION_ERROR(p->LineNumber()) << "didn't expect a tuple";
+      }
+      if (expected &&
+          tuple.Fields().size() != expected->GetTupleValue().elements.size()) {
+        FATAL_COMPILATION_ERROR(tuple.LineNumber())
+            << "tuples of different length";
+      }
+      for (size_t i = 0; i < tuple.Fields().size(); ++i) {
+        const TuplePattern::Field& field = tuple.Fields()[i];
+        const Value* expected_field_type = nullptr;
+        if (expected != nullptr) {
+          const TupleElement& expected_element =
+              expected->GetTupleValue().elements[i];
+          if (expected_element.name != field.name) {
+            FATAL_COMPILATION_ERROR(tuple.LineNumber())
+                << "field names do not match, expected "
+                << expected_element.name << " but got " << field.name;
+          }
+          expected_field_type = expected_element.value;
+        }
+        auto field_result = TypeCheckPattern(field.pattern, new_types, values,
+                                             expected_field_type);
+        new_types = field_result.types;
+        new_fields.push_back(
+            TuplePattern::Field(field.name, field_result.pattern));
+        field_types.push_back({.name = field.name, .value = field_result.type});
+      }
+      auto new_tuple = new TuplePattern(tuple.LineNumber(), new_fields);
+      auto tuple_t = Value::MakeTupleValue(std::move(field_types));
+      return {.pattern = new_tuple, .type = tuple_t, .types = new_types};
+    }
+    case Pattern::Kind::AlternativePattern: {
+      const auto& alternative = cast<AlternativePattern>(*p);
+      const Value* choice_type = InterpExp(values, alternative.ChoiceType());
+      if (choice_type->tag() != ValKind::ChoiceType) {
+        FATAL_COMPILATION_ERROR(alternative.LineNumber())
+            << "alternative pattern does not name a choice type.";
+      }
+      if (expected != nullptr) {
+        ExpectType(alternative.LineNumber(), "alternative pattern", expected,
+                   choice_type);
+      }
+      const Value* parameter_types =
+          FindInVarValues(alternative.AlternativeName(),
+                          choice_type->GetChoiceType().alternatives);
+      if (parameter_types == nullptr) {
+        FATAL_COMPILATION_ERROR(alternative.LineNumber())
+            << "'" << alternative.AlternativeName()
+            << "' is not an alternative of " << choice_type;
+      }
+      TCPattern arg_results = TypeCheckPattern(alternative.Arguments(), types,
+                                               values, parameter_types);
+      return {.pattern = new AlternativePattern(
+                  alternative.LineNumber(),
+                  ReifyType(choice_type, alternative.LineNumber()),
+                  alternative.AlternativeName(),
+                  cast<TuplePattern>(arg_results.pattern)),
+              .type = choice_type,
+              .types = arg_results.types};
+    }
+    case Pattern::Kind::ExpressionPattern: {
+      TCExpression result =
+          TypeCheckExp(cast<ExpressionPattern>(p)->Expression(), types, values);
+      return {.pattern = new ExpressionPattern(result.exp),
+              .type = result.type,
+              .types = result.types};
+    }
+  }
+}
+
+auto TypecheckCase(const Value* expected, const Pattern* pat,
                    const Statement* body, TypeEnv types, Env values,
                    const Value*& ret_type)
-    -> std::pair<const Expression*, const Statement*> {
-  auto pat_res =
-      TypeCheckExp(pat, types, values, expected, TCContext::PatternContext);
+    -> std::pair<const Pattern*, const Statement*> {
+  auto pat_res = TypeCheckPattern(pat, types, values, expected);
   auto res = TypeCheckStmt(body, pat_res.types, values, ret_type);
   return std::make_pair(pat, res.stmt);
 }
@@ -568,11 +619,10 @@ auto TypeCheckStmt(const Statement* s, TypeEnv types, Env values,
   }
   switch (s->tag()) {
     case StatementKind::Match: {
-      auto res = TypeCheckExp(s->GetMatch().exp, types, values, nullptr,
-                              TCContext::ValueContext);
+      auto res = TypeCheckExp(s->GetMatch().exp, types, values);
       auto res_type = res.type;
       auto new_clauses =
-          new std::list<std::pair<const Expression*, const Statement*>>();
+          new std::list<std::pair<const Pattern*, const Statement*>>();
       for (auto& clause : *s->GetMatch().clauses) {
         new_clauses->push_back(TypecheckCase(
             res_type, clause.first, clause.second, types, values, ret_type));
@@ -582,8 +632,7 @@ auto TypeCheckStmt(const Statement* s, TypeEnv types, Env values,
       return TCStatement(new_s, types);
     }
     case StatementKind::While: {
-      auto cnd_res = TypeCheckExp(s->GetWhile().cond, types, values, nullptr,
-                                  TCContext::ValueContext);
+      auto cnd_res = TypeCheckExp(s->GetWhile().cond, types, values);
       ExpectType(s->line_num, "condition of `while`", Value::MakeBoolType(),
                  cnd_res.type);
       auto body_res =
@@ -602,11 +651,10 @@ auto TypeCheckStmt(const Statement* s, TypeEnv types, Env values,
                          types);
     }
     case StatementKind::VariableDefinition: {
-      auto res = TypeCheckExp(s->GetVariableDefinition().init, types, values,
-                              nullptr, TCContext::ValueContext);
+      auto res = TypeCheckExp(s->GetVariableDefinition().init, types, values);
       const Value* rhs_ty = res.type;
-      auto lhs_res = TypeCheckExp(s->GetVariableDefinition().pat, types, values,
-                                  rhs_ty, TCContext::PatternContext);
+      auto lhs_res = TypeCheckPattern(s->GetVariableDefinition().pat, types,
+                                      values, rhs_ty);
       const Statement* new_s = Statement::MakeVariableDefinition(
           s->line_num, s->GetVariableDefinition().pat, res.exp);
       return TCStatement(new_s, lhs_res.types);
@@ -623,25 +671,21 @@ auto TypeCheckStmt(const Statement* s, TypeEnv types, Env values,
           types3);
     }
     case StatementKind::Assign: {
-      auto rhs_res = TypeCheckExp(s->GetAssign().rhs, types, values, nullptr,
-                                  TCContext::ValueContext);
+      auto rhs_res = TypeCheckExp(s->GetAssign().rhs, types, values);
       auto rhs_t = rhs_res.type;
-      auto lhs_res = TypeCheckExp(s->GetAssign().lhs, types, values, rhs_t,
-                                  TCContext::ValueContext);
+      auto lhs_res = TypeCheckExp(s->GetAssign().lhs, types, values);
       auto lhs_t = lhs_res.type;
       ExpectType(s->line_num, "assign", lhs_t, rhs_t);
       auto new_s = Statement::MakeAssign(s->line_num, lhs_res.exp, rhs_res.exp);
       return TCStatement(new_s, lhs_res.types);
     }
     case StatementKind::ExpressionStatement: {
-      auto res = TypeCheckExp(s->GetExpressionStatement().exp, types, values,
-                              nullptr, TCContext::ValueContext);
+      auto res = TypeCheckExp(s->GetExpressionStatement().exp, types, values);
       auto new_s = Statement::MakeExpressionStatement(s->line_num, res.exp);
       return TCStatement(new_s, types);
     }
     case StatementKind::If: {
-      auto cnd_res = TypeCheckExp(s->GetIf().cond, types, values, nullptr,
-                                  TCContext::ValueContext);
+      auto cnd_res = TypeCheckExp(s->GetIf().cond, types, values);
       ExpectType(s->line_num, "condition of `if`", Value::MakeBoolType(),
                  cnd_res.type);
       auto thn_res =
@@ -653,8 +697,7 @@ auto TypeCheckStmt(const Statement* s, TypeEnv types, Env values,
       return TCStatement(new_s, types);
     }
     case StatementKind::Return: {
-      auto res = TypeCheckExp(s->GetReturn().exp, types, values, nullptr,
-                              TCContext::ValueContext);
+      auto res = TypeCheckExp(s->GetReturn().exp, types, values);
       if (ret_type->tag() == ValKind::AutoType) {
         // The following infers the return type from the first 'return'
         // statement. This will get more difficult with subtyping, when we
@@ -676,9 +719,8 @@ auto TypeCheckStmt(const Statement* s, TypeEnv types, Env values,
       return TCStatement(new_continuation, types);
     }
     case StatementKind::Run: {
-      TCResult argument_result =
-          TypeCheckExp(s->GetRun().argument, types, values, nullptr,
-                       TCContext::ValueContext);
+      TCExpression argument_result =
+          TypeCheckExp(s->GetRun().argument, types, values);
       ExpectType(s->line_num, "argument of `run`",
                  Value::MakeContinuationType(), argument_result.type);
       const Statement* new_run =
@@ -706,7 +748,7 @@ auto CheckOrEnsureReturn(const Statement* stmt, bool void_return, int line_num)
   switch (stmt->tag()) {
     case StatementKind::Match: {
       auto new_clauses =
-          new std::list<std::pair<const Expression*, const Statement*>>();
+          new std::list<std::pair<const Pattern*, const Statement*>>();
       for (auto i = stmt->GetMatch().clauses->begin();
            i != stmt->GetMatch().clauses->end(); ++i) {
         auto s = CheckOrEnsureReturn(i->second, void_return, stmt->line_num);
@@ -774,10 +816,9 @@ auto TypeCheckFunDef(const FunctionDefinition* f, TypeEnv types, Env values)
     values.Set(deduced.name, a);
   }
   // Type check the parameter pattern
-  auto param_res = TypeCheckExp(f->param_pattern, types, values, nullptr,
-                                TCContext::PatternContext);
+  auto param_res = TypeCheckPattern(f->param_pattern, types, values, nullptr);
   // Evaluate the return type expression
-  auto return_type = InterpExp(values, f->return_type);
+  auto return_type = InterpPattern(values, f->return_type);
   if (f->name == "main") {
     ExpectType(f->line_num, "return type of `main`", Value::MakeIntType(),
                return_type);
@@ -786,9 +827,9 @@ auto TypeCheckFunDef(const FunctionDefinition* f, TypeEnv types, Env values)
   auto res = TypeCheckStmt(f->body, param_res.types, values, return_type);
   bool void_return = TypeEqual(return_type, Value::MakeUnitTypeVal());
   auto body = CheckOrEnsureReturn(res.stmt, void_return, f->line_num);
-  return new FunctionDefinition(f->line_num, f->name, f->deduced_parameters,
-                                f->param_pattern,
-                                ReifyType(return_type, f->line_num), body);
+  return new FunctionDefinition(
+      f->line_num, f->name, f->deduced_parameters, f->param_pattern,
+      new ExpressionPattern(ReifyType(return_type, f->line_num)), body);
 }
 
 auto TypeOfFunDef(TypeEnv types, Env values, const FunctionDefinition* fun_def)
@@ -801,13 +842,13 @@ auto TypeOfFunDef(TypeEnv types, Env values, const FunctionDefinition* fun_def)
     values.Set(deduced.name, a);
   }
   // Type check the parameter pattern
-  auto param_res = TypeCheckExp(fun_def->param_pattern, types, values, nullptr,
-                                TCContext::PatternContext);
+  auto param_res =
+      TypeCheckPattern(fun_def->param_pattern, types, values, nullptr);
   // Evaluate the return type expression
-  auto ret = InterpExp(values, fun_def->return_type);
+  auto ret = InterpPattern(values, fun_def->return_type);
   if (ret->tag() == ValKind::AutoType) {
     auto f = TypeCheckFunDef(fun_def, types, values);
-    ret = InterpExp(values, f->return_type);
+    ret = InterpPattern(values, f->return_type);
   }
   return Value::MakeFunctionType(fun_def->deduced_parameters, param_res.type,
                                  ret);
@@ -819,10 +860,22 @@ auto TypeOfStructDef(const StructDefinition* sd, TypeEnv /*types*/, Env ct_top)
   VarValues methods;
   for (const Member* m : sd->members) {
     switch (m->tag()) {
-      case MemberKind::FieldMember:
-        auto t = InterpExp(ct_top, m->GetFieldMember().type);
-        fields.push_back(std::make_pair(m->GetFieldMember().name, t));
+      case MemberKind::FieldMember: {
+        const BindingPattern* binding = m->GetFieldMember().binding;
+        if (!binding->Name().has_value()) {
+          FATAL_COMPILATION_ERROR(binding->LineNumber())
+              << "Struct members must have names";
+        }
+        const Expression* type_expression =
+            dyn_cast<ExpressionPattern>(binding->Type())->Expression();
+        if (type_expression == nullptr) {
+          FATAL_COMPILATION_ERROR(binding->LineNumber())
+              << "Struct members must have explicit types";
+        }
+        auto type = InterpExp(ct_top, type_expression);
+        fields.push_back(std::make_pair(*binding->Name(), type));
         break;
+      }
     }
   }
   return Value::MakeStructType(sd->name, std::move(fields), std::move(methods));
@@ -836,8 +889,14 @@ static auto GetName(const Declaration& d) -> const std::string& {
       return d.GetStructDeclaration().definition.name;
     case DeclarationKind::ChoiceDeclaration:
       return d.GetChoiceDeclaration().name;
-    case DeclarationKind::VariableDeclaration:
-      return d.GetVariableDeclaration().name;
+    case DeclarationKind::VariableDeclaration: {
+      const BindingPattern* binding = d.GetVariableDeclaration().binding;
+      if (!binding->Name().has_value()) {
+        FATAL_COMPILATION_ERROR(binding->LineNumber())
+            << "Top-level variable declarations must have names";
+      }
+      return *binding->Name();
+    }
   }
 }
 
@@ -872,9 +931,16 @@ auto MakeTypeChecked(const Declaration& d, const TypeEnv& types,
       // Signals a type error if the initializing expression does not have
       // the declared type of the variable, otherwise returns this
       // declaration with annotated types.
-      TCResult type_checked_initializer = TypeCheckExp(
-          var.initializer, types, values, nullptr, TCContext::ValueContext);
-      const Value* declared_type = InterpExp(values, var.type);
+      TCExpression type_checked_initializer =
+          TypeCheckExp(var.initializer, types, values);
+      const Expression* type =
+          dyn_cast<ExpressionPattern>(var.binding->Type())->Expression();
+      if (type == nullptr) {
+        // TODO: consider adding support for `auto`
+        FATAL_COMPILATION_ERROR(var.source_location)
+            << "Type of a top-level variable must be an expression.";
+      }
+      const Value* declared_type = InterpExp(values, type);
       ExpectType(var.source_location, "initializer of variable", declared_type,
                  type_checked_initializer.type);
       return d;
@@ -926,8 +992,10 @@ static void TopLevel(const Declaration& d, TypeCheckContext* tops) {
       const auto& var = d.GetVariableDeclaration();
       // Associate the variable name with it's declared type in the
       // compile-time symbol table.
-      const Value* declared_type = InterpExp(tops->values, var.type);
-      tops->types.Set(var.name, declared_type);
+      const Expression* type =
+          cast<ExpressionPattern>(var.binding->Type())->Expression();
+      const Value* declared_type = InterpExp(tops->values, type);
+      tops->types.Set(*var.binding->Name(), declared_type);
       break;
     }
   }

+ 12 - 6
executable_semantics/interpreter/typecheck.h

@@ -17,10 +17,8 @@ namespace Carbon {
 
 using TypeEnv = Dictionary<std::string, const Value*>;
 
-enum class TCContext { ValueContext, PatternContext, TypeContext };
-
-struct TCResult {
-  TCResult(const Expression* e, const Value* t, TypeEnv types)
+struct TCExpression {
+  TCExpression(const Expression* e, const Value* t, TypeEnv types)
       : exp(e), type(t), types(types) {}
 
   const Expression* exp;
@@ -28,6 +26,12 @@ struct TCResult {
   TypeEnv types;
 };
 
+struct TCPattern {
+  const Pattern* pattern;
+  const Value* type;
+  TypeEnv types;
+};
+
 struct TCStatement {
   TCStatement(const Statement* s, TypeEnv types) : stmt(s), types(types) {}
 
@@ -35,8 +39,10 @@ struct TCStatement {
   TypeEnv types;
 };
 
-auto TypeCheckExp(const Expression* e, TypeEnv types, Env values,
-                  const Value* expected, TCContext context) -> TCResult;
+auto TypeCheckExp(const Expression* e, TypeEnv types, Env values)
+    -> TCExpression;
+auto TypeCheckPattern(const Pattern* p, TypeEnv types, Env values,
+                      const Value* expected) -> TCPattern;
 
 auto TypeCheckStmt(const Statement*, TypeEnv, Env, Value const*&)
     -> TCStatement;

+ 0 - 16
executable_semantics/syntax/BUILD

@@ -42,23 +42,7 @@ cc_library(
 
 cc_library(
     name = "paren_contents",
-    srcs = ["paren_contents.cpp"],
     hdrs = ["paren_contents.h"],
-    deps = ["//executable_semantics/ast:expression"],
-)
-
-cc_test(
-    name = "paren_contents_test",
-    srcs = ["paren_contents_test.cpp"],
-    env = {
-        # TODO(#580): Remove this when leaks are fixed.
-        "ASAN_OPTIONS": "detect_leaks=0",
-    },
-    deps = [
-        ":paren_contents",
-        "@llvm-project//llvm:gtest",
-        "@llvm-project//llvm:gtest_main",
-    ],
 )
 
 genrule(

+ 0 - 22
executable_semantics/syntax/paren_contents.cpp

@@ -1,22 +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
-
-#include "executable_semantics/syntax/paren_contents.h"
-
-namespace Carbon {
-
-const Expression* ParenContents::AsExpression(int line_number) const {
-  if (fields_.size() == 1 && fields_.front().name == "" &&
-      has_trailing_comma_ == HasTrailingComma::No) {
-    return new Expression(*fields_.front().expression);
-  } else {
-    return AsTuple(line_number);
-  }
-}
-
-const Expression* ParenContents::AsTuple(int line_number) const {
-  return Expression::MakeTupleLiteral(line_number, fields_);
-}
-
-}  // namespace Carbon

+ 62 - 27
executable_semantics/syntax/paren_contents.h

@@ -5,46 +5,81 @@
 #ifndef EXECUTABLE_SEMANTICS_SYNTAX_PAREN_CONTENTS_H_
 #define EXECUTABLE_SEMANTICS_SYNTAX_PAREN_CONTENTS_H_
 
-#include <list>
+#include <optional>
+#include <string>
+#include <vector>
 
-#include "executable_semantics/ast/expression.h"
+#include "executable_semantics/common/error.h"
 
 namespace Carbon {
 
-// Represents the syntactic contents of an expression delimited by
-// parentheses. Such expressions can be interpreted as either tuples or
-// arbitrary expressions, depending on their context and the syntax of their
+// Represents the syntactic contents of an expression or pattern delimited by
+// parentheses. In those syntaxes, parentheses can be used either for grouping
+// or for forming a tuple, depending on their context and the syntax of their
 // contents; this class helps calling code resolve that ambiguity. Since that
 // ambiguity is purely syntactic, this class should only be needed during
 // parsing.
-class ParenContents {
- public:
-  // Indicates whether the paren expression's contents end with a comma.
-  enum class HasTrailingComma { Yes, No };
+//
+// `Term` is the type of the syntactic grouping being built, and the type of
+// the individual syntactic units it's built from; typically it should be
+// either `Expression` or `Pattern`.
+template <typename Term>
+struct ParenContents {
+  struct Element {
+    std::optional<std::string> name;
+    const Term* term;
+  };
 
-  // Constructs a ParenContents representing the contents of "()".
-  ParenContents() : fields_({}), has_trailing_comma_(HasTrailingComma::No) {}
+  // 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.
+  auto SingleTerm() const -> std::optional<const Term*>;
 
-  // Constructs a ParenContents representing the given list of fields,
-  // with or without a trailing comma.
-  ParenContents(std::vector<FieldInitializer> fields,
-                HasTrailingComma has_trailing_comma)
-      : fields_(fields), has_trailing_comma_(has_trailing_comma) {}
+  // Converts `elements` to std::vector<TupleElement>. TupleElement must
+  // have a constructor that takes a std::string and a const Term*.
+  //
+  // TODO: Find a way to deduce TupleElement from Term.
+  template <typename TupleElement>
+  auto TupleElements(int line_num) const -> std::vector<TupleElement>;
 
-  ParenContents(const ParenContents&) = default;
-  ParenContents& operator=(const ParenContents&) = default;
+  std::vector<Element> elements;
+  bool has_trailing_comma;
+};
 
-  // Returns the paren expression, interpreted as a tuple.
-  const Expression* AsTuple(int line_number) const;
+// Implementation details only below here.
 
-  // Returns the paren expression, with no external constraints on what kind
-  // of expression it represents.
-  const Expression* AsExpression(int line_number) const;
+template <typename Term>
+auto ParenContents<Term>::SingleTerm() const -> std::optional<const Term*> {
+  if (elements.size() == 1 && !elements.front().name.has_value() &&
+      !has_trailing_comma) {
+    return elements.front().term;
+  } else {
+    return std::nullopt;
+  }
+}
 
- private:
-  std::vector<FieldInitializer> fields_;
-  HasTrailingComma has_trailing_comma_;
-};
+template <typename Term>
+template <typename TupleElement>
+auto ParenContents<Term>::TupleElements(int line_num) 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_USER_ERROR(line_num)
+            << "positional members must come before named members";
+      }
+      result.push_back(TupleElement(std::to_string(i), element.term));
+    }
+    ++i;
+  }
+  return result;
+}
 
 }  // namespace Carbon
 

+ 0 - 114
executable_semantics/syntax/paren_contents_test.cpp

@@ -1,114 +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
-
-#include "executable_semantics/syntax/paren_contents.h"
-
-#include "gtest/gtest.h"
-
-namespace Carbon {
-namespace {
-
-TEST(ParenContentsTest, EmptyAsExpression) {
-  ParenContents contents;
-  const Expression* expression = contents.AsExpression(/*line_num=*/1);
-  EXPECT_EQ(expression->line_num, 1);
-  ASSERT_EQ(expression->tag(), ExpressionKind::TupleLiteral);
-  EXPECT_EQ(expression->GetTupleLiteral().fields.size(), 0);
-}
-
-TEST(ParenContentsTest, EmptyAsTuple) {
-  ParenContents contents;
-  const Expression* tuple = contents.AsTuple(/*line_num=*/1);
-  EXPECT_EQ(tuple->line_num, 1);
-  ASSERT_EQ(tuple->tag(), ExpressionKind::TupleLiteral);
-  EXPECT_EQ(tuple->GetTupleLiteral().fields.size(), 0);
-}
-
-TEST(ParenContentsTest, UnaryNoCommaAsExpression) {
-  // Equivalent to a code fragment like
-  // ```
-  // (
-  //   42
-  // )
-  // ```
-  ParenContents contents(
-      {{.expression = Expression::MakeIntLiteral(/*line_num=*/2, 42)}},
-      ParenContents::HasTrailingComma::No);
-
-  const Expression* expression = contents.AsExpression(/*line_num=*/1);
-  EXPECT_EQ(expression->line_num, 2);
-  ASSERT_EQ(expression->tag(), ExpressionKind::IntLiteral);
-}
-
-TEST(ParenContentsTest, UnaryNoCommaAsTuple) {
-  ParenContents contents(
-      {{.expression = Expression::MakeIntLiteral(/*line_num=*/2, 42)}},
-      ParenContents::HasTrailingComma::No);
-
-  const Expression* tuple = contents.AsTuple(/*line_num=*/1);
-  EXPECT_EQ(tuple->line_num, 1);
-  ASSERT_EQ(tuple->tag(), ExpressionKind::TupleLiteral);
-  std::vector<FieldInitializer> fields = tuple->GetTupleLiteral().fields;
-  ASSERT_EQ(fields.size(), 1);
-  EXPECT_EQ(fields[0].expression->tag(), ExpressionKind::IntLiteral);
-}
-
-TEST(ParenContentsTest, UnaryWithCommaAsExpression) {
-  ParenContents contents(
-      {{.expression = Expression::MakeIntLiteral(/*line_num=*/2, 42)}},
-      ParenContents::HasTrailingComma::Yes);
-
-  const Expression* expression = contents.AsExpression(/*line_num=*/1);
-  EXPECT_EQ(expression->line_num, 1);
-  ASSERT_EQ(expression->tag(), ExpressionKind::TupleLiteral);
-  std::vector<FieldInitializer> fields = expression->GetTupleLiteral().fields;
-  ASSERT_EQ(fields.size(), 1);
-  EXPECT_EQ(fields[0].expression->tag(), ExpressionKind::IntLiteral);
-}
-
-TEST(ParenContentsTest, UnaryWithCommaAsTuple) {
-  ParenContents contents(
-      {{.expression = Expression::MakeIntLiteral(/*line_num=*/2, 42)}},
-      ParenContents::HasTrailingComma::Yes);
-
-  const Expression* tuple = contents.AsTuple(/*line_num=*/1);
-  EXPECT_EQ(tuple->line_num, 1);
-  ASSERT_EQ(tuple->tag(), ExpressionKind::TupleLiteral);
-  std::vector<FieldInitializer> fields = tuple->GetTupleLiteral().fields;
-  ASSERT_EQ(fields.size(), 1);
-  EXPECT_EQ(fields[0].expression->tag(), ExpressionKind::IntLiteral);
-}
-
-TEST(ParenContentsTest, BinaryAsExpression) {
-  ParenContents contents(
-      {{.expression = Expression::MakeIntLiteral(/*line_num=*/2, 42)},
-       {.expression = Expression::MakeIntLiteral(/*line_num=*/3, 42)}},
-      ParenContents::HasTrailingComma::Yes);
-
-  const Expression* expression = contents.AsExpression(/*line_num=*/1);
-  EXPECT_EQ(expression->line_num, 1);
-  ASSERT_EQ(expression->tag(), ExpressionKind::TupleLiteral);
-  std::vector<FieldInitializer> fields = expression->GetTupleLiteral().fields;
-  ASSERT_EQ(fields.size(), 2);
-  EXPECT_EQ(fields[0].expression->tag(), ExpressionKind::IntLiteral);
-  EXPECT_EQ(fields[1].expression->tag(), ExpressionKind::IntLiteral);
-}
-
-TEST(ParenContentsTest, BinaryAsTuple) {
-  ParenContents contents(
-      {{.expression = Expression::MakeIntLiteral(/*line_num=*/2, 42)},
-       {.expression = Expression::MakeIntLiteral(/*line_num=*/3, 42)}},
-      ParenContents::HasTrailingComma::Yes);
-
-  const Expression* tuple = contents.AsTuple(/*line_num=*/1);
-  EXPECT_EQ(tuple->line_num, 1);
-  ASSERT_EQ(tuple->tag(), ExpressionKind::TupleLiteral);
-  std::vector<FieldInitializer> fields = tuple->GetTupleLiteral().fields;
-  ASSERT_EQ(fields.size(), 2);
-  EXPECT_EQ(fields[0].expression->tag(), ExpressionKind::IntLiteral);
-  EXPECT_EQ(fields[1].expression->tag(), ExpressionKind::IntLiteral);
-}
-
-}  // namespace
-}  // namespace Carbon

+ 138 - 60
executable_semantics/syntax/parser.ypp

@@ -49,6 +49,7 @@
 #include <cstdlib>
 #include <iostream>
 #include <list>
+#include <vector>
 
 #include "executable_semantics/syntax/syntax_helpers.h"
 #include "executable_semantics/syntax/parse_and_lex_context.h"
@@ -59,7 +60,9 @@
 
 #include "executable_semantics/ast/abstract_syntax_tree.h"
 #include "executable_semantics/ast/declaration.h"
+#include "executable_semantics/ast/expression.h"
 #include "executable_semantics/ast/function_definition.h"
+#include "executable_semantics/ast/pattern.h"
 #include "executable_semantics/syntax/paren_contents.h"
 
 namespace Carbon {
@@ -94,21 +97,28 @@ void yy::parser::error(const location_type&, const std::string& message) {
 %type <Carbon::GenericBinding> generic_binding
 %type <std::vector<Carbon::GenericBinding>> deduced_params
 %type <std::vector<Carbon::GenericBinding>> deduced_param_list
-%type <const Carbon::Expression*> pattern
+%type <const Carbon::Pattern*> pattern
+%type <const Carbon::Pattern*> non_expression_pattern
 %type <const Carbon::Expression*> return_type
 %type <const Carbon::Expression*> paren_expression
 %type <const Carbon::Expression*> tuple
 %type <std::optional<std::string>> binding_lhs
-%type <Carbon::Member*> variable_declaration
+%type <const Carbon::BindingPattern*> variable_declaration
 %type <Carbon::Member*> member
 %type <std::list<Carbon::Member*>> member_list
-%type <Carbon::FieldInitializer> field_initializer
-%type <Carbon::ParenContents> paren_contents
-%type <std::vector<Carbon::FieldInitializer>> paren_contents_without_trailing_comma
+%type <Carbon::ParenContents<Carbon::Expression>::Element> paren_expression_element
+%type <Carbon::ParenContents<Carbon::Expression>> paren_expression_base
+%type <Carbon::ParenContents<Carbon::Expression>> paren_expression_contents
+%type <const Carbon::Pattern*> paren_pattern
+%type <const Carbon::TuplePattern*> tuple_pattern
+%type <const Carbon::TuplePattern*> maybe_empty_tuple_pattern
+%type <Carbon::ParenContents<Carbon::Pattern>> paren_pattern_base
+%type <Carbon::ParenContents<Carbon::Pattern>::Element> paren_pattern_element
+%type <Carbon::ParenContents<Carbon::Pattern>> paren_pattern_contents
 %type <std::pair<std::string, const Carbon::Expression*>> alternative
 %type <std::list<std::pair<std::string, const Carbon::Expression*>>> alternative_list
-%type <std::pair<const Carbon::Expression*, const Carbon::Statement*>*> clause
-%type <std::list<std::pair<const Carbon::Expression*, const Carbon::Statement*>>*> clause_list
+%type <std::pair<const Carbon::Pattern*, const Carbon::Statement*>*> clause
+%type <std::list<std::pair<const Carbon::Pattern*, const Carbon::Statement*>>*> clause_list
 %token END_OF_FILE 0
 %token AND
 %token OR
@@ -192,14 +202,6 @@ void yy::parser::error(const location_type&, const std::string& message) {
 input: declaration_list
     { parsed_program = $1; }
 ;
-pattern:
-  expression
-    { $$ = $1; }
-;
-binding_lhs:
-  identifier { $$ = $1; }
-| UNDERSCORE { $$ = std::nullopt; }
-;
 expression:
   identifier
     { $$ = Carbon::Expression::MakeIdentifierExpression(yylineno, $1); }
@@ -207,10 +209,6 @@ expression:
     { $$ = Carbon::Expression::MakeFieldAccessExpression(yylineno, $1, $2); }
 | expression "[" expression "]"
     { $$ = Carbon::Expression::MakeIndexExpression(yylineno, $1, $3); }
-| binding_lhs ":" expression
-    {
-      $$ = Carbon::Expression::MakeBindingExpression(yylineno, $1, $3);
-    }
 | integer_literal
     { $$ = Carbon::Expression::MakeIntLiteral(yylineno, $1); }
 | TRUE
@@ -223,8 +221,6 @@ expression:
     { $$ = Carbon::Expression::MakeBoolTypeLiteral(yylineno); }
 | TYPE
     { $$ = Carbon::Expression::MakeTypeTypeLiteral(yylineno); }
-| AUTO
-    { $$ = Carbon::Expression::MakeAutoTypeLiteral(yylineno); }
 | CONTINUATION_TYPE
     { $$ = Carbon::Expression::MakeContinuationTypeLiteral(yylineno); }
 | paren_expression { $$ = $1; }
@@ -271,55 +267,133 @@ expression:
 ;
 designator: "." identifier { $$ = $2; }
 ;
-paren_expression: "(" paren_contents ")"
-    { $$ = $2.AsExpression(yylineno); }
+paren_expression: paren_expression_base
+    { $$ = Carbon::ExpressionFromParenContents(yylineno, $1); }
 ;
-tuple: "(" paren_contents ")"
-    { $$ = $2.AsTuple(yylineno); }
+tuple: paren_expression_base
+    { $$ = Carbon::TupleExpressionFromParenContents(yylineno, $1); }
 ;
-field_initializer:
-  pattern
-    { $$ = Carbon::FieldInitializer({"", $1}); }
-| designator "=" pattern
-    { $$ = Carbon::FieldInitializer({$1, $3}); }
+paren_expression_element:
+  expression
+    { $$ = {.name = std::nullopt, .term = $1}; }
+| designator "=" expression
+    { $$ = {.name = $1, .term = $3}; }
 ;
-paren_contents:
-  // Empty
-    { $$ = Carbon::ParenContents(); }
-| paren_contents_without_trailing_comma
+paren_expression_base:
+  "(" ")"
+    { $$ = {.elements = {}, .has_trailing_comma = false}; }
+| "(" paren_expression_contents ")"
+    { $$ = $2; }
+| "(" paren_expression_contents "," ")"
     {
-      $$ = Carbon::ParenContents($1,
-                                 Carbon::ParenContents::HasTrailingComma::No);
+      $$ = $2;
+      $$.has_trailing_comma = true;
     }
-| paren_contents_without_trailing_comma ","
+;
+paren_expression_contents:
+  paren_expression_element
+    { $$ = {.elements = {$1}, .has_trailing_comma = false}; }
+| paren_expression_contents "," paren_expression_element
     {
-      $$ = Carbon::ParenContents($1,
-                                 Carbon::ParenContents::HasTrailingComma::Yes);
+      $$ = $1;
+      $$.elements.push_back($3);
     }
 ;
-paren_contents_without_trailing_comma:
-  field_initializer
-    { $$ = {$1}; }
-| paren_contents_without_trailing_comma "," field_initializer
+
+// In many cases, using `pattern` recursively will result in ambiguities.
+// When that happens, it's necessary to factor out two separate productions,
+// one for when the sub-pattern is an expression, and one for when it is not.
+// To facilitate this, non-terminals besides `pattern` whose names contain
+// `pattern` are structured to be disjoint from `expression`, unless otherwise
+// specified.
+pattern:
+  non_expression_pattern
+    { $$ = $1; }
+| expression
+    { $$ = new Carbon::ExpressionPattern($1); }
+;
+non_expression_pattern:
+  AUTO
+    { $$ = new Carbon::AutoPattern(yylineno); }
+| binding_lhs ":" pattern
+    { $$ = new Carbon::BindingPattern(yylineno, $1, $3); }
+| paren_pattern
+    { $$ = $1; }
+| expression tuple_pattern
+    { $$ = new Carbon::AlternativePattern(yylineno, $1, $2); }
+;
+binding_lhs:
+  identifier { $$ = $1; }
+| UNDERSCORE { $$ = std::nullopt; }
+;
+paren_pattern: paren_pattern_base
+    { $$ = Carbon::PatternFromParenContents(yylineno, $1); }
+;
+paren_pattern_base:
+  "(" paren_pattern_contents ")"
+    { $$ = $2; }
+| "(" paren_pattern_contents "," ")"
+    {
+      $$ = $2;
+      $$.has_trailing_comma = true;
+    }
+;
+// paren_pattern is analogous to paren_expression, but in order to avoid
+// ambiguities, it must be disjoint from paren_expression, meaning it must
+// contain at least one non_expression_pattern. The structure of this rule
+// is very different from the corresponding expression rule because is has to
+// enforce that requirement.
+paren_pattern_contents:
+  paren_pattern_element
+    { $$ = {.elements = {$1}, .has_trailing_comma = false }; }
+| paren_expression_contents "," paren_pattern_element
+    {
+      $$ = Carbon::ParenExpressionToParenPattern($1);
+      $$.elements.push_back($3);
+    }
+| paren_pattern_contents "," paren_expression_element
     {
       $$ = $1;
-      $$.push_back($3);
+      $$.elements.push_back({.name = $3.name, .term = new Carbon::ExpressionPattern($3.term)});
     }
+| paren_pattern_contents "," paren_pattern_element
+    {
+      $$ = $1;
+      $$.elements.push_back($3);
+    }
+;
+paren_pattern_element:
+  non_expression_pattern
+    { $$ = {.name = std::nullopt, .term = $1}; }
+| designator "=" non_expression_pattern
+    { $$ = {.name = $1, .term = $3}; }
+;
+tuple_pattern: paren_pattern_base
+    { $$ = Carbon::TuplePatternFromParenContents(yylineno, $1); }
+;
+// Unlike most `pattern` nonterminals, this one overlaps with `expression`,
+// so it should be used only when prior context (such as an introducer)
+// rules out the possibility of an `expression` at this point.
+maybe_empty_tuple_pattern:
+  "(" ")"
+    { $$ = new Carbon::TuplePattern(yylineno, {}); }
+| tuple_pattern
+    { $$ = $1; }
 ;
 clause:
   CASE pattern DBLARROW statement
-    { $$ = new std::pair<const Carbon::Expression*, const Carbon::Statement*>($2, $4); }
+    { $$ = new std::pair<const Carbon::Pattern*, const Carbon::Statement*>($2, $4); }
 | DEFAULT DBLARROW statement
     {
-      auto vp = Carbon::Expression::MakeBindingExpression(
-          yylineno, "_", Carbon::Expression::MakeAutoTypeLiteral(yylineno));
-      $$ = new std::pair<const Carbon::Expression*, const Carbon::Statement*>(vp, $3);
+      auto vp = new Carbon::BindingPattern(
+          yylineno, std::nullopt, new Carbon::AutoPattern(yylineno));
+      $$ = new std::pair<const Carbon::Pattern*, const Carbon::Statement*>(vp, $3);
     }
 ;
 clause_list:
   // Empty
     {
-      $$ = new std::list<std::pair<const Carbon::Expression*, const Carbon::Statement*>>();
+      $$ = new std::list<std::pair<const Carbon::Pattern*, const Carbon::Statement*>>();
     }
 | clause clause_list
     { $$ = $2; $$->push_front(*$1); }
@@ -407,25 +481,30 @@ deduced_params:
     { $$ = $2; }
 ;
 function_definition:
-  FN identifier deduced_params tuple return_type block
-    { $$ = Carbon::FunctionDefinition(yylineno, $2, $3, $4, $5, $6); }
-| FN identifier deduced_params tuple DBLARROW expression ";"
+  FN identifier deduced_params maybe_empty_tuple_pattern return_type block
+    {
+      $$ = Carbon::FunctionDefinition(
+        yylineno, $2, $3, $4, new Carbon::ExpressionPattern($5), $6);
+    }
+| FN identifier deduced_params maybe_empty_tuple_pattern DBLARROW expression ";"
     {
       $$ = Carbon::FunctionDefinition(
                yylineno, $2, $3, $4,
-               Carbon::Expression::MakeAutoTypeLiteral(yylineno),
+               new Carbon::AutoPattern(yylineno),
                Carbon::Statement::MakeReturn(yylineno, $6));
     }
 ;
 function_declaration:
-  FN identifier  deduced_params tuple return_type ";"
-    { $$ = Carbon::FunctionDefinition(yylineno, $2, $3, $4, $5, 0); }
+  FN identifier deduced_params maybe_empty_tuple_pattern return_type ";"
+    {
+      $$ = Carbon::FunctionDefinition(
+        yylineno, $2, $3, $4, new Carbon::ExpressionPattern($5), 0); }
 ;
-variable_declaration: identifier ":" expression
-    { $$ = Carbon::Member::MakeFieldMember(yylineno, $1, $3); }
+variable_declaration: identifier ":" pattern
+    { $$ = new Carbon::BindingPattern(yylineno, $1, $3); }
 ;
 member: VAR variable_declaration ";"
-    { $$ = $2; }
+    { $$ = Carbon::Member::MakeFieldMember(yylineno, $2); }
 ;
 member_list:
   // Empty
@@ -468,8 +547,7 @@ declaration:
     }
 | VAR variable_declaration "=" expression ";"
     {
-      $$ = Carbon::Declaration::MakeVariableDeclaration(
-          yylineno, $2->GetFieldMember().name, $2->GetFieldMember().type, $4);
+      $$ = Carbon::Declaration::MakeVariableDeclaration(yylineno, $2, $4);
     }
 ;
 declaration_list:

+ 0 - 1
executable_semantics/test_list.bzl

@@ -60,7 +60,6 @@ TEST_LIST = [
     "match_int",
     "match_int_default",
     "match_placeholder",
-    "match_type",
     "next",
     "no_match",
     "pattern_init",

+ 3 - 1
executable_semantics/testdata/fun6_fail_type.golden

@@ -1,2 +1,4 @@
-COMPILATION ERROR: 10: tuples of different length
+COMPILATION ERROR: 10: type error in call
+expected: (0 = Int, 1 = Int)
+actual: (0 = (0 = Int, 1 = Int))
 EXIT CODE: 255

+ 0 - 17
executable_semantics/testdata/match_type.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
-
-fn main() -> Int {
-  var t: auto = fnty (Int,Int);
-  var x: Int = 0;
-  match (t) {
-    case z: Type =>
-      x = x + 1;
-  }
-  match (t) {
-    case fnty(a: Type,b: Type) =>
-      x = x - 1;
-  }
-  return x;
-}

+ 0 - 1
executable_semantics/testdata/match_type.golden

@@ -1 +0,0 @@
-result: 0

+ 1 - 1
executable_semantics/testdata/pattern_variable_fail.golden

@@ -1,2 +1,2 @@
-COMPILATION ERROR: 7: pattern variables are only allowed in pattern context
+executable_semantics/testdata/pattern_variable_fail.carbon:7: syntax error, unexpected :
 EXIT CODE: 255