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

Adds basic support for class functions and methods. (#1057)

* adding methods to the ast

* pre commit stuff?

* implementation of class functions

* implemented methods

* some cleanup

* more cleanup

* add newlines in test programs

* pre-commit fixups

* added include of return_term.h

* a test of a method calling another method

* replacing Member with Declaration

* removing the member.h etc files

* clarify a type annotation

* update uses of FunctionDeclaration

* remove ReturnTarget, no longer needed

* more cleanup

* more cleanup

* yet more cleanup, playing with pre-commit

* did a pre-commit run --all-files

* fixed const issue

* remove comment

* checking dependencies in BUILD files and headers

* pre-commit working now

* refactor NominalClassType to just hold a pointer to the class declaration

* remove Member from rtti

* responding to Geoffreys review

* change field_types to a non-member function
Jeremy G. Siek 4 лет назад
Родитель
Сommit
ac0b810bf3
27 измененных файлов с 739 добавлено и 351 удалено
  1. 14 16
      executable_semantics/ast/BUILD
  2. 0 2
      executable_semantics/ast/ast_rtti.txt
  3. 3 3
      executable_semantics/ast/ast_test_matchers_test.cpp
  4. 31 2
      executable_semantics/ast/declaration.cpp
  5. 27 92
      executable_semantics/ast/declaration.h
  6. 0 25
      executable_semantics/ast/member.cpp
  7. 0 70
      executable_semantics/ast/member.h
  8. 1 0
      executable_semantics/ast/pattern.h
  9. 102 0
      executable_semantics/ast/return_term.h
  10. 2 0
      executable_semantics/ast/statement.h
  11. 2 1
      executable_semantics/interpreter/BUILD
  12. 9 1
      executable_semantics/interpreter/exec_program.cpp
  13. 30 6
      executable_semantics/interpreter/interpreter.cpp
  14. 25 8
      executable_semantics/interpreter/resolve_control_flow.cpp
  15. 9 26
      executable_semantics/interpreter/resolve_names.cpp
  16. 90 52
      executable_semantics/interpreter/type_checker.cpp
  17. 3 1
      executable_semantics/interpreter/type_checker.h
  18. 111 8
      executable_semantics/interpreter/value.cpp
  19. 42 14
      executable_semantics/interpreter/value.h
  20. 32 24
      executable_semantics/syntax/parser.ypp
  21. 29 0
      executable_semantics/testdata/class/bound_method.carbon
  22. 27 0
      executable_semantics/testdata/class/class_function.carbon
  23. 27 0
      executable_semantics/testdata/class/class_function_from_instance.carbon
  24. 27 0
      executable_semantics/testdata/class/class_function_value.carbon
  25. 30 0
      executable_semantics/testdata/class/fail_method_from_class.carbon
  26. 31 0
      executable_semantics/testdata/class/method.carbon
  27. 35 0
      executable_semantics/testdata/class/method_call_method.carbon

+ 14 - 16
executable_semantics/ast/BUILD

@@ -77,8 +77,8 @@ cc_library(
     ],
     deps = [
         ":ast_node",
-        ":member",
         ":pattern",
+        ":return_term",
         ":source_location",
         ":statement",
         ":static_scope",
@@ -89,6 +89,18 @@ cc_library(
     ],
 )
 
+cc_library(
+    name = "return_term",
+    hdrs = ["return_term.h"],
+    deps = [
+        ":source_location",
+        "//common:check",
+        "//common:ostream",
+        "//executable_semantics/common:nonnull",
+        "@llvm-project//llvm:Support",
+    ],
+)
+
 cc_library(
     name = "expression",
     srcs = ["expression.cpp"],
@@ -119,20 +131,6 @@ cc_test(
     ],
 )
 
-cc_library(
-    name = "member",
-    srcs = ["member.cpp"],
-    hdrs = ["member.h"],
-    deps = [
-        ":expression",
-        ":pattern",
-        ":source_location",
-        "//common:ostream",
-        "//executable_semantics/common:arena",
-        "@llvm-project//llvm:Support",
-    ],
-)
-
 cc_library(
     name = "library_name",
     hdrs = ["library_name.h"],
@@ -186,7 +184,6 @@ cc_library(
         ":source_location",
         ":value_category",
         "//common:check",
-        "//executable_semantics/common:arena",
         "//executable_semantics/common:error",
         "//executable_semantics/common:nonnull",
     ],
@@ -209,6 +206,7 @@ cc_library(
         ":ast_node",
         ":expression",
         ":pattern",
+        ":return_term",
         ":source_location",
         ":static_scope",
         ":value_category",

+ 0 - 2
executable_semantics/ast/ast_rtti.txt

@@ -50,5 +50,3 @@ abstract class Expression : AstNode;
   class IdentifierExpression : Expression;
   class IntrinsicExpression : Expression;
   class UnimplementedExpression : Expression;
-abstract class Member : AstNode;
-  class FieldMember : Member;

+ 3 - 3
executable_semantics/ast/ast_test_matchers_test.cpp

@@ -104,7 +104,7 @@ TEST(MatchesReturnTest, BasicUsage) {
 TEST(MatchesFunctionDeclarationTest, BasicUsage) {
   TuplePattern params(DummyLoc, {});
   Block body(DummyLoc, {});
-  FunctionDeclaration decl(DummyLoc, "Foo", {}, &params,
+  FunctionDeclaration decl(DummyLoc, "Foo", {}, std::nullopt, &params,
                            ReturnTerm::Omitted(DummyLoc), &body);
 
   EXPECT_THAT(decl, MatchesFunctionDeclaration());
@@ -117,7 +117,7 @@ TEST(MatchesFunctionDeclarationTest, BasicUsage) {
   EXPECT_THAT(decl,
               Not(MatchesFunctionDeclaration().WithBody(MatchesLiteral(0))));
 
-  FunctionDeclaration forward_decl(DummyLoc, "Foo", {}, &params,
+  FunctionDeclaration forward_decl(DummyLoc, "Foo", {}, std::nullopt, &params,
                                    ReturnTerm::Omitted(DummyLoc), std::nullopt);
   EXPECT_THAT(forward_decl, MatchesFunctionDeclaration().WithName("Foo"));
   EXPECT_THAT(forward_decl, Not(MatchesFunctionDeclaration().WithBody(_)));
@@ -150,7 +150,7 @@ TEST(MatchesUnimplementedExpressionTest, BasicUsage) {
 TEST(ASTDeclarationsTest, BasicUsage) {
   TuplePattern params(DummyLoc, {});
   Block body(DummyLoc, {});
-  FunctionDeclaration decl(DummyLoc, "Foo", {}, &params,
+  FunctionDeclaration decl(DummyLoc, "Foo", {}, std::nullopt, &params,
                            ReturnTerm::Omitted(DummyLoc), &body);
   AST ast = {.declarations = {&decl}};
 

+ 31 - 2
executable_semantics/ast/declaration.cpp

@@ -22,7 +22,7 @@ void Declaration::Print(llvm::raw_ostream& out) const {
     case DeclarationKind::ClassDeclaration: {
       const auto& class_decl = cast<ClassDeclaration>(*this);
       out << "class " << class_decl.name() << " {\n";
-      for (Nonnull<Member*> m : class_decl.members()) {
+      for (Nonnull<Declaration*> m : class_decl.members()) {
         out << *m;
       }
       out << "}\n";
@@ -41,7 +41,11 @@ void Declaration::Print(llvm::raw_ostream& out) const {
 
     case DeclarationKind::VariableDeclaration: {
       const auto& var = cast<VariableDeclaration>(*this);
-      out << "var " << var.binding() << " = " << var.initializer() << "\n";
+      out << "var " << var.binding();
+      if (var.has_initializer()) {
+        out << " = " << var.initializer();
+      }
+      out << ";\n";
       break;
     }
   }
@@ -64,6 +68,31 @@ void ReturnTerm::Print(llvm::raw_ostream& out) const {
   }
 }
 
+// Look for the `me` parameter in the `deduced_parameters_`
+// and put it in the `me_pattern_`.
+void FunctionDeclaration::ResolveDeducedAndReceiver(
+    const std::vector<Nonnull<AstNode*>>& deduced_params) {
+  for (Nonnull<AstNode*> param : deduced_params) {
+    switch (param->kind()) {
+      case AstNodeKind::GenericBinding:
+        deduced_parameters_.push_back(&cast<GenericBinding>(*param));
+        break;
+      case AstNodeKind::BindingPattern: {
+        Nonnull<BindingPattern*> bp = &cast<BindingPattern>(*param);
+        if (me_pattern_.has_value() || bp->name() != "me") {
+          FATAL_COMPILATION_ERROR(source_loc())
+              << "illegal binding pattern in implicit parameter list";
+        }
+        me_pattern_ = bp;
+        break;
+      }
+      default:
+        FATAL_COMPILATION_ERROR(source_loc())
+            << "illegal AST node in implicit parameter list";
+    }
+  }
+}
+
 void FunctionDeclaration::PrintDepth(int depth, llvm::raw_ostream& out) const {
   out << "fn " << name_ << " ";
   if (!deduced_parameters_.empty()) {

+ 27 - 92
executable_semantics/ast/declaration.h

@@ -10,8 +10,9 @@
 #include <vector>
 
 #include "common/ostream.h"
-#include "executable_semantics/ast/member.h"
+#include "executable_semantics/ast/ast_node.h"
 #include "executable_semantics/ast/pattern.h"
+#include "executable_semantics/ast/return_term.h"
 #include "executable_semantics/ast/source_location.h"
 #include "executable_semantics/ast/statement.h"
 #include "executable_semantics/ast/static_scope.h"
@@ -34,8 +35,8 @@ class Declaration : public AstNode {
  public:
   ~Declaration() override = 0;
 
-  Declaration(const Member&) = delete;
-  auto operator=(const Member&) -> Declaration& = delete;
+  Declaration(const Declaration&) = delete;
+  auto operator=(const Declaration&) -> Declaration& = delete;
 
   void Print(llvm::raw_ostream& out) const override;
 
@@ -126,100 +127,24 @@ class GenericBinding : public AstNode {
   std::optional<Nonnull<const Value*>> constant_value_;
 };
 
-// The syntactic representation of a function declaration's return type.
-// This syntax can take one of three forms:
-// - An _explicit_ term consists of `->` followed by a type expression.
-// - An _auto_ term consists of `-> auto`.
-// - An _omitted_ term consists of no tokens at all.
-// Each of these forms has a corresponding factory function.
-class ReturnTerm {
- public:
-  ReturnTerm(const ReturnTerm&) = default;
-  auto operator=(const ReturnTerm&) -> ReturnTerm& = default;
-
-  // Represents an omitted return term at `source_loc`.
-  static auto Omitted(SourceLocation source_loc) -> ReturnTerm {
-    return ReturnTerm(ReturnKind::Omitted, source_loc);
-  }
-
-  // Represents an auto return term at `source_loc`.
-  static auto Auto(SourceLocation source_loc) -> ReturnTerm {
-    return ReturnTerm(ReturnKind::Auto, source_loc);
-  }
-
-  // Represents an explicit return term with the given type expression.
-  static auto Explicit(Nonnull<Expression*> type_expression) -> ReturnTerm {
-    return ReturnTerm(type_expression);
-  }
-
-  // Returns true if this represents an omitted return term.
-  auto is_omitted() const -> bool { return kind_ == ReturnKind::Omitted; }
-
-  // Returns true if this represents an auto return term.
-  auto is_auto() const -> bool { return kind_ == ReturnKind::Auto; }
-
-  // If this represents an explicit return term, returns the type expression.
-  // Otherwise, returns nullopt.
-  auto type_expression() const -> std::optional<Nonnull<const Expression*>> {
-    return type_expression_;
-  }
-  auto type_expression() -> std::optional<Nonnull<Expression*>> {
-    return type_expression_;
-  }
-
-  // The static return type this term resolves to. Cannot be called before
-  // typechecking.
-  auto static_type() const -> const Value& { return **static_type_; }
-
-  // Sets the value of static_type(). Can only be called once, during
-  // typechecking.
-  void set_static_type(Nonnull<const Value*> type) { static_type_ = type; }
-
-  // Returns whether static_type() has been set. Should only be called
-  // during typechecking: before typechecking it's guaranteed to be false,
-  // and after typechecking it's guaranteed to be true.
-  auto has_static_type() const -> bool { return static_type_.has_value(); }
-
-  auto source_loc() const -> SourceLocation { return source_loc_; }
-
-  void Print(llvm::raw_ostream& out) const;
-  LLVM_DUMP_METHOD void Dump() const { Print(llvm::errs()); }
-
- private:
-  enum class ReturnKind { Omitted, Auto, Expression };
-
-  explicit ReturnTerm(ReturnKind kind, SourceLocation source_loc)
-      : kind_(kind), source_loc_(source_loc) {
-    CHECK(kind != ReturnKind::Expression);
-  }
-
-  explicit ReturnTerm(Nonnull<Expression*> type_expression)
-      : kind_(ReturnKind::Expression),
-        type_expression_(type_expression),
-        source_loc_(type_expression->source_loc()) {}
-
-  ReturnKind kind_;
-  std::optional<Nonnull<Expression*>> type_expression_;
-  std::optional<Nonnull<const Value*>> static_type_;
-
-  SourceLocation source_loc_;
-};
-
 class FunctionDeclaration : public Declaration {
  public:
   using ImplementsCarbonNamedEntity = void;
 
   FunctionDeclaration(SourceLocation source_loc, std::string name,
-                      std::vector<Nonnull<GenericBinding*>> deduced_params,
+                      std::vector<Nonnull<AstNode*>> deduced_params,
+                      std::optional<Nonnull<BindingPattern*>> me_pattern,
                       Nonnull<TuplePattern*> param_pattern,
                       ReturnTerm return_term,
                       std::optional<Nonnull<Block*>> body)
       : Declaration(AstNodeKind::FunctionDeclaration, source_loc),
         name_(std::move(name)),
-        deduced_parameters_(std::move(deduced_params)),
+        me_pattern_(me_pattern),
         param_pattern_(param_pattern),
         return_term_(return_term),
-        body_(body) {}
+        body_(body) {
+    ResolveDeducedAndReceiver(deduced_params);
+  }
 
   static auto classof(const AstNode* node) -> bool {
     return InheritsFromFunctionDeclaration(node->kind());
@@ -235,6 +160,8 @@ class FunctionDeclaration : public Declaration {
   auto deduced_parameters() -> llvm::ArrayRef<Nonnull<GenericBinding*>> {
     return deduced_parameters_;
   }
+  auto me_pattern() const -> const BindingPattern& { return **me_pattern_; }
+  auto me_pattern() -> BindingPattern& { return **me_pattern_; }
   auto param_pattern() const -> const TuplePattern& { return *param_pattern_; }
   auto param_pattern() -> TuplePattern& { return *param_pattern_; }
   auto return_term() const -> const ReturnTerm& { return return_term_; }
@@ -254,9 +181,13 @@ class FunctionDeclaration : public Declaration {
     constant_value_ = value;
   }
 
+  bool is_method() const { return me_pattern_.has_value(); }
+
  private:
+  void ResolveDeducedAndReceiver(const std::vector<Nonnull<AstNode*>>&);
   std::string name_;
   std::vector<Nonnull<GenericBinding*>> deduced_parameters_;
+  std::optional<Nonnull<BindingPattern*>> me_pattern_;
   Nonnull<TuplePattern*> param_pattern_;
   ReturnTerm return_term_;
   std::optional<Nonnull<Block*>> body_;
@@ -268,7 +199,7 @@ class ClassDeclaration : public Declaration {
   using ImplementsCarbonNamedEntity = void;
 
   ClassDeclaration(SourceLocation source_loc, std::string name,
-                   std::vector<Nonnull<Member*>> members)
+                   std::vector<Nonnull<Declaration*>> members)
       : Declaration(AstNodeKind::ClassDeclaration, source_loc),
         name_(std::move(name)),
         members_(std::move(members)) {}
@@ -278,7 +209,9 @@ class ClassDeclaration : public Declaration {
   }
 
   auto name() const -> const std::string& { return name_; }
-  auto members() const -> llvm::ArrayRef<Nonnull<Member*>> { return members_; }
+  auto members() const -> llvm::ArrayRef<Nonnull<Declaration*>> {
+    return members_;
+  }
 
   auto value_category() const -> ValueCategory { return ValueCategory::Let; }
   auto constant_value() const -> std::optional<Nonnull<const Value*>> {
@@ -294,7 +227,7 @@ class ClassDeclaration : public Declaration {
 
  private:
   std::string name_;
-  std::vector<Nonnull<Member*>> members_;
+  std::vector<Nonnull<Declaration*>> members_;
   std::optional<Nonnull<const Value*>> constant_value_;
 };
 
@@ -367,7 +300,7 @@ class VariableDeclaration : public Declaration {
  public:
   VariableDeclaration(SourceLocation source_loc,
                       Nonnull<BindingPattern*> binding,
-                      Nonnull<Expression*> initializer)
+                      std::optional<Nonnull<Expression*>> initializer)
       : Declaration(AstNodeKind::VariableDeclaration, source_loc),
         binding_(binding),
         initializer_(initializer) {}
@@ -378,15 +311,17 @@ class VariableDeclaration : public Declaration {
 
   auto binding() const -> const BindingPattern& { return *binding_; }
   auto binding() -> BindingPattern& { return *binding_; }
-  auto initializer() const -> const Expression& { return *initializer_; }
-  auto initializer() -> Expression& { return *initializer_; }
+  auto initializer() const -> const Expression& { return **initializer_; }
+  auto initializer() -> Expression& { return **initializer_; }
+
+  bool has_initializer() const { return initializer_.has_value(); }
 
  private:
   // 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.
   Nonnull<BindingPattern*> binding_;
-  Nonnull<Expression*> initializer_;
+  std::optional<Nonnull<Expression*>> initializer_;
 };
 
 }  // namespace Carbon

+ 0 - 25
executable_semantics/ast/member.cpp

@@ -1,25 +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/ast/member.h"
-
-#include "executable_semantics/common/arena.h"
-#include "llvm/Support/Casting.h"
-
-namespace Carbon {
-
-using llvm::cast;
-
-Member::~Member() = default;
-
-void Member::Print(llvm::raw_ostream& out) const {
-  switch (kind()) {
-    case MemberKind::FieldMember:
-      const auto& field = cast<FieldMember>(*this);
-      out << "var " << field.binding() << ";\n";
-      break;
-  }
-}
-
-}  // namespace Carbon

+ 0 - 70
executable_semantics/ast/member.h

@@ -1,70 +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
-
-#ifndef EXECUTABLE_SEMANTICS_AST_MEMBER_H_
-#define EXECUTABLE_SEMANTICS_AST_MEMBER_H_
-
-#include <string>
-
-#include "common/ostream.h"
-#include "executable_semantics/ast/expression.h"
-#include "executable_semantics/ast/pattern.h"
-#include "executable_semantics/ast/source_location.h"
-#include "llvm/Support/Compiler.h"
-
-namespace Carbon {
-
-// Abstract base class of all AST nodes representing patterns.
-//
-// Member and its derived classes support LLVM-style RTTI, including
-// llvm::isa, llvm::cast, and llvm::dyn_cast. To support this, every
-// class derived from Member 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 Member : public AstNode {
- public:
-  ~Member() override = 0;
-
-  Member(const Member&) = delete;
-  auto operator=(const Member&) -> Member& = delete;
-
-  void Print(llvm::raw_ostream& out) const override;
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromMember(node->kind());
-  }
-
-  // Returns the enumerator corresponding to the most-derived type of this
-  // object.
-  auto kind() const -> MemberKind {
-    return static_cast<MemberKind>(root_kind());
-  }
-
- protected:
-  Member(AstNodeKind kind, SourceLocation source_loc)
-      : AstNode(kind, source_loc) {}
-};
-
-class FieldMember : public Member {
- public:
-  FieldMember(SourceLocation source_loc, Nonnull<BindingPattern*> binding)
-      : Member(AstNodeKind::FieldMember, source_loc), binding_(binding) {
-    CHECK(binding->name() != AnonymousName);
-  }
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromFieldMember(node->kind());
-  }
-
-  auto binding() const -> const BindingPattern& { return *binding_; }
-  auto binding() -> BindingPattern& { return *binding_; }
-
- private:
-  Nonnull<BindingPattern*> binding_;
-};
-
-}  // namespace Carbon
-
-#endif  // EXECUTABLE_SEMANTICS_AST_MEMBER_H_

+ 1 - 0
executable_semantics/ast/pattern.h

@@ -15,6 +15,7 @@
 #include "executable_semantics/ast/expression.h"
 #include "executable_semantics/ast/source_location.h"
 #include "executable_semantics/ast/static_scope.h"
+#include "executable_semantics/ast/value_category.h"
 #include "llvm/ADT/ArrayRef.h"
 
 namespace Carbon {

+ 102 - 0
executable_semantics/ast/return_term.h

@@ -0,0 +1,102 @@
+// 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_RETURN_TERM_H_
+#define EXECUTABLE_SEMANTICS_AST_RETURN_TERM_H_
+
+#include <optional>
+#include <utility>
+
+#include "common/check.h"
+#include "common/ostream.h"
+#include "executable_semantics/ast/source_location.h"
+#include "executable_semantics/common/nonnull.h"
+
+namespace Carbon {
+
+class Value;
+class Expression;
+
+// The syntactic representation of a function declaration's return type.
+// This syntax can take one of three forms:
+// - An _explicit_ term consists of `->` followed by a type expression.
+// - An _auto_ term consists of `-> auto`.
+// - An _omitted_ term consists of no tokens at all.
+// Each of these forms has a corresponding factory function.
+class ReturnTerm {
+ public:
+  ReturnTerm(const ReturnTerm&) = default;
+  auto operator=(const ReturnTerm&) -> ReturnTerm& = default;
+
+  // Represents an omitted return term at `source_loc`.
+  static auto Omitted(SourceLocation source_loc) -> ReturnTerm {
+    return ReturnTerm(ReturnKind::Omitted, source_loc);
+  }
+
+  // Represents an auto return term at `source_loc`.
+  static auto Auto(SourceLocation source_loc) -> ReturnTerm {
+    return ReturnTerm(ReturnKind::Auto, source_loc);
+  }
+
+  // Represents an explicit return term with the given type expression.
+  static auto Explicit(Nonnull<Expression*> type_expression) -> ReturnTerm {
+    return ReturnTerm(type_expression);
+  }
+
+  // Returns true if this represents an omitted return term.
+  auto is_omitted() const -> bool { return kind_ == ReturnKind::Omitted; }
+
+  // Returns true if this represents an auto return term.
+  auto is_auto() const -> bool { return kind_ == ReturnKind::Auto; }
+
+  // If this represents an explicit return term, returns the type expression.
+  // Otherwise, returns nullopt.
+  auto type_expression() const -> std::optional<Nonnull<const Expression*>> {
+    return type_expression_;
+  }
+  auto type_expression() -> std::optional<Nonnull<Expression*>> {
+    return type_expression_;
+  }
+
+  // The static return type this term resolves to. Cannot be called before
+  // typechecking.
+  auto static_type() const -> const Value& { return **static_type_; }
+
+  // Sets the value of static_type(). Can only be called once, during
+  // typechecking.
+  void set_static_type(Nonnull<const Value*> type) { static_type_ = type; }
+
+  // Returns whether static_type() has been set. Should only be called
+  // during typechecking: before typechecking it's guaranteed to be false,
+  // and after typechecking it's guaranteed to be true.
+  auto has_static_type() const -> bool { return static_type_.has_value(); }
+
+  auto source_loc() const -> SourceLocation { return source_loc_; }
+
+  void Print(llvm::raw_ostream& out) const;
+  LLVM_DUMP_METHOD void Dump() const { Print(llvm::errs()); }
+
+ private:
+  enum class ReturnKind { Omitted, Auto, Expression };
+
+  explicit ReturnTerm(ReturnKind kind, SourceLocation source_loc)
+      : kind_(kind), source_loc_(source_loc) {
+    CHECK(kind != ReturnKind::Expression);
+  }
+
+  explicit ReturnTerm(Nonnull<Expression*> type_expression)
+      : kind_(ReturnKind::Expression),
+        type_expression_(type_expression),
+        source_loc_(type_expression->source_loc()) {}
+
+  ReturnKind kind_;
+  std::optional<Nonnull<Expression*>> type_expression_;
+  std::optional<Nonnull<const Value*>> static_type_;
+
+  SourceLocation source_loc_;
+};
+
+}  // namespace Carbon
+
+#endif  // EXECUTABLE_SEMANTICS_AST_RETURN_TERM_H_

+ 2 - 0
executable_semantics/ast/statement.h

@@ -9,8 +9,10 @@
 #include <vector>
 
 #include "common/ostream.h"
+#include "executable_semantics/ast/ast_node.h"
 #include "executable_semantics/ast/expression.h"
 #include "executable_semantics/ast/pattern.h"
+#include "executable_semantics/ast/return_term.h"
 #include "executable_semantics/ast/source_location.h"
 #include "executable_semantics/ast/static_scope.h"
 #include "executable_semantics/ast/value_category.h"

+ 2 - 1
executable_semantics/interpreter/BUILD

@@ -149,6 +149,7 @@ cc_library(
         "//common:check",
         "//executable_semantics/ast",
         "//executable_semantics/ast:declaration",
+        "//executable_semantics/ast:return_term",
         "//executable_semantics/ast:statement",
         "//executable_semantics/common:error",
         "//executable_semantics/common:nonnull",
@@ -165,8 +166,8 @@ cc_library(
         "//executable_semantics/ast",
         "//executable_semantics/ast:declaration",
         "//executable_semantics/ast:expression",
-        "//executable_semantics/ast:member",
         "//executable_semantics/ast:pattern",
+        "//executable_semantics/ast:return_term",
         "//executable_semantics/ast:statement",
         "//executable_semantics/ast:static_scope",
         "//executable_semantics/common:arena",

+ 9 - 1
executable_semantics/interpreter/exec_program.cpp

@@ -22,7 +22,6 @@ void ExecProgram(Nonnull<Arena*> arena, AST ast, bool trace) {
     for (const auto decl : ast.declarations) {
       llvm::outs() << *decl;
     }
-    llvm::outs() << "********** type checking **********\n";
   }
   SourceLocation source_loc("<Main()>", 0);
   ast.main_call = arena->New<CallExpression>(
@@ -30,8 +29,17 @@ void ExecProgram(Nonnull<Arena*> arena, AST ast, bool trace) {
       arena->New<TupleLiteral>(source_loc));
   // Although name resolution is currently done once, generic programming
   // (particularly templates) may require more passes.
+  if (trace) {
+    llvm::outs() << "********** resolving names **********\n";
+  }
   ResolveNames(ast);
+  if (trace) {
+    llvm::outs() << "********** resolving control flow **********\n";
+  }
   ResolveControlFlow(ast);
+  if (trace) {
+    llvm::outs() << "********** type checking **********\n";
+  }
   TypeChecker(arena, trace).TypeCheck(ast);
   if (trace) {
     llvm::outs() << "\n";

+ 30 - 6
executable_semantics/interpreter/interpreter.cpp

@@ -316,9 +316,11 @@ void Interpreter::StepLvalue() {
       }
     }
     case ExpressionKind::PrimitiveOperatorExpression: {
-      const PrimitiveOperatorExpression& op = cast<PrimitiveOperatorExpression>(exp);
+      const PrimitiveOperatorExpression& op =
+          cast<PrimitiveOperatorExpression>(exp);
       if (op.op() != Operator::Deref) {
-        FATAL() << "Can't treat primitive operator expression as lvalue: " << exp;
+        FATAL() << "Can't treat primitive operator expression as lvalue: "
+                << exp;
       }
       if (act.pos() == 0) {
         return todo_.Spawn(
@@ -355,6 +357,7 @@ auto Interpreter::Convert(Nonnull<const Value*> value,
   switch (value->kind()) {
     case Value::Kind::IntValue:
     case Value::Kind::FunctionValue:
+    case Value::Kind::BoundMethodValue:
     case Value::Kind::PointerValue:
     case Value::Kind::LValue:
     case Value::Kind::BoolValue:
@@ -573,6 +576,23 @@ void Interpreter::StepExp() {
                 std::make_unique<StatementAction>(*function.body()),
                 std::move(function_scope));
           }
+          case Value::Kind::BoundMethodValue: {
+            const BoundMethodValue& m =
+                cast<BoundMethodValue>(*act.results()[0]);
+            const FunctionDeclaration& method = m.declaration();
+            Nonnull<const Value*> converted_args = Convert(
+                act.results()[1], &method.param_pattern().static_type());
+            RuntimeScope method_scope(&heap_);
+            CHECK(PatternMatch(&method.me_pattern().value(), m.receiver(),
+                               exp.source_loc(), &method_scope));
+            CHECK(PatternMatch(&method.param_pattern().value(), converted_args,
+                               exp.source_loc(), &method_scope));
+            CHECK(method.body().has_value())
+                << "Calling a method that's missing a body";
+            return todo_.Spawn(
+                std::make_unique<StatementAction>(*method.body()),
+                std::move(method_scope));
+          }
           default:
             FATAL_RUNTIME_ERROR(exp.source_loc())
                 << "in call, expected a function, not " << *act.results()[0];
@@ -928,11 +948,15 @@ void Interpreter::StepDeclaration() {
   switch (decl.kind()) {
     case DeclarationKind::VariableDeclaration: {
       const auto& var_decl = cast<VariableDeclaration>(decl);
-      if (act.pos() == 0) {
-        return todo_.Spawn(
-            std::make_unique<ExpressionAction>(&var_decl.initializer()));
+      if (var_decl.has_initializer()) {
+        if (act.pos() == 0) {
+          return todo_.Spawn(
+              std::make_unique<ExpressionAction>(&var_decl.initializer()));
+        } else {
+          todo_.Initialize(&var_decl.binding(), act.results()[0]);
+          return todo_.FinishAction();
+        }
       } else {
-        todo_.Initialize(&var_decl.binding(), act.results()[0]);
         return todo_.FinishAction();
       }
     }

+ 25 - 8
executable_semantics/interpreter/resolve_control_flow.cpp

@@ -5,6 +5,7 @@
 #include "executable_semantics/interpreter/resolve_control_flow.h"
 
 #include "executable_semantics/ast/declaration.h"
+#include "executable_semantics/ast/return_term.h"
 #include "executable_semantics/ast/statement.h"
 #include "executable_semantics/common/error.h"
 #include "llvm/Support/Casting.h"
@@ -110,16 +111,32 @@ static void ResolveControlFlow(Nonnull<Statement*> statement,
   }
 }
 
-void ResolveControlFlow(AST& ast) {
-  for (auto declaration : ast.declarations) {
-    if (declaration->kind() != DeclarationKind::FunctionDeclaration) {
-      continue;
+void ResolveControlFlow(Nonnull<Declaration*> declaration) {
+  switch (declaration->kind()) {
+    case DeclarationKind::FunctionDeclaration: {
+      auto& function = cast<FunctionDeclaration>(*declaration);
+      if (function.body().has_value()) {
+        FunctionData data = {.declaration = &function};
+        ResolveControlFlow(*function.body(), std::nullopt, &data);
+      }
+      break;
     }
-    auto& function = cast<FunctionDeclaration>(*declaration);
-    if (function.body().has_value()) {
-      FunctionData data = {.declaration = &function};
-      ResolveControlFlow(*function.body(), std::nullopt, &data);
+    case DeclarationKind::ClassDeclaration: {
+      auto& class_decl = cast<ClassDeclaration>(*declaration);
+      for (Nonnull<Declaration*> member : class_decl.members()) {
+        ResolveControlFlow(member);
+      }
+      break;
     }
+    default:
+      // do nothing
+      break;
+  }
+}
+
+void ResolveControlFlow(AST& ast) {
+  for (auto declaration : ast.declarations) {
+    ResolveControlFlow(declaration);
   }
 }
 

+ 9 - 26
executable_semantics/interpreter/resolve_names.cpp

@@ -8,7 +8,6 @@
 
 #include "executable_semantics/ast/declaration.h"
 #include "executable_semantics/ast/expression.h"
-#include "executable_semantics/ast/member.h"
 #include "executable_semantics/ast/pattern.h"
 #include "executable_semantics/ast/statement.h"
 #include "executable_semantics/ast/static_scope.h"
@@ -21,20 +20,6 @@ namespace Carbon {
 // Adds the names exposed by the given AST node to enclosing_scope.
 static void AddExposedNames(const Declaration& declaration,
                             StaticScope& enclosing_scope);
-static void AddExposedNames(const Member& member, StaticScope& enclosing_scope);
-
-static void AddExposedNames(const Member& member,
-                            StaticScope& enclosing_scope) {
-  switch (member.kind()) {
-    case MemberKind::FieldMember: {
-      const auto& field = cast<FieldMember>(member);
-      if (field.binding().name() != AnonymousName) {
-        enclosing_scope.Add(field.binding().name(), &field.binding());
-      }
-      break;
-    }
-  }
-}
 
 static void AddExposedNames(const Declaration& declaration,
                             StaticScope& enclosing_scope) {
@@ -77,7 +62,6 @@ static void ResolveNames(Expression& expression,
                          const StaticScope& enclosing_scope);
 static void ResolveNames(Pattern& pattern, StaticScope& enclosing_scope);
 static void ResolveNames(Statement& statement, StaticScope& enclosing_scope);
-static void ResolveNames(Member& member, StaticScope& enclosing_scope);
 static void ResolveNames(Declaration& declaration,
                          StaticScope& enclosing_scope);
 
@@ -257,13 +241,6 @@ static void ResolveNames(Statement& statement, StaticScope& enclosing_scope) {
   }
 }
 
-static void ResolveNames(Member& member, StaticScope& enclosing_scope) {
-  switch (member.kind()) {
-    case MemberKind::FieldMember:
-      ResolveNames(cast<FieldMember>(member).binding(), enclosing_scope);
-  }
-}
-
 static void ResolveNames(Declaration& declaration,
                          StaticScope& enclosing_scope) {
   switch (declaration.kind()) {
@@ -275,6 +252,9 @@ static void ResolveNames(Declaration& declaration,
         function_scope.Add(binding->name(), binding);
         ResolveNames(binding->type(), function_scope);
       }
+      if (function.is_method()) {
+        ResolveNames(function.me_pattern(), function_scope);
+      }
       ResolveNames(function.param_pattern(), function_scope);
       if (function.return_term().type_expression().has_value()) {
         ResolveNames(**function.return_term().type_expression(),
@@ -289,10 +269,11 @@ static void ResolveNames(Declaration& declaration,
       auto& class_decl = cast<ClassDeclaration>(declaration);
       StaticScope class_scope;
       class_scope.AddParent(&enclosing_scope);
-      for (Nonnull<Member*> member : class_decl.members()) {
+      class_scope.Add(class_decl.name(), &class_decl);
+      for (Nonnull<Declaration*> member : class_decl.members()) {
         AddExposedNames(*member, class_scope);
       }
-      for (Nonnull<Member*> member : class_decl.members()) {
+      for (Nonnull<Declaration*> member : class_decl.members()) {
         ResolveNames(*member, class_scope);
       }
       break;
@@ -316,7 +297,9 @@ static void ResolveNames(Declaration& declaration,
     case DeclarationKind::VariableDeclaration: {
       auto& var = cast<VariableDeclaration>(declaration);
       ResolveNames(var.binding(), enclosing_scope);
-      ResolveNames(var.initializer(), enclosing_scope);
+      if (var.has_initializer()) {
+        ResolveNames(var.initializer(), enclosing_scope);
+      }
       break;
     }
   }

+ 90 - 52
executable_semantics/interpreter/type_checker.cpp

@@ -74,6 +74,7 @@ static auto IsConcreteType(Nonnull<const Value*> value) -> bool {
   switch (value->kind()) {
     case Value::Kind::IntValue:
     case Value::Kind::FunctionValue:
+    case Value::Kind::BoundMethodValue:
     case Value::Kind::PointerValue:
     case Value::Kind::LValue:
     case Value::Kind::BoolValue:
@@ -165,7 +166,7 @@ static auto IsImplicitlyConvertible(Nonnull<const Value*> source,
         case Value::Kind::NominalClassType:
           return FieldTypesImplicitlyConvertible(
               cast<StructType>(*source).fields(),
-              cast<NominalClassType>(*destination).fields());
+              FieldTypes(cast<NominalClassType>(*destination)));
         default:
           return false;
       }
@@ -314,6 +315,7 @@ void TypeChecker::ArgumentDeduction(
     case Value::Kind::IntValue:
     case Value::Kind::BoolValue:
     case Value::Kind::FunctionValue:
+    case Value::Kind::BoundMethodValue:
     case Value::Kind::PointerValue:
     case Value::Kind::LValue:
     case Value::Kind::StructValue:
@@ -380,6 +382,7 @@ auto TypeChecker::Substitute(
     case Value::Kind::IntValue:
     case Value::Kind::BoolValue:
     case Value::Kind::FunctionValue:
+    case Value::Kind::BoundMethodValue:
     case Value::Kind::PointerValue:
     case Value::Kind::LValue:
     case Value::Kind::StructValue:
@@ -480,25 +483,28 @@ void TypeChecker::TypeCheckExp(Nonnull<Expression*> e) {
         }
         case Value::Kind::NominalClassType: {
           const auto& t_class = cast<NominalClassType>(aggregate_type);
-          // Search for a field
-          for (auto& field : t_class.fields()) {
-            if (access.field() == field.name) {
-              SetStaticType(&access, field.value);
-              access.set_value_category(access.aggregate().value_category());
-              return;
-            }
-          }
-          // Search for a method
-          for (auto& method : t_class.methods()) {
-            if (access.field() == method.name) {
-              SetStaticType(&access, method.value);
-              access.set_value_category(ValueCategory::Let);
-              return;
+          if (std::optional<Nonnull<const Declaration*>> member =
+                  t_class.FindMember(access.field());
+              member.has_value()) {
+            SetStaticType(&access, &(*member)->static_type());
+            switch ((*member)->kind()) {
+              case DeclarationKind::VariableDeclaration:
+                access.set_value_category(access.aggregate().value_category());
+                break;
+              case DeclarationKind::FunctionDeclaration:
+                access.set_value_category(ValueCategory::Let);
+                break;
+              default:
+                FATAL() << "member " << access.field()
+                        << " is not a field or method";
+                break;
             }
+            return;
+          } else {
+            FATAL_COMPILATION_ERROR(e->source_loc())
+                << "class " << t_class.declaration().name()
+                << " does not have a field named " << access.field();
           }
-          FATAL_COMPILATION_ERROR(e->source_loc())
-              << "class " << t_class.name() << " does not have a field named "
-              << access.field();
         }
         case Value::Kind::TypeOfChoiceType: {
           const ChoiceType& choice =
@@ -517,10 +523,36 @@ void TypeChecker::TypeCheckExp(Nonnull<Expression*> e) {
           access.set_value_category(ValueCategory::Let);
           return;
         }
+        case Value::Kind::TypeOfClassType: {
+          const NominalClassType& class_type =
+              cast<TypeOfClassType>(aggregate_type).class_type();
+          if (std::optional<Nonnull<const Declaration*>> member =
+                  class_type.FindMember(access.field());
+              member.has_value()) {
+            switch ((*member)->kind()) {
+              case DeclarationKind::FunctionDeclaration: {
+                const auto& func = cast<FunctionDeclaration>(*member);
+                if (func->is_method()) {
+                  break;
+                }
+                SetStaticType(&access, &(*member)->static_type());
+                access.set_value_category(ValueCategory::Let);
+                return;
+              }
+              default:
+                break;
+            }
+            FATAL_COMPILATION_ERROR(access.source_loc())
+                << access.field() << " is not a class function";
+          } else {
+            FATAL_COMPILATION_ERROR(access.source_loc())
+                << class_type << " does not have a class function named "
+                << access.field();
+          }
+        }
         default:
           FATAL_COMPILATION_ERROR(e->source_loc())
-              << "field access, expected a struct\n"
-              << *e;
+              << "field access, unexpected " << aggregate_type << " in " << *e;
       }
     }
     case ExpressionKind::IdentifierExpression: {
@@ -623,8 +655,9 @@ void TypeChecker::TypeCheckExp(Nonnull<Expression*> e) {
           return;
         case Operator::AddressOf:
           if (op.arguments()[0]->value_category() != ValueCategory::Var) {
-            FATAL_COMPILATION_ERROR(op.arguments()[0]->source_loc()) <<
-                "Argument to " << ToString(op.op()) << " should be an lvalue.";
+            FATAL_COMPILATION_ERROR(op.arguments()[0]->source_loc())
+                << "Argument to " << ToString(op.op())
+                << " should be an lvalue.";
           }
           SetStaticType(&op, arena_->New<PointerType>(ts[0]));
           op.set_value_category(ValueCategory::Let);
@@ -1001,6 +1034,11 @@ void TypeChecker::TypeCheckFunctionDeclaration(Nonnull<FunctionDeclaration*> f,
     SetStaticType(deduced, arena_->New<VariableType>(deduced));
     SetConstantValue(deduced, &deduced->static_type());
   }
+  if (f->is_method()) {
+    // Type check the receiver patter
+    TypeCheckPattern(&f->me_pattern(), std::nullopt);
+  }
+
   // Type check the parameter pattern
   TypeCheckPattern(&f->param_pattern(), std::nullopt);
 
@@ -1044,32 +1082,32 @@ void TypeChecker::TypeCheckFunctionDeclaration(Nonnull<FunctionDeclaration*> f,
                     arena_->New<IntType>(), &f->return_term().static_type());
     // TODO: Check that main doesn't have any parameters.
   }
+  SetConstantValue(f, arena_->New<FunctionValue>(f));
   return;
 }
 
 void TypeChecker::TypeCheckClassDeclaration(
     Nonnull<ClassDeclaration*> class_decl) {
-  std::vector<NamedValue> fields;
-  std::vector<NamedValue> methods;
-  for (Nonnull<Member*> m : class_decl->members()) {
-    switch (m->kind()) {
-      case MemberKind::FieldMember: {
-        BindingPattern& binding = cast<FieldMember>(*m).binding();
-        if (binding.name() == AnonymousName) {
-          FATAL_COMPILATION_ERROR(binding.source_loc())
-              << "Struct members must have names";
-        }
-        TypeCheckPattern(&binding, std::nullopt);
-        fields.push_back(
-            {.name = binding.name(), .value = &binding.static_type()});
-        break;
-      }
-    }
+  // The declarations of the members may refer to the class, so we
+  // must set the constant value of the class and its static type
+  // before we start processing the members.
+  Nonnull<NominalClassType*> class_type =
+      arena_->New<NominalClassType>(class_decl);
+  SetConstantValue(class_decl, class_type);
+  SetStaticType(class_decl, arena_->New<TypeOfClassType>(class_type));
+
+  // First pass: process the field, class function, and method
+  // declarations but not the bodies of class functions or method
+  // declarations.
+  for (Nonnull<Declaration*> m : class_decl->members()) {
+    DeclareDeclaration(m);
+  }
+
+  // Second pass: type check the bodies of the class functions and
+  // methods.
+  for (Nonnull<Declaration*> m : class_decl->members()) {
+    TypeCheckDeclaration(m);
   }
-  SetStaticType(
-      class_decl,
-      arena_->New<TypeOfClassType>(arena_->New<NominalClassType>(
-          class_decl->name(), std::move(fields), std::move(methods))));
 }
 
 void TypeChecker::TypeCheckChoiceDeclaration(
@@ -1081,12 +1119,13 @@ void TypeChecker::TypeCheckChoiceDeclaration(
     alternatives.push_back({.name = alternative->name(), .value = signature});
   }
   auto ct = arena_->New<ChoiceType>(choice->name(), std::move(alternatives));
+  SetConstantValue(choice, ct);
   SetStaticType(choice, arena_->New<TypeOfChoiceType>(ct));
 }
 
 void TypeChecker::TypeCheck(AST& ast) {
   for (Nonnull<Declaration*> declaration : ast.declarations) {
-    TopLevel(declaration);
+    DeclareDeclaration(declaration);
   }
   for (Nonnull<Declaration*> decl : ast.declarations) {
     TypeCheckDeclaration(decl);
@@ -1111,7 +1150,9 @@ void TypeChecker::TypeCheckDeclaration(Nonnull<Declaration*> d) {
       // Signals a type error if the initializing expression does not have
       // the declared type of the variable, otherwise returns this
       // declaration with annotated types.
-      TypeCheckExp(&var.initializer());
+      if (var.has_initializer()) {
+        TypeCheckExp(&var.initializer());
+      }
       const auto* binding_type =
           dyn_cast<ExpressionPattern>(&var.binding().type());
       if (binding_type == nullptr) {
@@ -1122,35 +1163,32 @@ void TypeChecker::TypeCheckDeclaration(Nonnull<Declaration*> d) {
       Nonnull<const Value*> declared_type =
           InterpExp(&binding_type->expression(), arena_, trace_);
       SetStaticType(&var, declared_type);
-      ExpectType(var.source_loc(), "initializer of variable", declared_type,
-                 &var.initializer().static_type());
+      if (var.has_initializer()) {
+        ExpectType(var.source_loc(), "initializer of variable", declared_type,
+                   &var.initializer().static_type());
+      }
       return;
     }
   }
 }
 
-void TypeChecker::TopLevel(Nonnull<Declaration*> d) {
+void TypeChecker::DeclareDeclaration(Nonnull<Declaration*> d) {
   switch (d->kind()) {
     case DeclarationKind::FunctionDeclaration: {
       auto& func_def = cast<FunctionDeclaration>(*d);
       TypeCheckFunctionDeclaration(&func_def, /*check_body=*/false);
-      SetConstantValue(&func_def, arena_->New<FunctionValue>(&func_def));
       break;
     }
 
     case DeclarationKind::ClassDeclaration: {
       auto& class_decl = cast<ClassDeclaration>(*d);
       TypeCheckClassDeclaration(&class_decl);
-      const auto& type = cast<TypeOfClassType>(class_decl.static_type());
-      SetConstantValue(&class_decl, &type.class_type());
       break;
     }
 
     case DeclarationKind::ChoiceDeclaration: {
       auto& choice = cast<ChoiceDeclaration>(*d);
       TypeCheckChoiceDeclaration(&choice);
-      const auto& type = cast<TypeOfChoiceType>(choice.static_type());
-      SetConstantValue(&choice, &type.choice_type());
       break;
     }
 

+ 3 - 1
executable_semantics/interpreter/type_checker.h

@@ -71,7 +71,9 @@ class TypeChecker {
   // Equivalent to TypeCheckExp, but operates on the AST rooted at choice_decl.
   void TypeCheckChoiceDeclaration(Nonnull<ChoiceDeclaration*> choice);
 
-  void TopLevel(Nonnull<Declaration*> d);
+  // Establish the type of the declaration without deeply checking
+  // the declaration, such as checking the body of a function.
+  void DeclareDeclaration(Nonnull<Declaration*> d);
 
   // Verifies that opt_stmt holds a statement, and it is structurally impossible
   // for control flow to leave that statement except via a `return`.

+ 111 - 8
executable_semantics/interpreter/value.cpp

@@ -40,10 +40,27 @@ static auto GetMember(Nonnull<Arena*> arena, Nonnull<const Value*> v,
       return *field;
     }
     case Value::Kind::NominalClassValue: {
+      const NominalClassValue& object = cast<NominalClassValue>(*v);
+      // Look for a field
       std::optional<Nonnull<const Value*>> field =
-          cast<StructValue>(cast<NominalClassValue>(*v).inits()).FindField(f);
+          cast<StructValue>(object.inits()).FindField(f);
       if (field == std::nullopt) {
-        FATAL_RUNTIME_ERROR(source_loc) << "member " << f << " not in " << *v;
+        // Look for a method in the object's class
+        const NominalClassType& class_type =
+            cast<NominalClassType>(object.type());
+        std::optional<Nonnull<const FunctionValue*>> func =
+            class_type.FindFunction(f);
+        if (func == std::nullopt) {
+          FATAL_RUNTIME_ERROR(source_loc) << "member " << f << " not in " << *v
+                                          << " or its class " << class_type;
+        } else if ((*func)->declaration().is_method()) {
+          // Found a method. Turn it into a bound method.
+          const FunctionValue& m = cast<FunctionValue>(**func);
+          return arena->New<BoundMethodValue>(&m.declaration(), &object);
+        } else {
+          // Found a class function
+          return *func;
+        }
       }
       return *field;
     }
@@ -55,6 +72,16 @@ static auto GetMember(Nonnull<Arena*> arena, Nonnull<const Value*> v,
       }
       return arena->New<AlternativeConstructorValue>(f, choice.name());
     }
+    case Value::Kind::NominalClassType: {
+      const NominalClassType& class_type = cast<NominalClassType>(*v);
+      std::optional<Nonnull<const FunctionValue*>> fun =
+          class_type.FindFunction(f);
+      if (fun == std::nullopt) {
+        FATAL_RUNTIME_ERROR(source_loc)
+            << "class function " << f << " not in " << *v;
+      }
+      return *fun;
+    }
     default:
       FATAL() << "field access not allowed for value " << *v;
   }
@@ -158,7 +185,7 @@ void Value::Print(llvm::raw_ostream& out) const {
     }
     case Value::Kind::NominalClassValue: {
       const auto& s = cast<NominalClassValue>(*this);
-      out << cast<NominalClassType>(s.type()).name() << s.inits();
+      out << cast<NominalClassType>(s.type()).declaration().name() << s.inits();
       break;
     }
     case Value::Kind::TupleValue: {
@@ -179,6 +206,10 @@ void Value::Print(llvm::raw_ostream& out) const {
     case Value::Kind::FunctionValue:
       out << "fun<" << cast<FunctionValue>(*this).declaration().name() << ">";
       break;
+    case Value::Kind::BoundMethodValue:
+      out << "bound_method<"
+          << cast<BoundMethodValue>(*this).declaration().name() << ">";
+      break;
     case Value::Kind::PointerValue:
       out << "ptr<" << cast<PointerValue>(*this).address() << ">";
       break;
@@ -230,9 +261,11 @@ void Value::Print(llvm::raw_ostream& out) const {
       out << "}";
       break;
     }
-    case Value::Kind::NominalClassType:
-      out << "class " << cast<NominalClassType>(*this).name();
+    case Value::Kind::NominalClassType: {
+      const NominalClassType& class_type = cast<NominalClassType>(*this);
+      out << "class " << class_type.declaration().name();
       break;
+    }
     case Value::Kind::ChoiceType:
       out << "choice " << cast<ChoiceType>(*this).name();
       break;
@@ -252,7 +285,8 @@ void Value::Print(llvm::raw_ostream& out) const {
       out << "\"";
       break;
     case Value::Kind::TypeOfClassType:
-      out << "typeof(" << cast<TypeOfClassType>(*this).class_type().name()
+      out << "typeof("
+          << cast<TypeOfClassType>(*this).class_type().declaration().name()
           << ")";
       break;
     case Value::Kind::TypeOfChoiceType:
@@ -328,8 +362,8 @@ auto TypeEqual(Nonnull<const Value*> t1, Nonnull<const Value*> t2) -> bool {
       return true;
     }
     case Value::Kind::NominalClassType:
-      return cast<NominalClassType>(*t1).name() ==
-             cast<NominalClassType>(*t2).name();
+      return cast<NominalClassType>(*t1).declaration().name() ==
+             cast<NominalClassType>(*t2).declaration().name();
     case Value::Kind::ChoiceType:
       return cast<ChoiceType>(*t1).name() == cast<ChoiceType>(*t2).name();
     case Value::Kind::TupleValue: {
@@ -387,6 +421,15 @@ auto ValueEqual(Nonnull<const Value*> v1, Nonnull<const Value*> v2) -> bool {
       return body1.has_value() == body2.has_value() &&
              (!body1.has_value() || *body1 == *body2);
     }
+    case Value::Kind::BoundMethodValue: {
+      const BoundMethodValue& m1 = cast<BoundMethodValue>(*v1);
+      const BoundMethodValue& m2 = cast<BoundMethodValue>(*v2);
+      std::optional<Nonnull<const Statement*>> body1 = m1.declaration().body();
+      std::optional<Nonnull<const Statement*>> body2 = m2.declaration().body();
+      return ValueEqual(m1.receiver(), m2.receiver()) &&
+             body1.has_value() == body2.has_value() &&
+             (!body1.has_value() || *body1 == *body2);
+    }
     case Value::Kind::TupleValue: {
       const std::vector<Nonnull<const Value*>>& elements1 =
           cast<TupleValue>(*v1).elements();
@@ -455,4 +498,64 @@ auto ChoiceType::FindAlternative(std::string_view name) const
   return std::nullopt;
 }
 
+auto NominalClassType::FindFunction(const std::string& name) const
+    -> std::optional<Nonnull<const FunctionValue*>> {
+  for (const auto& member : declaration().members()) {
+    switch (member->kind()) {
+      case DeclarationKind::FunctionDeclaration: {
+        const auto& fun = cast<FunctionDeclaration>(*member);
+        if (fun.name() == name) {
+          return &cast<FunctionValue>(**fun.constant_value());
+        }
+        break;
+      }
+      default:
+        break;
+    }
+  }
+  return std::nullopt;
+}
+
+auto FieldTypes(const NominalClassType& class_type) -> std::vector<NamedValue> {
+  std::vector<NamedValue> field_types;
+  for (Nonnull<Declaration*> m : class_type.declaration().members()) {
+    switch (m->kind()) {
+      case DeclarationKind::VariableDeclaration: {
+        const auto& var = cast<VariableDeclaration>(*m);
+        field_types.push_back({.name = var.binding().name(),
+                               .value = &var.binding().static_type()});
+        break;
+      }
+      default:
+        break;
+    }
+  }
+  return field_types;
+}
+
+auto NominalClassType::FindMember(const std::string& name) const
+    -> std::optional<Nonnull<const Declaration*>> {
+  for (const auto& member : declaration().members()) {
+    switch (member->kind()) {
+      case DeclarationKind::FunctionDeclaration: {
+        const auto& fun = cast<FunctionDeclaration>(*member);
+        if (fun.name() == name) {
+          return &fun;
+        }
+        break;
+      }
+      case DeclarationKind::VariableDeclaration: {
+        const auto& var = cast<VariableDeclaration>(*member);
+        if (var.binding().name() == name) {
+          return &var;
+        }
+        break;
+      }
+      default:
+        break;
+    }
+  }
+  return std::nullopt;
+}
+
 }  // namespace Carbon

+ 42 - 14
executable_semantics/interpreter/value.h

@@ -36,6 +36,7 @@ class Value {
   enum class Kind {
     IntValue,
     FunctionValue,
+    BoundMethodValue,
     PointerValue,
     LValue,
     BoolValue,
@@ -135,6 +136,30 @@ class FunctionValue : public Value {
   Nonnull<const FunctionDeclaration*> declaration_;
 };
 
+// A bound method value. It includes the receiver object.
+class BoundMethodValue : public Value {
+ public:
+  explicit BoundMethodValue(Nonnull<const FunctionDeclaration*> declaration,
+                            Nonnull<const Value*> receiver)
+      : Value(Kind::BoundMethodValue),
+        declaration_(declaration),
+        receiver_(receiver) {}
+
+  static auto classof(const Value* value) -> bool {
+    return value->kind() == Kind::BoundMethodValue;
+  }
+
+  auto declaration() const -> const FunctionDeclaration& {
+    return *declaration_;
+  }
+
+  auto receiver() const -> Nonnull<const Value*> { return receiver_; }
+
+ private:
+  Nonnull<const FunctionDeclaration*> declaration_;
+  Nonnull<const Value*> receiver_;
+};
+
 // The value of a location in memory.
 class LValue : public Value {
  public:
@@ -212,7 +237,7 @@ class StructValue : public Value {
   std::vector<NamedValue> elements_;
 };
 
-// A value of a nominal class type.
+// A value of a nominal class type, i.e., an object.
 class NominalClassValue : public Value {
  public:
   NominalClassValue(Nonnull<const Value*> type, Nonnull<const Value*> inits)
@@ -227,7 +252,7 @@ class NominalClassValue : public Value {
 
  private:
   Nonnull<const Value*> type_;
-  Nonnull<const Value*> inits_;
+  Nonnull<const Value*> inits_;  // The initializing StructValue.
 };
 
 // An alternative constructor value.
@@ -429,27 +454,30 @@ class StructType : public Value {
 // A class type.
 class NominalClassType : public Value {
  public:
-  NominalClassType(std::string name, std::vector<NamedValue> fields,
-                   std::vector<NamedValue> methods)
-      : Value(Kind::NominalClassType),
-        name_(std::move(name)),
-        fields_(std::move(fields)),
-        methods_(std::move(methods)) {}
+  NominalClassType(Nonnull<const ClassDeclaration*> declaration)
+      : Value(Kind::NominalClassType), declaration_(declaration) {}
 
   static auto classof(const Value* value) -> bool {
     return value->kind() == Kind::NominalClassType;
   }
 
-  auto name() const -> const std::string& { return name_; }
-  auto fields() const -> llvm::ArrayRef<NamedValue> { return fields_; }
-  auto methods() const -> llvm::ArrayRef<NamedValue> { return methods_; }
+  auto declaration() const -> const ClassDeclaration& { return *declaration_; }
+
+  // Return the declaration of the member with the given name.
+  auto FindMember(const std::string& name) const
+      -> std::optional<Nonnull<const Declaration*>>;
+
+  // Returns the value of the function named `name` in this class, or
+  // nullopt if there is no such function.
+  auto FindFunction(const std::string& name) const
+      -> std::optional<Nonnull<const FunctionValue*>>;
 
  private:
-  std::string name_;
-  std::vector<NamedValue> fields_;
-  std::vector<NamedValue> methods_;
+  Nonnull<const ClassDeclaration*> declaration_;
 };
 
+auto FieldTypes(const NominalClassType&) -> std::vector<NamedValue>;
+
 // A choice type.
 class ChoiceType : public Value {
  public:

+ 32 - 24
executable_semantics/syntax/parser.ypp

@@ -110,8 +110,8 @@
 %type <std::vector<Nonnull<Statement*>>> statement_list
 %type <Nonnull<Expression*>> expression
 %type <Nonnull<GenericBinding*>> generic_binding
-%type <std::vector<Nonnull<GenericBinding*>>> deduced_params
-%type <std::vector<Nonnull<GenericBinding*>>> deduced_param_list
+%type <std::vector<Nonnull<AstNode*>>> deduced_params
+%type <std::vector<Nonnull<AstNode*>>> deduced_param_list
 %type <Nonnull<Pattern*>> pattern
 %type <Nonnull<Pattern*>> non_expression_pattern
 %type <BisonWrap<ReturnTerm>> return_term
@@ -122,9 +122,8 @@
 %type <std::vector<FieldInitializer>> struct_type_literal_contents
 %type <Nonnull<TupleLiteral*>> tuple
 %type <std::string> binding_lhs
+%type <std::optional<Nonnull<BindingPattern*>>> receiver
 %type <Nonnull<BindingPattern*>> variable_declaration
-%type <Nonnull<Member*>> member
-%type <std::vector<Nonnull<Member*>>> member_list
 %type <ParenContents<Expression>> paren_expression_base
 %type <ParenContents<Expression>> paren_expression_contents
 %type <Nonnull<Pattern*>> paren_pattern
@@ -653,10 +652,10 @@ generic_binding:
 ;
 deduced_param_list:
   // Empty
-    { $$ = std::vector<Nonnull<GenericBinding*>>(); }
+    { $$ = std::vector<Nonnull<AstNode*>>(); }
 | generic_binding
     {
-      $$ = std::vector<Nonnull<GenericBinding*>>();
+      $$ = std::vector<Nonnull<AstNode*>>();
       $$.push_back($1);
     }
 | generic_binding COMMA deduced_param_list
@@ -664,40 +663,44 @@ deduced_param_list:
       $$ = $3;
       $$.push_back($1);
     }
+| variable_declaration
+    {
+      $$ = std::vector<Nonnull<AstNode*>>();
+      $$.push_back($1);
+    }
+| variable_declaration COMMA deduced_param_list
+    {
+      $$ = $3;
+      $$.push_back($1);
+    }
 ;
 deduced_params:
   // Empty
-    { $$ = std::vector<Nonnull<GenericBinding*>>(); }
+    { $$ = std::vector<Nonnull<AstNode*>>(); }
 | LEFT_SQUARE_BRACKET deduced_param_list RIGHT_SQUARE_BRACKET
     { $$ = $2; }
 ;
+receiver:
+  // Empty
+    { $$ = std::nullopt; }
+| LEFT_CURLY_BRACE variable_declaration RIGHT_CURLY_BRACE
+    { $$ = $2; }
+;
 function_declaration:
-  FN identifier deduced_params maybe_empty_tuple_pattern return_term block
+  FN identifier deduced_params receiver maybe_empty_tuple_pattern return_term block
     {
       $$ = arena->New<FunctionDeclaration>(context.source_loc(), $2, $3, $4, $5,
-                                           $6);
+                                           $6, $7);
     }
-| FN identifier deduced_params maybe_empty_tuple_pattern return_term SEMICOLON
+| FN identifier deduced_params receiver maybe_empty_tuple_pattern return_term SEMICOLON
     {
       $$ = arena->New<FunctionDeclaration>(context.source_loc(), $2, $3, $4, $5,
-                                           std::nullopt);
+                                           $6, std::nullopt);
     }
 ;
 variable_declaration: identifier COLON pattern
     { $$ = arena->New<BindingPattern>(context.source_loc(), $1, $3); }
 ;
-member: VAR variable_declaration SEMICOLON
-    { $$ = arena->New<FieldMember>(context.source_loc(), $2); }
-;
-member_list:
-  // Empty
-    { $$ = {}; }
-| member_list member
-    {
-      $$ = $1;
-      $$.push_back($2);
-    }
-;
 alternative:
   identifier tuple
     { $$ = arena->New<AlternativeSignature>(context.source_loc(), $1, $2); }
@@ -728,10 +731,15 @@ alternative_list_contents:
 declaration:
   function_declaration
     { $$ = $1; }
-| CLASS identifier LEFT_CURLY_BRACE member_list RIGHT_CURLY_BRACE
+| CLASS identifier LEFT_CURLY_BRACE declaration_list RIGHT_CURLY_BRACE
     { $$ = arena->New<ClassDeclaration>(context.source_loc(), $2, $4); }
 | CHOICE identifier LEFT_CURLY_BRACE alternative_list RIGHT_CURLY_BRACE
     { $$ = arena->New<ChoiceDeclaration>(context.source_loc(), $2, $4); }
+| VAR variable_declaration SEMICOLON
+    {
+      $$ = arena->New<VariableDeclaration>(context.source_loc(), $2,
+                                           std::nullopt);
+    }
 | VAR variable_declaration EQUAL expression SEMICOLON
     { $$ = arena->New<VariableDeclaration>(context.source_loc(), $2, $4); }
 ;

+ 29 - 0
executable_semantics/testdata/class/bound_method.carbon

@@ -0,0 +1,29 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// RUN: %{executable_semantics} %s 2>&1 | \
+// RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
+// RUN: %{executable_semantics} --trace %s 2>&1 | \
+// RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes %s
+// AUTOUPDATE: %{executable_semantics} %s
+// CHECK: result: 0
+
+package ExecutableSemanticsTest api;
+
+class Point {
+  fn Origin() -> Point {
+    return {.x = 0, .y = 0};
+  }
+  fn GetX[me: Point]() -> i32 {
+    return me.x;
+  }
+  var x: i32;
+  var y: i32;
+}
+
+fn Main() -> i32 {
+  var p: Point = Point.Origin();
+  var f: __Fn()-> i32 = p.GetX;
+  return f();
+}

+ 27 - 0
executable_semantics/testdata/class/class_function.carbon

@@ -0,0 +1,27 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// RUN: %{executable_semantics} %s 2>&1 | \
+// RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
+// RUN: %{executable_semantics} --trace %s 2>&1 | \
+// RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes %s
+// AUTOUPDATE: %{executable_semantics} %s
+// CHECK: result: 0
+
+package ExecutableSemanticsTest api;
+
+class Point {
+
+  fn Origin() -> Point {
+    return {.x = 0, .y = 0};
+  }
+
+  var x: i32;
+  var y: i32;
+}
+
+fn Main() -> i32 {
+  var p: Point = Point.Origin();
+  return p.x;
+}

+ 27 - 0
executable_semantics/testdata/class/class_function_from_instance.carbon

@@ -0,0 +1,27 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// RUN: %{executable_semantics} %s 2>&1 | \
+// RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
+// RUN: %{executable_semantics} --trace %s 2>&1 | \
+// RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes %s
+// AUTOUPDATE: %{executable_semantics} %s
+// CHECK: result: 0
+
+package ExecutableSemanticsTest api;
+
+class Point {
+
+  fn Origin() -> Point {
+    return {.x = 0, .y = 0};
+  }
+
+  var x: i32;
+  var y: i32;
+}
+
+fn Main() -> i32 {
+  var p: Point = Point.Origin();
+  return p.Origin().x;
+}

+ 27 - 0
executable_semantics/testdata/class/class_function_value.carbon

@@ -0,0 +1,27 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// RUN: %{executable_semantics} %s 2>&1 | \
+// RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
+// RUN: %{executable_semantics} --trace %s 2>&1 | \
+// RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes %s
+// AUTOUPDATE: %{executable_semantics} %s
+// CHECK: result: 0
+
+package ExecutableSemanticsTest api;
+
+class Point {
+
+  fn Origin() -> Point {
+    return {.x = 0, .y = 0};
+  }
+
+  var x: i32;
+  var y: i32;
+}
+
+fn Main() -> i32 {
+  var f: __Fn()->Point = Point.Origin;
+  return f().x;
+}

+ 30 - 0
executable_semantics/testdata/class/fail_method_from_class.carbon

@@ -0,0 +1,30 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// RUN: %{not} %{executable_semantics} %s 2>&1 | \
+// RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
+// RUN: %{not} %{executable_semantics} --trace %s 2>&1 | \
+// RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes %s
+// AUTOUPDATE: %{executable_semantics} %s
+// CHECK: COMPILATION ERROR: {{.*}}/executable_semantics/testdata/class/fail_method_from_class.carbon:29: GetX is not a class function
+
+package ExecutableSemanticsTest api;
+
+class Point {
+
+  fn Origin() -> Point {
+    return {.x = 0, .y = 0};
+  }
+
+  fn GetX[me: Point]() -> i32 {
+    return me.x;
+  }
+
+  var x: i32;
+  var y: i32;
+}
+
+fn Main() -> i32 {
+  return Point.GetX();
+}

+ 31 - 0
executable_semantics/testdata/class/method.carbon

@@ -0,0 +1,31 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// RUN: %{executable_semantics} %s 2>&1 | \
+// RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
+// RUN: %{executable_semantics} --trace %s 2>&1 | \
+// RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes %s
+// AUTOUPDATE: %{executable_semantics} %s
+// CHECK: result: 0
+
+package ExecutableSemanticsTest api;
+
+class Point {
+
+  fn Origin() -> Point {
+    return {.x = 0, .y = 0};
+  }
+
+  fn GetX[me: Point]() -> i32 {
+    return me.x;
+  }
+
+  var x: i32;
+  var y: i32;
+}
+
+fn Main() -> i32 {
+  var p: Point = Point.Origin();
+  return p.GetX();
+}

+ 35 - 0
executable_semantics/testdata/class/method_call_method.carbon

@@ -0,0 +1,35 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// RUN: %{executable_semantics} %s 2>&1 | \
+// RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
+// RUN: %{executable_semantics} --trace %s 2>&1 | \
+// RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes %s
+// AUTOUPDATE: %{executable_semantics} %s
+// CHECK: result: 0
+
+package ExecutableSemanticsTest api;
+
+class Point {
+
+  fn Origin() -> Point {
+    return {.x = 0, .y = 0};
+  }
+
+  fn GetX[me: Point]() -> i32 {
+    return me.x;
+  }
+
+  fn GetXY[me: Point]() -> (i32, i32) {
+    return (me.GetX(), me.y);
+  }
+
+  var x: i32;
+  var y: i32;
+}
+
+fn Main() -> i32 {
+  var p: Point = Point.Origin();
+  return p.GetXY()[0];
+}