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

interfaces, impls, and constrained generics (basics) (#1073)

* interfaces, impls, and constrained generics (basics)

* separate type checking into declare vs. type check, removing redundancy

* external impls

* added impl scopes to handle generics calling generics

* cleanup

* more cleanup

* Update executable_semantics/testdata/interface/external_impl_point_vector.carbon

Co-authored-by: josh11b <josh11b@users.noreply.github.com>

* Update executable_semantics/testdata/interface/generic_call_generic.carbon

Co-authored-by: josh11b <josh11b@users.noreply.github.com>

* Update executable_semantics/testdata/interface/tuple_vector_add_scale.carbon

Co-authored-by: josh11b <josh11b@users.noreply.github.com>

* Update executable_semantics/testdata/interface/vector_point_add_scale.carbon

Co-authored-by: josh11b <josh11b@users.noreply.github.com>

* change ImplementationDeclaration to ImplDeclaration

* remove impl_type_value

* split NamedEntity into two

* changed GetName to be a free function

* adding comments

* more edits to respond to review

* introduce ImplBinding, remove punning on GenericBinding

* new test case and some minor edits

* refactor GetMember and GetField to move impl logic to interpreter

* remove commennt

* change EntityView to ImplBinding in FieldAccess...

* move ImplBinding

* review response

* added example to impl_scope.h

* minor edits

* Update executable_semantics/interpreter/field_path.h

Co-authored-by: Geoff Romer <gromer@google.com>

* Update executable_semantics/interpreter/value.cpp

Co-authored-by: Geoff Romer <gromer@google.com>

* Update executable_semantics/interpreter/interpreter.cpp

Co-authored-by: Geoff Romer <gromer@google.com>

* Update executable_semantics/ast/expression.h

Co-authored-by: Geoff Romer <gromer@google.com>

* Update executable_semantics/ast/expression.h

Co-authored-by: Geoff Romer <gromer@google.com>

* Update executable_semantics/ast/generic_binding.h

Co-authored-by: Geoff Romer <gromer@google.com>

* more edits from review

* review response

* Update executable_semantics/ast/static_scope.h

Co-authored-by: Geoff Romer <gromer@google.com>

* remove ImplType, renamed node_view to value_node

Co-authored-by: josh11b <josh11b@users.noreply.github.com>
Co-authored-by: Geoff Romer <gromer@google.com>
Jeremy G. Siek 4 лет назад
Родитель
Сommit
7cce1bd124
43 измененных файлов с 1693 добавлено и 415 удалено
  1. 18 0
      executable_semantics/ast/BUILD
  2. 1 1
      executable_semantics/ast/README.md
  3. 3 0
      executable_semantics/ast/ast_rtti.txt
  4. 44 0
      executable_semantics/ast/declaration.cpp
  5. 107 57
      executable_semantics/ast/declaration.h
  6. 46 13
      executable_semantics/ast/expression.h
  7. 139 0
      executable_semantics/ast/generic_binding.h
  8. 6 8
      executable_semantics/ast/pattern.h
  9. 4 6
      executable_semantics/ast/return_term.h
  10. 5 7
      executable_semantics/ast/statement.h
  11. 5 5
      executable_semantics/ast/static_scope.cpp
  12. 47 52
      executable_semantics/ast/static_scope.h
  13. 10 2
      executable_semantics/interpreter/BUILD
  14. 9 9
      executable_semantics/interpreter/action.cpp
  15. 5 5
      executable_semantics/interpreter/action.h
  16. 10 10
      executable_semantics/interpreter/action_stack.cpp
  17. 5 5
      executable_semantics/interpreter/action_stack.h
  18. 32 4
      executable_semantics/interpreter/field_path.h
  19. 3 2
      executable_semantics/interpreter/heap.cpp
  20. 2 2
      executable_semantics/interpreter/heap.h
  21. 78 0
      executable_semantics/interpreter/impl_scope.cpp
  22. 83 0
      executable_semantics/interpreter/impl_scope.h
  23. 35 8
      executable_semantics/interpreter/interpreter.cpp
  24. 0 1
      executable_semantics/interpreter/interpreter.h
  25. 16 1
      executable_semantics/interpreter/resolve_control_flow.cpp
  26. 35 1
      executable_semantics/interpreter/resolve_names.cpp
  27. 345 144
      executable_semantics/interpreter/type_checker.cpp
  28. 54 21
      executable_semantics/interpreter/type_checker.h
  29. 96 37
      executable_semantics/interpreter/value.cpp
  30. 73 11
      executable_semantics/interpreter/value.h
  31. 6 0
      executable_semantics/syntax/lexer.lpp
  32. 19 0
      executable_semantics/syntax/parser.ypp
  33. 0 1
      executable_semantics/testdata/basic_syntax/trace.carbon
  34. 1 1
      executable_semantics/testdata/global_variable/fail_init_order.carbon
  35. 44 0
      executable_semantics/testdata/interface/external_impl_point_vector.carbon
  36. 43 0
      executable_semantics/testdata/interface/fail_impl_bad_member.carbon
  37. 40 0
      executable_semantics/testdata/interface/fail_impl_missing_member.carbon
  38. 33 0
      executable_semantics/testdata/interface/fail_no_impl.carbon
  39. 45 0
      executable_semantics/testdata/interface/generic_call_generic.carbon
  40. 59 0
      executable_semantics/testdata/interface/generic_with_two_params.carbon
  41. 43 0
      executable_semantics/testdata/interface/tuple_vector_add_scale.carbon
  42. 43 0
      executable_semantics/testdata/interface/vector_point_add_scale.carbon
  43. 1 1
      executable_semantics/testdata/tuple/fail_index_var.carbon

+ 18 - 0
executable_semantics/ast/BUILD

@@ -69,6 +69,22 @@ cc_test(
     ],
 )
 
+cc_library(
+    name = "generic_binding",
+    hdrs = [
+        "generic_binding.h",
+    ],
+    deps = [
+        ":ast_node",
+        ":source_location",
+        ":value_category",
+        "//common:check",
+        "//common:ostream",
+        "//executable_semantics/common:nonnull",
+        "@llvm-project//llvm:Support",
+    ],
+)
+
 cc_library(
     name = "declaration",
     srcs = ["declaration.cpp"],
@@ -77,6 +93,7 @@ cc_library(
     ],
     deps = [
         ":ast_node",
+        ":generic_binding",
         ":pattern",
         ":return_term",
         ":source_location",
@@ -108,6 +125,7 @@ cc_library(
     hdrs = ["expression.h"],
     deps = [
         ":ast_node",
+        ":generic_binding",
         ":paren_contents",
         ":source_location",
         ":static_scope",

+ 1 - 1
executable_semantics/ast/README.md

@@ -40,5 +40,5 @@ inheritance, we handle these cases using a form of type erasure: we specify a
 notional interface that those types conform to, and then define a "view" class
 that behaves like a pointer to an instance of that interface. Types declare that
 they model an interface `Foo` by defining a public static member named
-`ImplementsCarbonFoo`. See [NamedEntityView](static_scope.h) for an example of
+`ImplementsCarbonFoo`. See [ValueNodeView](static_scope.h) for an example of
 this pattern.

+ 3 - 0
executable_semantics/ast/ast_rtti.txt

@@ -14,7 +14,10 @@ abstract class Declaration : AstNode;
   class ClassDeclaration : Declaration;
   class ChoiceDeclaration : Declaration;
   class VariableDeclaration : Declaration;
+  class InterfaceDeclaration : Declaration;
+  class ImplDeclaration : Declaration;
 class GenericBinding : AstNode;
+class ImplBinding : AstNode;
 class AlternativeSignature : AstNode;
 abstract class Statement : AstNode;
   class ExpressionStatement : Statement;

+ 44 - 0
executable_semantics/ast/declaration.cpp

@@ -15,6 +15,32 @@ Declaration::~Declaration() = default;
 
 void Declaration::Print(llvm::raw_ostream& out) const {
   switch (kind()) {
+    case DeclarationKind::InterfaceDeclaration: {
+      const auto& iface_decl = cast<InterfaceDeclaration>(*this);
+      out << "interface " << iface_decl.name() << " {\n";
+      for (Nonnull<Declaration*> m : iface_decl.members()) {
+        out << *m;
+      }
+      out << "}\n";
+      break;
+    }
+    case DeclarationKind::ImplDeclaration: {
+      const auto& impl_decl = cast<ImplDeclaration>(*this);
+      switch (impl_decl.kind()) {
+        case ImplKind::InternalImpl:
+          break;
+        case ImplKind::ExternalImpl:
+          out << "external ";
+          break;
+      }
+      out << "impl " << *impl_decl.impl_type() << " as "
+          << impl_decl.interface() << " {\n";
+      for (Nonnull<Declaration*> m : impl_decl.members()) {
+        out << *m;
+      }
+      out << "}\n";
+      break;
+    }
     case DeclarationKind::FunctionDeclaration:
       cast<FunctionDeclaration>(*this).PrintDepth(-1, out);
       break;
@@ -51,6 +77,23 @@ void Declaration::Print(llvm::raw_ostream& out) const {
   }
 }
 
+auto GetName(const Declaration& declaration) -> std::optional<std::string> {
+  switch (declaration.kind()) {
+    case DeclarationKind::FunctionDeclaration:
+      return cast<FunctionDeclaration>(declaration).name();
+    case DeclarationKind::ClassDeclaration:
+      return cast<ClassDeclaration>(declaration).name();
+    case DeclarationKind::ChoiceDeclaration:
+      return cast<ChoiceDeclaration>(declaration).name();
+    case DeclarationKind::InterfaceDeclaration:
+      return cast<InterfaceDeclaration>(declaration).name();
+    case DeclarationKind::VariableDeclaration:
+      return cast<VariableDeclaration>(declaration).binding().name();
+    case DeclarationKind::ImplDeclaration:
+      return std::nullopt;
+  }
+}
+
 void GenericBinding::Print(llvm::raw_ostream& out) const {
   out << name() << ":! " << type();
 }
@@ -63,6 +106,7 @@ void ReturnTerm::Print(llvm::raw_ostream& out) const {
       out << "-> auto";
       return;
     case ReturnKind::Expression:
+      CHECK(type_expression_.has_value());
       out << "-> " << **type_expression_;
       return;
   }

+ 107 - 57
executable_semantics/ast/declaration.h

@@ -11,6 +11,7 @@
 
 #include "common/ostream.h"
 #include "executable_semantics/ast/ast_node.h"
+#include "executable_semantics/ast/generic_binding.h"
 #include "executable_semantics/ast/pattern.h"
 #include "executable_semantics/ast/return_term.h"
 #include "executable_semantics/ast/source_location.h"
@@ -56,7 +57,10 @@ class Declaration : public AstNode {
 
   // Sets the static type of the declared entity. Can only be called once,
   // during typechecking.
-  void set_static_type(Nonnull<const Value*> type) { static_type_ = type; }
+  void set_static_type(Nonnull<const Value*> type) {
+    CHECK(!static_type_.has_value());
+    static_type_ = type;
+  }
 
   // Returns whether the static type has been set. Should only be called
   // during typechecking: before typechecking it's guaranteed to be false,
@@ -74,62 +78,9 @@ class Declaration : public AstNode {
   std::optional<Nonnull<const Value*>> static_type_;
 };
 
-// TODO: expand the kinds of things that can be deduced parameters.
-//   For now, only generic parameters are supported.
-class GenericBinding : public AstNode {
- public:
-  using ImplementsCarbonNamedEntity = void;
-
-  GenericBinding(SourceLocation source_loc, std::string name,
-                 Nonnull<Expression*> type)
-      : AstNode(AstNodeKind::GenericBinding, source_loc),
-        name_(std::move(name)),
-        type_(type) {}
-
-  void Print(llvm::raw_ostream& out) const override;
-
-  static auto classof(const AstNode* node) -> bool {
-    return InheritsFromGenericBinding(node->kind());
-  }
-
-  auto name() const -> const std::string& { return name_; }
-  auto type() const -> const Expression& { return *type_; }
-  auto type() -> Expression& { return *type_; }
-
-  // The static type of the binding. Cannot be called before typechecking.
-  auto static_type() const -> const Value& { return **static_type_; }
-
-  // Sets the static type of the binding. Can only be called once, during
-  // typechecking.
-  void set_static_type(Nonnull<const Value*> type) { static_type_ = type; }
-
-  // Returns whether the 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 value_category() const -> ValueCategory { return ValueCategory::Let; }
-  auto constant_value() const -> std::optional<Nonnull<const Value*>> {
-    return constant_value_;
-  }
-
-  // Sets the value returned by constant_value(). Can only be called once,
-  // during typechecking.
-  void set_constant_value(Nonnull<const Value*> value) {
-    CHECK(!constant_value_.has_value());
-    constant_value_ = value;
-  }
-
- private:
-  std::string name_;
-  Nonnull<Expression*> type_;
-  std::optional<Nonnull<const Value*>> static_type_;
-  std::optional<Nonnull<const Value*>> constant_value_;
-};
-
 class FunctionDeclaration : public Declaration {
  public:
-  using ImplementsCarbonNamedEntity = void;
+  using ImplementsCarbonValueNode = void;
 
   FunctionDeclaration(SourceLocation source_loc, std::string name,
                       std::vector<Nonnull<AstNode*>> deduced_params,
@@ -196,7 +147,7 @@ class FunctionDeclaration : public Declaration {
 
 class ClassDeclaration : public Declaration {
  public:
-  using ImplementsCarbonNamedEntity = void;
+  using ImplementsCarbonValueNode = void;
 
   ClassDeclaration(SourceLocation source_loc, std::string name,
                    std::vector<Nonnull<Declaration*>> members)
@@ -256,7 +207,7 @@ class AlternativeSignature : public AstNode {
 
 class ChoiceDeclaration : public Declaration {
  public:
-  using ImplementsCarbonNamedEntity = void;
+  using ImplementsCarbonValueNode = void;
 
   ChoiceDeclaration(SourceLocation source_loc, std::string name,
                     std::vector<Nonnull<AlternativeSignature*>> alternatives)
@@ -324,6 +275,105 @@ class VariableDeclaration : public Declaration {
   std::optional<Nonnull<Expression*>> initializer_;
 };
 
+class InterfaceDeclaration : public Declaration {
+ public:
+  using ImplementsCarbonValueNode = void;
+
+  InterfaceDeclaration(SourceLocation source_loc, std::string name,
+                       Nonnull<GenericBinding*> self,
+                       std::vector<Nonnull<Declaration*>> members)
+      : Declaration(AstNodeKind::InterfaceDeclaration, source_loc),
+        name_(std::move(name)),
+        members_(std::move(members)),
+        self_(self) {}
+
+  static auto classof(const AstNode* node) -> bool {
+    return InheritsFromInterfaceDeclaration(node->kind());
+  }
+
+  auto name() const -> const std::string& { return name_; }
+  auto members() const -> llvm::ArrayRef<Nonnull<Declaration*>> {
+    return members_;
+  }
+  auto self() const -> Nonnull<const GenericBinding*> { return self_; }
+  auto self() -> Nonnull<GenericBinding*> { return self_; }
+
+  auto value_category() const -> ValueCategory { return ValueCategory::Let; }
+  auto constant_value() const -> std::optional<Nonnull<const Value*>> {
+    return constant_value_;
+  }
+
+  // Sets the value returned by constant_value(). Can only be called once,
+  // during typechecking.
+  void set_constant_value(Nonnull<const Value*> value) {
+    CHECK(!constant_value_.has_value());
+    constant_value_ = value;
+  }
+
+ private:
+  std::string name_;
+  std::vector<Nonnull<Declaration*>> members_;
+  std::optional<Nonnull<const Value*>> constant_value_;
+  Nonnull<GenericBinding*> self_;
+};
+
+enum class ImplKind { InternalImpl, ExternalImpl };
+
+class ImplDeclaration : public Declaration {
+ public:
+  using ImplementsCarbonValueNode = void;
+
+  ImplDeclaration(SourceLocation source_loc, ImplKind kind,
+                  Nonnull<Expression*> impl_type,
+                  Nonnull<Expression*> interface,
+                  std::vector<Nonnull<Declaration*>> members)
+      : Declaration(AstNodeKind::ImplDeclaration, source_loc),
+        kind_(kind),
+        impl_type_(impl_type),
+        interface_(interface),
+        members_(members) {}
+
+  static auto classof(const AstNode* node) -> bool {
+    return InheritsFromImplDeclaration(node->kind());
+  }
+  // Return whether this is an external or internal impl.
+  auto kind() const -> ImplKind { return kind_; }
+  // Return the type that is doing the implementing.
+  auto impl_type() const -> Nonnull<Expression*> { return impl_type_; }
+  // Return the interface that is being implemented.
+  auto interface() const -> const Expression& { return *interface_; }
+  auto interface() -> Expression& { return *interface_; }
+  void set_interface_type(Nonnull<const Value*> iface_type) {
+    interface_type_ = iface_type;
+  }
+  auto interface_type() const -> Nonnull<const Value*> {
+    return *interface_type_;
+  }
+  auto members() const -> llvm::ArrayRef<Nonnull<Declaration*>> {
+    return members_;
+  }
+  // Return the witness table for this impl.
+  auto constant_value() const -> std::optional<Nonnull<const Value*>> {
+    return constant_value_;
+  }
+  void set_constant_value(Nonnull<const Value*> value) {
+    CHECK(!constant_value_.has_value());
+    constant_value_ = value;
+  }
+  auto value_category() const -> ValueCategory { return ValueCategory::Let; }
+
+ private:
+  ImplKind kind_;
+  Nonnull<Expression*> impl_type_;  // TODO: make this optional
+  Nonnull<Expression*> interface_;
+  std::optional<Nonnull<const Value*>> interface_type_;
+  std::vector<Nonnull<Declaration*>> members_;
+  std::optional<Nonnull<const Value*>> constant_value_;
+};
+
+// Return the name of a declaration, if it has one.
+auto GetName(const Declaration&) -> std::optional<std::string>;
+
 }  // namespace Carbon
 
 #endif  // EXECUTABLE_SEMANTICS_AST_DECLARATION_H_

+ 46 - 13
executable_semantics/ast/expression.h

@@ -12,6 +12,7 @@
 
 #include "common/ostream.h"
 #include "executable_semantics/ast/ast_node.h"
+#include "executable_semantics/ast/generic_binding.h"
 #include "executable_semantics/ast/paren_contents.h"
 #include "executable_semantics/ast/source_location.h"
 #include "executable_semantics/ast/static_scope.h"
@@ -23,6 +24,7 @@
 namespace Carbon {
 
 class Value;
+class VariableType;
 
 class Expression : public AstNode {
  public:
@@ -45,12 +47,10 @@ class Expression : public AstNode {
 
   // Sets the static type of this expression. Can only be called once, during
   // typechecking.
-  void set_static_type(Nonnull<const Value*> type) { static_type_ = type; }
-
-  // Returns whether the 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(); }
+  void set_static_type(Nonnull<const Value*> type) {
+    CHECK(!static_type_.has_value());
+    static_type_ = type;
+  }
 
   // The value category of this expression. Cannot be called before
   // typechecking.
@@ -123,20 +123,20 @@ class IdentifierExpression : public Expression {
 
   auto name() const -> const std::string& { return name_; }
 
-  // Returns the NamedEntityView this identifier refers to. Cannot be called
+  // Returns the ValueNodeView this identifier refers to. Cannot be called
   // before name resolution.
-  auto named_entity() const -> const NamedEntityView& { return *named_entity_; }
+  auto value_node() const -> const ValueNodeView& { return *value_node_; }
 
-  // Sets the value returned by named_entity. Can be called only once,
+  // Sets the value returned by value_node. Can be called only once,
   // during name resolution.
-  void set_named_entity(NamedEntityView named_entity) {
-    CHECK(!named_entity_.has_value());
-    named_entity_ = std::move(named_entity);
+  void set_value_node(ValueNodeView value_node) {
+    CHECK(!value_node_.has_value());
+    value_node_ = std::move(value_node);
   }
 
  private:
   std::string name_;
-  std::optional<NamedEntityView> named_entity_;
+  std::optional<ValueNodeView> value_node_;
 };
 
 class FieldAccessExpression : public Expression {
@@ -156,9 +156,23 @@ class FieldAccessExpression : public Expression {
   auto aggregate() -> Expression& { return *aggregate_; }
   auto field() const -> const std::string& { return field_; }
 
+  // If `aggregate` has a generic type, returns the `ImplBinding` that
+  // identifies its witness table. Otherwise, returns `std::nullopt`. Should not
+  // be called before typechecking.
+  auto impl() const -> std::optional<Nonnull<const ImplBinding*>> {
+    return impl_;
+  }
+
+  // Can only be called once, during typechecking.
+  void set_impl(Nonnull<const ImplBinding*> impl) {
+    CHECK(!impl_.has_value());
+    impl_ = impl;
+  }
+
  private:
   Nonnull<Expression*> aggregate_;
   std::string field_;
+  std::optional<Nonnull<const ImplBinding*>> impl_;
 };
 
 class IndexExpression : public Expression {
@@ -340,6 +354,8 @@ class PrimitiveOperatorExpression : public Expression {
   std::vector<Nonnull<Expression*>> arguments_;
 };
 
+class ImplBinding;
+
 class CallExpression : public Expression {
  public:
   explicit CallExpression(SourceLocation source_loc,
@@ -358,9 +374,26 @@ class CallExpression : public Expression {
   auto argument() const -> const Expression& { return *argument_; }
   auto argument() -> Expression& { return *argument_; }
 
+  // Maps each of `function`'s generic parameters to the AST node
+  // that identifies the witness table for the corresponding argument.
+  // Should not be called before typechecking, or if `function` is not
+  // a generic function.
+  auto impls() const
+      -> const std::map<Nonnull<const ImplBinding*>, ValueNodeView>& {
+    return impls_;
+  }
+
+  // Can only be called once, during typechecking.
+  void set_impls(
+      const std::map<Nonnull<const ImplBinding*>, ValueNodeView>& impls) {
+    CHECK(impls_.empty());
+    impls_ = impls;
+  }
+
  private:
   Nonnull<Expression*> function_;
   Nonnull<Expression*> argument_;
+  std::map<Nonnull<const ImplBinding*>, ValueNodeView> impls_;
 };
 
 class FunctionTypeLiteral : public Expression {

+ 139 - 0
executable_semantics/ast/generic_binding.h

@@ -0,0 +1,139 @@
+// 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_GENERIC_BINDING_H_
+#define EXECUTABLE_SEMANTICS_AST_GENERIC_BINDING_H_
+
+#include <map>
+
+#include "common/check.h"
+#include "common/ostream.h"
+#include "executable_semantics/ast/ast_node.h"
+#include "executable_semantics/ast/value_category.h"
+
+namespace Carbon {
+
+class Value;
+class Expression;
+class ImplBinding;
+
+// TODO: expand the kinds of things that can be deduced parameters.
+//   For now, only generic parameters are supported.
+class GenericBinding : public AstNode {
+ public:
+  using ImplementsCarbonValueNode = void;
+
+  GenericBinding(SourceLocation source_loc, std::string name,
+                 Nonnull<Expression*> type)
+      : AstNode(AstNodeKind::GenericBinding, source_loc),
+        name_(std::move(name)),
+        type_(type) {}
+
+  void Print(llvm::raw_ostream& out) const override;
+
+  static auto classof(const AstNode* node) -> bool {
+    return InheritsFromGenericBinding(node->kind());
+  }
+
+  auto name() const -> const std::string& { return name_; }
+  auto type() const -> const Expression& { return *type_; }
+  auto type() -> Expression& { return *type_; }
+
+  // The static type of the binding. Cannot be called before typechecking.
+  auto static_type() const -> const Value& { return **static_type_; }
+
+  // Sets the static type of the binding. Can only be called once, during
+  // typechecking.
+  void set_static_type(Nonnull<const Value*> type) {
+    CHECK(!static_type_.has_value());
+    static_type_ = type;
+  }
+
+  auto value_category() const -> ValueCategory { return ValueCategory::Let; }
+  auto constant_value() const -> std::optional<Nonnull<const Value*>> {
+    return constant_value_;
+  }
+
+  // Sets the value returned by constant_value(). Can only be called once,
+  // during typechecking.
+  void set_constant_value(Nonnull<const Value*> value) {
+    CHECK(!constant_value_.has_value());
+    constant_value_ = value;
+  }
+
+  // The impl binding associated with this type variable.
+  auto impl_binding() const -> std::optional<Nonnull<const ImplBinding*>> {
+    return impl_binding_;
+  }
+  // Set the impl binding.
+  void set_impl_binding(Nonnull<const ImplBinding*> binding) {
+    CHECK(!impl_binding_.has_value());
+    impl_binding_ = binding;
+  }
+
+ private:
+  std::string name_;
+  Nonnull<Expression*> type_;
+  std::optional<Nonnull<const Value*>> static_type_;
+  std::optional<Nonnull<const Value*>> constant_value_;
+  std::optional<Nonnull<const ImplBinding*>> impl_binding_;
+};
+
+using BindingMap =
+    std::map<Nonnull<const GenericBinding*>, Nonnull<const Value*>>;
+
+// The run-time counterpart of a `GenericBinding`.
+//
+// Once a generic binding has been declared, it can be used
+// in two different ways: as a compile-time constant with a
+// symbolic value (such as a `VariableType`), or as a run-time
+// variable with a concrete value that is stored on the stack.
+// An `ImplBinding` is used in contexts where the second
+// interpretation is intended.
+class ImplBinding : public AstNode {
+ public:
+  using ImplementsCarbonValueNode = void;
+
+  ImplBinding(SourceLocation source_loc,
+              Nonnull<const GenericBinding*> type_var,
+              Nonnull<const Value*> iface)
+      : AstNode(AstNodeKind::ImplBinding, source_loc),
+        type_var_(type_var),
+        iface_(iface) {}
+
+  static auto classof(const AstNode* node) -> bool {
+    return InheritsFromImplBinding(node->kind());
+  }
+  void Print(llvm::raw_ostream& out) const override;
+
+  // The binding for the type variable.
+  auto type_var() const -> Nonnull<const GenericBinding*> { return type_var_; }
+  // The interface being implemented.
+  auto interface() const -> Nonnull<const Value*> { return iface_; }
+
+  // Required for the the ValueNode interface
+  auto constant_value() const -> std::optional<Nonnull<const Value*>> {
+    return std::nullopt;
+  }
+
+  // The static type of the impl. Cannot be called before typechecking.
+  auto static_type() const -> const Value& { return **static_type_; }
+
+  // Sets the static type of the impl. Can only be called once, during
+  // typechecking.
+  void set_static_type(Nonnull<const Value*> type) {
+    CHECK(!static_type_.has_value());
+    static_type_ = type;
+  }
+  auto value_category() const -> ValueCategory { return ValueCategory::Let; }
+
+ private:
+  Nonnull<const GenericBinding*> type_var_;
+  Nonnull<const Value*> iface_;
+  std::optional<Nonnull<const Value*>> static_type_;
+};
+
+}  // namespace Carbon
+
+#endif  // EXECUTABLE_SEMANTICS_AST_GENERIC_BINDING_H_

+ 6 - 8
executable_semantics/ast/pattern.h

@@ -54,12 +54,10 @@ class Pattern : public AstNode {
 
   // Sets the static type of this expression. Can only be called once, during
   // typechecking.
-  void set_static_type(Nonnull<const Value*> type) { static_type_ = type; }
-
-  // Returns whether the 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(); }
+  void set_static_type(Nonnull<const Value*> type) {
+    CHECK(!static_type_.has_value());
+    static_type_ = type;
+  }
 
   // The value of this pattern. Cannot be called before typechecking.
   // TODO rename to avoid confusion with BindingPattern::constant_value
@@ -101,7 +99,7 @@ class AutoPattern : public Pattern {
 // a name to it.
 class BindingPattern : public Pattern {
  public:
-  using ImplementsCarbonNamedEntity = void;
+  using ImplementsCarbonValueNode = void;
 
   BindingPattern(SourceLocation source_loc, std::string name,
                  Nonnull<Pattern*> type)
@@ -115,7 +113,7 @@ class BindingPattern : public Pattern {
 
   // The name this pattern binds, if any. If equal to AnonymousName, indicates
   // that this BindingPattern does not bind a name, which in turn means it
-  // should not be used as a NamedEntity.
+  // should not be used as a ValueNode.
   auto name() const -> const std::string& { return name_; }
 
   // The pattern specifying the type of values that this pattern matches.

+ 4 - 6
executable_semantics/ast/return_term.h

@@ -65,12 +65,10 @@ class ReturnTerm {
 
   // 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(); }
+  void set_static_type(Nonnull<const Value*> type) {
+    CHECK(!static_type_.has_value());
+    static_type_ = type;
+  }
 
   auto source_loc() const -> SourceLocation { return source_loc_; }
 

+ 5 - 7
executable_semantics/ast/statement.h

@@ -314,7 +314,7 @@ class Match : public Statement {
 //     }
 class Continuation : public Statement {
  public:
-  using ImplementsCarbonNamedEntity = void;
+  using ImplementsCarbonValueNode = void;
 
   Continuation(SourceLocation source_loc, std::string name,
                Nonnull<Block*> body)
@@ -338,12 +338,10 @@ class Continuation : public Statement {
 
   // Sets the static type of the continuation. Can only be called once,
   // during typechecking.
-  void set_static_type(Nonnull<const Value*> type) { static_type_ = type; }
-
-  // Returns whether the 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(); }
+  void set_static_type(Nonnull<const Value*> type) {
+    CHECK(!static_type_.has_value());
+    static_type_ = type;
+  }
 
   auto value_category() const -> ValueCategory { return ValueCategory::Var; }
   auto constant_value() const -> std::optional<Nonnull<const Value*>> {

+ 5 - 5
executable_semantics/ast/static_scope.cpp

@@ -8,7 +8,7 @@
 
 namespace Carbon {
 
-void StaticScope::Add(std::string name, NamedEntityView entity) {
+void StaticScope::Add(std::string name, ValueNodeView entity) {
   auto [it, success] = declared_names_.insert({name, entity});
   if (!success && it->second != entity) {
     FATAL_COMPILATION_ERROR(entity.base().source_loc())
@@ -18,8 +18,8 @@ void StaticScope::Add(std::string name, NamedEntityView entity) {
 }
 
 auto StaticScope::Resolve(const std::string& name,
-                          SourceLocation source_loc) const -> NamedEntityView {
-  std::optional<NamedEntityView> result = TryResolve(name, source_loc);
+                          SourceLocation source_loc) const -> ValueNodeView {
+  std::optional<ValueNodeView> result = TryResolve(name, source_loc);
   if (!result.has_value()) {
     FATAL_COMPILATION_ERROR(source_loc) << "could not resolve '" << name << "'";
   }
@@ -28,12 +28,12 @@ auto StaticScope::Resolve(const std::string& name,
 
 auto StaticScope::TryResolve(const std::string& name,
                              SourceLocation source_loc) const
-    -> std::optional<NamedEntityView> {
+    -> std::optional<ValueNodeView> {
   auto it = declared_names_.find(name);
   if (it != declared_names_.end()) {
     return it->second;
   }
-  std::optional<NamedEntityView> result;
+  std::optional<ValueNodeView> result;
   for (Nonnull<const StaticScope*> parent : parent_scopes_) {
     auto parent_result = parent->TryResolve(name, source_loc);
     if (parent_result.has_value() && result.has_value() &&

+ 47 - 52
executable_semantics/ast/static_scope.h

@@ -21,78 +21,78 @@ namespace Carbon {
 
 class Value;
 
-// The placeholder name exposed by anonymous NamedEntities.
+// The placeholder name exposed by anonymous ValueNodes.
 static constexpr std::string_view AnonymousName = "_";
 
-// True if NodeType::ImplementsCarbonNamedEntity is valid and names a type,
-// indicating that NodeType implements the NamedEntity interface, which means
-// it must define the following methods, with contracts as documented.
+// ImplementsValueNode is true if NodeType::ImplementsCarbonValueNode
+// is valid and names a type, indicating that NodeType implements the
+// ValueNode interface, defined below.
+
+template <typename NodeType, typename = void>
+static constexpr bool ImplementsValueNode = false;
+
 /*
+  ValueNode is an interface implemented by AstNodes that can be associated
+  with a value, such as declarations and bindings. The interface consists of
+  the following methods:
+
   // Returns the static type of an IdentifierExpression that names *this.
   auto static_type() const -> const Value&;
 
   // Returns the value category of an IdentifierExpression that names *this.
   auto value_category() const -> ValueCategory;
 
-  // Returns the name of an IdentifierExpression that names *this. If *this
-  // is anonymous, returns AnonymousName.
-  auto name() const -> std::string_view;
+  // Print the node for diagnostic or tracing purposes.
+  void Print(llvm::raw_ostream& out) const;
+
 
-  // If *this names a compile-time constant whose value is known, returns that
-  // value. Otherwise returns std::nullopt.
-  auto constant_value() const -> std::optional<Nonnull<const Value*>>;
 */
-// NodeType must be derived from AstNode.
-//
 // TODO: consider turning the above documentation into real code, as sketched
 // at https://godbolt.org/z/186oEozhc
-template <typename T, typename = void>
-static constexpr bool ImplementsNamedEntity = false;
 
 template <typename T>
 static constexpr bool
-    ImplementsNamedEntity<T, typename T::ImplementsCarbonNamedEntity> = true;
+    ImplementsValueNode<T, typename T::ImplementsCarbonValueNode> = true;
 
-// Non-owning type-erased wrapper around a const NodeType* `node`, where
-// NodeType implements the NamedEntity interface.
-class NamedEntityView {
+class ValueNodeView {
  public:
-  // REQUIRES: node->name() != AnonymousName
   template <typename NodeType,
-            typename = std::enable_if_t<ImplementsNamedEntity<NodeType>>>
+            typename = std::enable_if_t<ImplementsValueNode<NodeType>>>
   // NOLINTNEXTLINE(google-explicit-constructor)
-  NamedEntityView(Nonnull<const NodeType*> node)
+  ValueNodeView(Nonnull<const NodeType*> node)
       // Type-erase NodeType, retaining a pointer to the base class AstNode
       // and using std::function to encapsulate the ability to call
       // the derived class's methods.
       : base_(node),
-        name_([](const AstNode& base) -> std::string_view {
-          return llvm::cast<NodeType>(base).name();
+        constant_value_(
+            [](const AstNode& base) -> std::optional<Nonnull<const Value*>> {
+              return llvm::cast<NodeType>(base).constant_value();
+            }),
+        print_([](const AstNode& base, llvm::raw_ostream& out) -> void {
+          // TODO: change this to print a summary of the node
+          return llvm::cast<NodeType>(base).Print(out);
         }),
         static_type_([](const AstNode& base) -> const Value& {
           return llvm::cast<NodeType>(base).static_type();
         }),
         value_category_([](const AstNode& base) -> ValueCategory {
           return llvm::cast<NodeType>(base).value_category();
-        }),
-        constant_value_(
-            [](const AstNode& base) -> std::optional<Nonnull<const Value*>> {
-              return llvm::cast<NodeType>(base).constant_value();
-            }) {
-    CHECK(node->name() != AnonymousName)
-        << "Entity with no name used as NamedEntity: " << *node;
-  }
+        }) {}
 
-  NamedEntityView(const NamedEntityView&) = default;
-  NamedEntityView(NamedEntityView&&) = default;
-  auto operator=(const NamedEntityView&) -> NamedEntityView& = default;
-  auto operator=(NamedEntityView&&) -> NamedEntityView& = default;
+  ValueNodeView(const ValueNodeView&) = default;
+  ValueNodeView(ValueNodeView&&) = default;
+  auto operator=(const ValueNodeView&) -> ValueNodeView& = default;
+  auto operator=(ValueNodeView&&) -> ValueNodeView& = default;
 
   // Returns `node` as an instance of the base class AstNode.
   auto base() const -> const AstNode& { return *base_; }
 
-  // Returns node->name()
-  auto name() const -> std::string_view { return name_(*base_); }
+  // Returns node->constant_value()
+  auto constant_value() const -> std::optional<Nonnull<const Value*>> {
+    return constant_value_(*base_);
+  }
+
+  void Print(llvm::raw_ostream& out) const { print_(*base_, out); }
 
   // Returns node->static_type()
   auto static_type() const -> const Value& { return static_type_(*base_); }
@@ -102,33 +102,28 @@ class NamedEntityView {
     return value_category_(*base_);
   }
 
-  // Returns node->constant_value()
-  auto constant_value() const -> std::optional<Nonnull<const Value*>> {
-    return constant_value_(*base_);
-  }
-
-  friend auto operator==(const NamedEntityView& lhs, const NamedEntityView& rhs)
+  friend auto operator==(const ValueNodeView& lhs, const ValueNodeView& rhs)
       -> bool {
     return lhs.base_ == rhs.base_;
   }
 
-  friend auto operator!=(const NamedEntityView& lhs, const NamedEntityView& rhs)
+  friend auto operator!=(const ValueNodeView& lhs, const ValueNodeView& rhs)
       -> bool {
     return lhs.base_ != rhs.base_;
   }
 
-  friend auto operator<(const NamedEntityView& lhs, const NamedEntityView& rhs)
+  friend auto operator<(const ValueNodeView& lhs, const ValueNodeView& rhs)
       -> bool {
     return std::less<>()(lhs.base_, rhs.base_);
   }
 
  private:
   Nonnull<const AstNode*> base_;
-  std::function<std::string_view(const AstNode&)> name_;
-  std::function<const Value&(const AstNode&)> static_type_;
-  std::function<ValueCategory(const AstNode&)> value_category_;
   std::function<std::optional<Nonnull<const Value*>>(const AstNode&)>
       constant_value_;
+  std::function<void(const AstNode&, llvm::raw_ostream&)> print_;
+  std::function<const Value&(const AstNode&)> static_type_;
+  std::function<ValueCategory(const AstNode&)> value_category_;
 };
 
 // Maps the names visible in a given scope to the entities they name.
@@ -138,7 +133,7 @@ class StaticScope {
  public:
   // Defines `name` to be `entity` in this scope, or reports a compilation error
   // if `name` is already defined to be a different entity in this scope.
-  void Add(std::string name, NamedEntityView entity);
+  void Add(std::string name, ValueNodeView entity);
 
   // Make `parent` a parent of this scope.
   // REQUIRES: `parent` is not already a parent of this scope.
@@ -150,17 +145,17 @@ class StaticScope {
   // scope, or reports a compilation error at `source_loc` there isn't exactly
   // one such definition.
   auto Resolve(const std::string& name, SourceLocation source_loc) const
-      -> NamedEntityView;
+      -> ValueNodeView;
 
  private:
   // Equivalent to Resolve, but returns `nullopt` instead of raising an error
   // if no definition can be found. Still raises a compilation error if more
   // than one definition is found.
   auto TryResolve(const std::string& name, SourceLocation source_loc) const
-      -> std::optional<NamedEntityView>;
+      -> std::optional<ValueNodeView>;
 
   // Maps locally declared names to their entities.
-  std::unordered_map<std::string, NamedEntityView> declared_names_;
+  std::unordered_map<std::string, ValueNodeView> declared_names_;
 
   // A list of scopes used for name lookup within this scope.
   std::vector<Nonnull<StaticScope*>> parent_scopes_;

+ 10 - 2
executable_semantics/interpreter/BUILD

@@ -85,6 +85,8 @@ cc_library(
     hdrs = ["field_path.h"],
     deps = [
         "//common:ostream",
+        "//executable_semantics/ast",
+        "//executable_semantics/ast:static_scope",
         "@llvm-project//llvm:Support",
     ],
 )
@@ -183,8 +185,14 @@ cc_library(
 
 cc_library(
     name = "type_checker",
-    srcs = ["type_checker.cpp"],
-    hdrs = ["type_checker.h"],
+    srcs = [
+        "impl_scope.cpp",
+        "type_checker.cpp",
+    ],
+    hdrs = [
+        "impl_scope.h",
+        "type_checker.h",
+    ],
     deps = [
         ":action_and_value",
         ":dictionary",

+ 9 - 9
executable_semantics/interpreter/action.cpp

@@ -44,20 +44,20 @@ RuntimeScope::~RuntimeScope() {
 void RuntimeScope::Print(llvm::raw_ostream& out) const {
   out << "{";
   llvm::ListSeparator sep;
-  for (const auto& [named_entity, value] : locals_) {
-    out << sep << named_entity.name() << ": " << *value;
+  for (const auto& [value_node, value] : locals_) {
+    out << sep << value_node.base() << ": " << *value;
   }
   out << "}";
 }
 
-void RuntimeScope::Initialize(NamedEntityView named_entity,
+void RuntimeScope::Initialize(ValueNodeView value_node,
                               Nonnull<const Value*> value) {
-  CHECK(!named_entity.constant_value().has_value());
+  CHECK(!value_node.constant_value().has_value());
   CHECK(value->kind() != Value::Kind::LValue);
   allocations_.push_back(heap_->AllocateValue(value));
   auto [it, success] = locals_.insert(
-      {named_entity, heap_->arena().New<LValue>(Address(allocations_.back()))});
-  CHECK(success) << "Duplicate definition of " << named_entity.name();
+      {value_node, heap_->arena().New<LValue>(Address(allocations_.back()))});
+  CHECK(success) << "Duplicate definition of " << value_node.base();
 }
 
 void RuntimeScope::Merge(RuntimeScope other) {
@@ -65,15 +65,15 @@ void RuntimeScope::Merge(RuntimeScope other) {
   locals_.merge(other.locals_);
   CHECK(other.locals_.empty())
       << "Duplicate definition of " << other.locals_.size()
-      << " names, including " << other.locals_.begin()->first.name();
+      << " names, including " << other.locals_.begin()->first.base();
   allocations_.insert(allocations_.end(), other.allocations_.begin(),
                       other.allocations_.end());
   other.allocations_.clear();
 }
 
-auto RuntimeScope::Get(NamedEntityView named_entity) const
+auto RuntimeScope::Get(ValueNodeView value_node) const
     -> std::optional<Nonnull<const LValue*>> {
-  auto it = locals_.find(named_entity);
+  auto it = locals_.find(value_node);
   if (it != locals_.end()) {
     return it->second;
   } else {

+ 5 - 5
executable_semantics/interpreter/action.h

@@ -45,21 +45,21 @@ class RuntimeScope {
   void Print(llvm::raw_ostream& out) const;
   LLVM_DUMP_METHOD void Dump() const { Print(llvm::errs()); }
 
-  // Allocates storage for `named_entity` in `heap`, and initializes it with
+  // Allocates storage for `value_node` in `heap`, and initializes it with
   // `value`.
-  void Initialize(NamedEntityView named_entity, Nonnull<const Value*> value);
+  void Initialize(ValueNodeView value_node, Nonnull<const Value*> value);
 
   // Transfers the names and allocations from `other` into *this. The two
   // scopes must not define the same name, and must be backed by the same Heap.
   void Merge(RuntimeScope other);
 
-  // Returns the local storage for named_entity, if it has storage local to
+  // Returns the local storage for value_node, if it has storage local to
   // this scope.
-  auto Get(NamedEntityView named_entity) const
+  auto Get(ValueNodeView value_node) const
       -> std::optional<Nonnull<const LValue*>>;
 
  private:
-  std::map<NamedEntityView, Nonnull<const LValue*>> locals_;
+  std::map<ValueNodeView, Nonnull<const LValue*>> locals_;
   std::vector<AllocationId> allocations_;
   Nonnull<HeapAllocationInterface*> heap_;
 };

+ 10 - 10
executable_semantics/interpreter/action_stack.cpp

@@ -36,47 +36,47 @@ void ActionStack::Start(std::unique_ptr<Action> action) {
   todo_.Push(std::move(action));
 }
 
-void ActionStack::Initialize(NamedEntityView named_entity,
+void ActionStack::Initialize(ValueNodeView value_node,
                              Nonnull<const Value*> value) {
   for (const std::unique_ptr<Action>& action : todo_) {
     if (action->scope().has_value()) {
-      action->scope()->Initialize(named_entity, value);
+      action->scope()->Initialize(value_node, value);
       return;
     }
   }
-  globals_->Initialize(named_entity, value);
+  globals_->Initialize(value_node, value);
 }
 
-auto ActionStack::ValueOfName(NamedEntityView named_entity,
+auto ActionStack::ValueOfNode(ValueNodeView value_node,
                               SourceLocation source_loc) const
     -> Nonnull<const Value*> {
   if (std::optional<Nonnull<const Value*>> constant_value =
-          named_entity.constant_value();
+          value_node.constant_value();
       constant_value.has_value()) {
     return *constant_value;
   }
   for (const std::unique_ptr<Action>& action : todo_) {
-    // TODO: have static name resolution identify the scope of named_entity
+    // TODO: have static name resolution identify the scope of value_node
     // as an AstNode, and then perform lookup _only_ on the Action associated
     // with that node. This will help keep unwanted dynamic-scoping behavior
     // from sneaking in.
     if (action->scope().has_value()) {
       std::optional<Nonnull<const Value*>> result =
-          action->scope()->Get(named_entity);
+          action->scope()->Get(value_node);
       if (result.has_value()) {
         return *result;
       }
     }
   }
   if (globals_.has_value()) {
-    std::optional<Nonnull<const Value*>> result = globals_->Get(named_entity);
+    std::optional<Nonnull<const Value*>> result = globals_->Get(value_node);
     if (result.has_value()) {
       return *result;
     }
   }
-  // TODO: Move these errors to compile time and explain them more clearly.
+  // TODO: Move these errors to name resolution and explain them more clearly.
   FATAL_RUNTIME_ERROR(source_loc)
-      << "could not find `" << named_entity.name() << "`";
+      << "could not find `" << value_node.base() << "`";
 }
 
 void ActionStack::MergeScope(RuntimeScope scope) {

+ 5 - 5
executable_semantics/interpreter/action_stack.h

@@ -43,13 +43,13 @@ class ActionStack {
   // ScopeAction.
   auto CurrentAction() -> Action& { return *todo_.Top(); }
 
-  // Allocates storage for `named_entity`, and initializes it to `value`.
-  void Initialize(NamedEntityView named_entity, Nonnull<const Value*> value);
+  // Allocates storage for `value_node`, and initializes it to `value`.
+  void Initialize(ValueNodeView value_node, Nonnull<const Value*> value);
 
-  // Returns the value bound to `named_entity`. If `named_entity` is a local
+  // Returns the value bound to `value_node`. If `value_node` is a local
   // variable, this will be an LValue.
-  auto ValueOfName(NamedEntityView named_entity,
-                   SourceLocation source_loc) const -> Nonnull<const Value*>;
+  auto ValueOfNode(ValueNodeView value_node, SourceLocation source_loc) const
+      -> Nonnull<const Value*>;
 
   // Merges `scope` into the innermost scope currently on the stack.
   void MergeScope(RuntimeScope scope);

+ 32 - 4
executable_semantics/interpreter/field_path.h

@@ -9,10 +9,13 @@
 #include <vector>
 
 #include "common/ostream.h"
+#include "executable_semantics/ast/static_scope.h"
 #include "llvm/Support/Compiler.h"
 
 namespace Carbon {
 
+class Witness;
+
 // Given some initial Value, a FieldPath identifies a sub-Value within it,
 // in much the same way that a file path identifies a file within some
 // directory. FieldPaths are relative rather than absolute: the initial
@@ -29,8 +32,33 @@ class FieldPath {
   // Constructs an empty FieldPath.
   FieldPath() = default;
 
+  // A single component of the FieldPath, which is typically the name
+  // of a field. However, inside a generic, when there is a field
+  // access on something of a generic type, e.g., `T`, then we also
+  // need `witness`, a pointer to the witness table containing that field.
+  class Component {
+   public:
+    explicit Component(std::string name) : name_(std::move(name)) {}
+    Component(std::string name, std::optional<Nonnull<const Witness*>> witness)
+        : name_(std::move(name)), witness_(witness) {}
+
+    auto name() const -> const std::string& { return name_; }
+
+    auto witness() const -> std::optional<Nonnull<const Witness*>> {
+      return witness_;
+    }
+
+    void Print(llvm::raw_ostream& out) const { out << name_; }
+
+   private:
+    std::string name_;
+    std::optional<Nonnull<const Witness*>> witness_;
+  };
+
   // Constructs a FieldPath consisting of a single step.
-  explicit FieldPath(std::string name) : components_({std::move(name)}) {}
+  explicit FieldPath(std::string name)
+      : components_({Component(std::move(name))}) {}
+  explicit FieldPath(const Component& f) : components_({f}) {}
 
   FieldPath(const FieldPath&) = default;
   FieldPath(FieldPath&&) = default;
@@ -42,11 +70,11 @@ class FieldPath {
 
   // Appends `name` to the end of *this.
   auto Append(std::string name) -> void {
-    components_.push_back(std::move(name));
+    components_.push_back(Component(std::move(name)));
   }
 
   void Print(llvm::raw_ostream& out) const {
-    for (const std::string& component : components_) {
+    for (const Component& component : components_) {
       out << "." << component;
     }
   }
@@ -58,7 +86,7 @@ class FieldPath {
   // another Value, so its implementation details are tied to the implementation
   // details of Value.
   friend class Value;
-  std::vector<std::string> components_;
+  std::vector<Component> components_;
 };
 
 }  // namespace Carbon

+ 3 - 2
executable_semantics/interpreter/heap.cpp

@@ -20,7 +20,7 @@ auto Heap::AllocateValue(Nonnull<const Value*> v) -> AllocationId {
   return a;
 }
 
-auto Heap::Read(const Address& a, SourceLocation source_loc)
+auto Heap::Read(const Address& a, SourceLocation source_loc) const
     -> Nonnull<const Value*> {
   this->CheckAlive(a.allocation_, source_loc);
   return values_[a.allocation_.index_]->GetField(arena_, a.field_path_,
@@ -34,7 +34,8 @@ void Heap::Write(const Address& a, Nonnull<const Value*> v,
       arena_, a.field_path_, v, source_loc);
 }
 
-void Heap::CheckAlive(AllocationId allocation, SourceLocation source_loc) {
+void Heap::CheckAlive(AllocationId allocation,
+                      SourceLocation source_loc) const {
   if (!alive_[allocation.index_]) {
     FATAL_RUNTIME_ERROR(source_loc)
         << "undefined behavior: access to dead value "

+ 2 - 2
executable_semantics/interpreter/heap.h

@@ -27,7 +27,7 @@ class Heap : public HeapAllocationInterface {
 
   // Returns the value at the given address in the heap after
   // checking that it is alive.
-  auto Read(const Address& a, SourceLocation source_loc)
+  auto Read(const Address& a, SourceLocation source_loc) const
       -> Nonnull<const Value*>;
 
   // Writes the given value at the address in the heap after
@@ -50,7 +50,7 @@ class Heap : public HeapAllocationInterface {
 
  private:
   // Signal an error if the allocation is no longer alive.
-  void CheckAlive(AllocationId allocation, SourceLocation source_loc);
+  void CheckAlive(AllocationId allocation, SourceLocation source_loc) const;
 
   Nonnull<Arena*> arena_;
   std::vector<Nonnull<const Value*>> values_;

+ 78 - 0
executable_semantics/interpreter/impl_scope.cpp

@@ -0,0 +1,78 @@
+// 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/interpreter/impl_scope.h"
+
+#include "executable_semantics/common/error.h"
+#include "executable_semantics/interpreter/value.h"
+#include "llvm/Support/Casting.h"
+
+using llvm::cast;
+
+namespace Carbon {
+
+void ImplScope::Add(Nonnull<const Value*> iface, Nonnull<const Value*> type,
+                    ValueNodeView impl) {
+  impls_.push_back({.interface = iface, .type = type, .impl = impl});
+}
+
+void ImplScope::AddParent(Nonnull<const ImplScope*> parent) {
+  parent_scopes_.push_back(parent);
+}
+
+auto ImplScope::Resolve(Nonnull<const Value*> iface_type,
+                        Nonnull<const Value*> type,
+                        SourceLocation source_loc) const -> ValueNodeView {
+  std::optional<ValueNodeView> result =
+      TryResolve(iface_type, type, source_loc);
+  if (!result.has_value()) {
+    FATAL_COMPILATION_ERROR(source_loc) << "could not find implementation of "
+                                        << *iface_type << " for " << *type;
+  }
+  return *result;
+}
+
+auto ImplScope::TryResolve(Nonnull<const Value*> iface_type,
+                           Nonnull<const Value*> type,
+                           SourceLocation source_loc) const
+    -> std::optional<ValueNodeView> {
+  std::optional<ValueNodeView> result =
+      ResolveHere(iface_type, type, source_loc);
+  if (result.has_value()) {
+    return result;
+  }
+  for (Nonnull<const ImplScope*> parent : parent_scopes_) {
+    auto parent_result = parent->TryResolve(iface_type, type, source_loc);
+    if (parent_result.has_value() && result.has_value() &&
+        *parent_result != *result) {
+      FATAL_COMPILATION_ERROR(source_loc)
+          << "ambiguous implementations of " << *iface_type << " for " << *type;
+    }
+    result = parent_result;
+  }
+  return result;
+}
+
+auto ImplScope::ResolveHere(Nonnull<const Value*> iface_type,
+                            Nonnull<const Value*> impl_type,
+                            SourceLocation source_loc) const
+    -> std::optional<ValueNodeView> {
+  switch (iface_type->kind()) {
+    case Value::Kind::InterfaceType: {
+      const auto& iface = cast<InterfaceType>(*iface_type);
+      for (const Impl& impl : impls_) {
+        if (TypeEqual(&iface, impl.interface) &&
+            TypeEqual(impl_type, impl.type)) {
+          return impl.impl;
+        }
+      }
+      return std::nullopt;
+    }
+    default:
+      FATAL() << "expected an interface, not " << *iface_type;
+      break;
+  }
+}
+
+}  // namespace Carbon

+ 83 - 0
executable_semantics/interpreter/impl_scope.h

@@ -0,0 +1,83 @@
+// 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_IMPL_SCOPE_H_
+#define EXECUTABLE_SEMANTICS_AST_IMPL_SCOPE_H_
+
+#include "executable_semantics/ast/declaration.h"
+
+namespace Carbon {
+
+class Value;
+
+// The `ImplScope` class is responsible for mapping a type and
+// interface to the location of the witness table for the `impl` for
+// that type and interface.  A scope may have parent scopes, whose
+// impls will also be visible in the child scope.
+//
+// There is typically one instance of `ImplScope` class per scope
+// because the impls that are visible for a given type and interface
+// can vary from scope to scope. For example, consider the `bar` and
+// `baz` methods in the following class C and nested class D.
+//
+//     class C(U:! Type, T:! Type)  {
+//       class D(V:! Type where U is Fooable(T)) {
+//         fn bar[me: Self](x: U, y : T) -> T{
+//           return x.foo(y)
+//         }
+//       }
+//       fn baz[me: Self](x: U, y : T) -> T {
+//         return x.foo(y);
+//       }
+//     }
+//
+//  The call to `x.foo` in `bar` is valid because the `U is Fooable(T)`
+//  impl is visible in the body of `bar`. In contrast, the call to
+//  `x.foo` in `baz` is not valid because there is no visible impl for
+//  `U` and `Fooable` in that scope.
+class ImplScope {
+ public:
+  // Associates `iface` and `type` with the `impl` in this scope.
+  void Add(Nonnull<const Value*> iface, Nonnull<const Value*> type,
+           ValueNodeView impl);
+
+  // Make `parent` a parent of this scope.
+  // REQUIRES: `parent` is not already a parent of this scope.
+  void AddParent(Nonnull<const ImplScope*> parent);
+
+  // Returns the associated impl for the given `iface` and `type` in
+  // the ancestor graph of this scope, or reports a compilation error
+  // at `source_loc` there isn't exactly one matching impl.
+  auto Resolve(Nonnull<const Value*> iface, Nonnull<const Value*> type,
+               SourceLocation source_loc) const -> ValueNodeView;
+
+ private:
+  auto TryResolve(Nonnull<const Value*> iface_type, Nonnull<const Value*> type,
+                  SourceLocation source_loc) const
+      -> std::optional<ValueNodeView>;
+  auto ResolveHere(Nonnull<const Value*> iface_type,
+                   Nonnull<const Value*> impl_type,
+                   SourceLocation source_loc) const
+      -> std::optional<ValueNodeView>;
+
+  // The `Impl` struct is a key-value pair where the key is the
+  // combination of a type and an interface, e.g., `List` and `Container`,
+  // and the value is the result of statically resolving to the `impl`
+  // for `List` as `Container`, which is an `ValueNodeView`. The generality
+  // of `ValueNodeView` is needed (not just `ImplDeclaration`) because
+  // inside a generic, we need to map, e.g., from `T` and `Container` to the
+  // witness table that is passed into the generic.
+  struct Impl {
+    Nonnull<const Value*> interface;
+    Nonnull<const Value*> type;
+    ValueNodeView impl;
+  };
+
+  std::vector<Impl> impls_;
+  std::vector<Nonnull<const ImplScope*>> parent_scopes_;
+};
+
+}  // namespace Carbon
+
+#endif  // EXECUTABLE_SEMANTICS_AST_IMPL_SCOPE_H_

+ 35 - 8
executable_semantics/interpreter/interpreter.cpp

@@ -17,6 +17,7 @@
 #include "executable_semantics/common/arena.h"
 #include "executable_semantics/common/error.h"
 #include "executable_semantics/interpreter/action.h"
+#include "executable_semantics/interpreter/action_stack.h"
 #include "executable_semantics/interpreter/stack.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/Support/Casting.h"
@@ -183,8 +184,8 @@ auto PatternMatch(Nonnull<const Value*> p, Nonnull<const Value*> v,
             << "Name bindings are not supported in this context";
       }
       const auto& placeholder = cast<BindingPlaceholderValue>(*p);
-      if (placeholder.named_entity().has_value()) {
-        (*bindings)->Initialize(*placeholder.named_entity(), v);
+      if (placeholder.value_node().has_value()) {
+        (*bindings)->Initialize(*placeholder.value_node(), v);
       }
       return true;
     }
@@ -275,8 +276,8 @@ void Interpreter::StepLvalue() {
     case ExpressionKind::IdentifierExpression: {
       //    { {x :: C, E, F} :: S, H}
       // -> { {E(x) :: C, E, F} :: S, H}
-      Nonnull<const Value*> value = todo_.ValueOfName(
-          cast<IdentifierExpression>(exp).named_entity(), exp.source_loc());
+      Nonnull<const Value*> value = todo_.ValueOfNode(
+          cast<IdentifierExpression>(exp).value_node(), exp.source_loc());
       CHECK(isa<LValue>(value)) << *value;
       return todo_.FinishAction(value);
     }
@@ -371,6 +372,8 @@ auto Interpreter::Convert(Nonnull<const Value*> value,
     case Value::Kind::AutoType:
     case Value::Kind::StructType:
     case Value::Kind::NominalClassType:
+    case Value::Kind::InterfaceType:
+    case Value::Kind::Witness:
     case Value::Kind::ChoiceType:
     case Value::Kind::ContinuationType:
     case Value::Kind::VariableType:
@@ -380,6 +383,7 @@ auto Interpreter::Convert(Nonnull<const Value*> value,
     case Value::Kind::StringType:
     case Value::Kind::StringValue:
     case Value::Kind::TypeOfClassType:
+    case Value::Kind::TypeOfInterfaceType:
     case Value::Kind::TypeOfChoiceType:
       // TODO: add `CHECK(TypeEqual(type, value->dynamic_type()))`, once we
       // have Value::dynamic_type.
@@ -497,8 +501,18 @@ void Interpreter::StepExp() {
       } else {
         //    { { v :: [].f :: C, E, F} :: S, H}
         // -> { { v_f :: C, E, F} : S, H}
-        return todo_.FinishAction(act.results()[0]->GetField(
-            arena_, FieldPath(access.field()), exp.source_loc()));
+        std::optional<Nonnull<const Witness*>> witness = std::nullopt;
+        if (access.impl().has_value()) {
+          auto witness_addr =
+              todo_.ValueOfNode(*access.impl(), access.source_loc());
+          witness = cast<Witness>(
+              heap_.Read(llvm::cast<LValue>(witness_addr)->address(),
+                         access.source_loc()));
+        }
+        FieldPath::Component field(access.field(), witness);
+        Nonnull<const Value*> member = act.results()[0]->GetField(
+            arena_, FieldPath(field), exp.source_loc());
+        return todo_.FinishAction(member);
       }
     }
     case ExpressionKind::IdentifierExpression: {
@@ -506,7 +520,7 @@ void Interpreter::StepExp() {
       const auto& ident = cast<IdentifierExpression>(exp);
       // { {x :: C, E, F} :: S, H} -> { {H(E(x)) :: C, E, F} :: S, H}
       Nonnull<const Value*> value =
-          todo_.ValueOfName(ident.named_entity(), ident.source_loc());
+          todo_.ValueOfNode(ident.value_node(), ident.source_loc());
       if (const auto* lvalue = dyn_cast<LValue>(value)) {
         value = heap_.Read(lvalue->address(), exp.source_loc());
       }
@@ -567,6 +581,17 @@ void Interpreter::StepExp() {
             Nonnull<const Value*> converted_args = Convert(
                 act.results()[1], &function.param_pattern().static_type());
             RuntimeScope function_scope(&heap_);
+            // Bring the impl witness tables into scope.
+            for (const auto& [impl_bind, impl_node] :
+                 cast<CallExpression>(exp).impls()) {
+              Nonnull<const Value*> witness =
+                  todo_.ValueOfNode(impl_node, exp.source_loc());
+              if (witness->kind() == Value::Kind::LValue) {
+                const LValue& lval = cast<LValue>(*witness);
+                witness = heap_.Read(lval.address(), exp.source_loc());
+              }
+              function_scope.Initialize(impl_bind, witness);
+            }
             CHECK(PatternMatch(&function.param_pattern().value(),
                                converted_args, exp.source_loc(),
                                &function_scope));
@@ -649,7 +674,7 @@ void Interpreter::StepExp() {
         // -> { fn pt -> rt :: {C, E, F} :: S, H}
         return todo_.FinishAction(arena_->New<FunctionType>(
             std::vector<Nonnull<const GenericBinding*>>(), act.results()[0],
-            act.results()[1]));
+            act.results()[1], std::vector<Nonnull<const ImplBinding*>>()));
       }
     }
     case ExpressionKind::ContinuationTypeLiteral: {
@@ -963,6 +988,8 @@ void Interpreter::StepDeclaration() {
     case DeclarationKind::FunctionDeclaration:
     case DeclarationKind::ClassDeclaration:
     case DeclarationKind::ChoiceDeclaration:
+    case DeclarationKind::InterfaceDeclaration:
+    case DeclarationKind::ImplDeclaration:
       // These declarations have no run-time effects.
       return todo_.FinishAction();
   }

+ 0 - 1
executable_semantics/interpreter/interpreter.h

@@ -15,7 +15,6 @@
 #include "executable_semantics/ast/expression.h"
 #include "executable_semantics/ast/pattern.h"
 #include "executable_semantics/interpreter/action.h"
-#include "executable_semantics/interpreter/action_stack.h"
 #include "executable_semantics/interpreter/heap.h"
 #include "executable_semantics/interpreter/value.h"
 #include "llvm/ADT/ArrayRef.h"

+ 16 - 1
executable_semantics/interpreter/resolve_control_flow.cpp

@@ -128,7 +128,22 @@ void ResolveControlFlow(Nonnull<Declaration*> declaration) {
       }
       break;
     }
-    default:
+    case DeclarationKind::InterfaceDeclaration: {
+      auto& iface_decl = cast<InterfaceDeclaration>(*declaration);
+      for (Nonnull<Declaration*> member : iface_decl.members()) {
+        ResolveControlFlow(member);
+      }
+      break;
+    }
+    case DeclarationKind::ImplDeclaration: {
+      auto& impl_decl = cast<ImplDeclaration>(*declaration);
+      for (Nonnull<Declaration*> member : impl_decl.members()) {
+        ResolveControlFlow(member);
+      }
+      break;
+    }
+    case DeclarationKind::ChoiceDeclaration:
+    case DeclarationKind::VariableDeclaration:
       // do nothing
       break;
   }

+ 35 - 1
executable_semantics/interpreter/resolve_names.cpp

@@ -24,6 +24,15 @@ static void AddExposedNames(const Declaration& declaration,
 static void AddExposedNames(const Declaration& declaration,
                             StaticScope& enclosing_scope) {
   switch (declaration.kind()) {
+    case DeclarationKind::InterfaceDeclaration: {
+      auto& iface_decl = cast<InterfaceDeclaration>(declaration);
+      enclosing_scope.Add(iface_decl.name(), &iface_decl);
+      break;
+    }
+    case DeclarationKind::ImplDeclaration: {
+      // Nothing to do here
+      break;
+    }
     case DeclarationKind::FunctionDeclaration: {
       auto& func = cast<FunctionDeclaration>(declaration);
       enclosing_scope.Add(func.name(), &func);
@@ -115,7 +124,7 @@ static void ResolveNames(Expression& expression,
       break;
     case ExpressionKind::IdentifierExpression: {
       auto& identifier = cast<IdentifierExpression>(expression);
-      identifier.set_named_entity(
+      identifier.set_value_node(
           enclosing_scope.Resolve(identifier.name(), identifier.source_loc()));
       break;
     }
@@ -244,6 +253,31 @@ static void ResolveNames(Statement& statement, StaticScope& enclosing_scope) {
 static void ResolveNames(Declaration& declaration,
                          StaticScope& enclosing_scope) {
   switch (declaration.kind()) {
+    case DeclarationKind::InterfaceDeclaration: {
+      auto& iface = cast<InterfaceDeclaration>(declaration);
+      StaticScope iface_scope;
+      iface_scope.AddParent(&enclosing_scope);
+      iface_scope.Add("Self", iface.self());
+      for (Nonnull<Declaration*> member : iface.members()) {
+        AddExposedNames(*member, iface_scope);
+      }
+      for (Nonnull<Declaration*> member : iface.members()) {
+        ResolveNames(*member, iface_scope);
+      }
+      break;
+    }
+    case DeclarationKind::ImplDeclaration: {
+      auto& impl = cast<ImplDeclaration>(declaration);
+      ResolveNames(impl.interface(), enclosing_scope);
+      ResolveNames(*impl.impl_type(), enclosing_scope);
+      for (Nonnull<Declaration*> member : impl.members()) {
+        AddExposedNames(*member, enclosing_scope);
+      }
+      for (Nonnull<Declaration*> member : impl.members()) {
+        ResolveNames(*member, enclosing_scope);
+      }
+      break;
+    }
     case DeclarationKind::FunctionDeclaration: {
       auto& function = cast<FunctionDeclaration>(declaration);
       StaticScope function_scope;

Разница между файлами не показана из-за своего большого размера
+ 345 - 144
executable_semantics/interpreter/type_checker.cpp


+ 54 - 21
executable_semantics/interpreter/type_checker.h

@@ -13,6 +13,7 @@
 #include "executable_semantics/ast/statement.h"
 #include "executable_semantics/common/nonnull.h"
 #include "executable_semantics/interpreter/dictionary.h"
+#include "executable_semantics/interpreter/impl_scope.h"
 #include "executable_semantics/interpreter/interpreter.h"
 
 namespace Carbon {
@@ -31,17 +32,16 @@ class TypeChecker {
   // inside the argument type.
   // The `deduced` parameter is an accumulator, that is, it holds the
   // results so-far.
-  static void ArgumentDeduction(
-      SourceLocation source_loc,
-      std::map<Nonnull<const GenericBinding*>, Nonnull<const Value*>>& deduced,
-      Nonnull<const Value*> param, Nonnull<const Value*> arg);
+  static void ArgumentDeduction(SourceLocation source_loc, BindingMap& deduced,
+                                Nonnull<const Value*> param,
+                                Nonnull<const Value*> arg);
 
   // Traverses the AST rooted at `e`, populating the static_type() of all nodes
   // and ensuring they follow Carbon's typing rules.
   //
   // `values` maps variable names to their compile-time values. It is not
   //    directly used in this function but is passed to InterExp.
-  void TypeCheckExp(Nonnull<Expression*> e);
+  void TypeCheckExp(Nonnull<Expression*> e, const ImplScope& impl_scope);
 
   // Equivalent to TypeCheckExp, but operates on the AST rooted at `p`.
   //
@@ -49,31 +49,64 @@ class TypeChecker {
   // surrounding context gives us that information. Otherwise, it is
   // nullopt.
   void TypeCheckPattern(Nonnull<Pattern*> p,
-                        std::optional<Nonnull<const Value*>> expected);
-
-  // Equivalent to TypeCheckExp, but operates on the AST rooted at `d`.
-  void TypeCheckDeclaration(Nonnull<Declaration*> d);
+                        std::optional<Nonnull<const Value*>> expected,
+                        const ImplScope& impl_scope);
 
   // Equivalent to TypeCheckExp, but operates on the AST rooted at `s`.
   //
   // REQUIRES: f.return_term().has_static_type() || f.return_term().is_auto(),
   // where `f` is nearest enclosing FunctionDeclaration of `s`.
-  void TypeCheckStmt(Nonnull<Statement*> s);
+  void TypeCheckStmt(Nonnull<Statement*> s, const ImplScope& impl_scope);
+
+  // Establish the `static_type` and `constant_value` of the
+  // declaration and all of its nested declarations. This involves the
+  // compile-time interpretation of any type expressions in the
+  // declaration. It does not involve type checking statements and
+  // (runtime) expressions, as in the body of a function or a method.
+  // Dispatches to one of the following functions.
+  void DeclareDeclaration(Nonnull<Declaration*> d, ImplScope& enclosing_scope);
+
+  void DeclareFunctionDeclaration(Nonnull<FunctionDeclaration*> f,
+                                  const ImplScope& enclosing_scope);
+
+  void DeclareClassDeclaration(Nonnull<ClassDeclaration*> class_decl,
+                               ImplScope& enclosing_scope);
+
+  void DeclareInterfaceDeclaration(Nonnull<InterfaceDeclaration*> iface_decl,
+                                   ImplScope& enclosing_scope);
 
-  // Equivalent to TypeCheckExp, but operates on the AST rooted at `f`,
-  // and may not traverse f->body() if `check_body` is false.
+  void DeclareImplDeclaration(Nonnull<ImplDeclaration*> impl_decl,
+                              ImplScope& enclosing_scope);
+
+  void DeclareChoiceDeclaration(Nonnull<ChoiceDeclaration*> choice,
+                                const ImplScope& enclosing_scope);
+
+  // Checks the statements and (runtime) expressions within the
+  // declaration, such as the body of a function.
+  // Dispatches to one of the following functions.
+  // Assumes that DeclareDeclaration has already been invoked on `d`.
+  void TypeCheckDeclaration(Nonnull<Declaration*> d,
+                            const ImplScope& impl_scope);
+
+  // Type check the body of the function.
   void TypeCheckFunctionDeclaration(Nonnull<FunctionDeclaration*> f,
-                                    bool check_body);
+                                    const ImplScope& impl_scope);
+
+  // Type check all the members of the class.
+  void TypeCheckClassDeclaration(Nonnull<ClassDeclaration*> class_decl,
+                                 const ImplScope& impl_scope);
 
-  // Equivalent to TypeCheckExp, but operates on the AST rooted at class_decl.
-  void TypeCheckClassDeclaration(Nonnull<ClassDeclaration*> class_decl);
+  // Type check all the members of the interface.
+  void TypeCheckInterfaceDeclaration(Nonnull<InterfaceDeclaration*> iface_decl,
+                                     const ImplScope& impl_scope);
 
-  // Equivalent to TypeCheckExp, but operates on the AST rooted at choice_decl.
-  void TypeCheckChoiceDeclaration(Nonnull<ChoiceDeclaration*> choice);
+  // Type check all the members of the implementation.
+  void TypeCheckImplDeclaration(Nonnull<ImplDeclaration*> impl_decl,
+                                const ImplScope& impl_scope);
 
-  // Establish the type of the declaration without deeply checking
-  // the declaration, such as checking the body of a function.
-  void DeclareDeclaration(Nonnull<Declaration*> d);
+  // This currently does nothing, but perhaps that will change in the future.
+  void TypeCheckChoiceDeclaration(Nonnull<ChoiceDeclaration*> choice,
+                                  const ImplScope& impl_scope);
 
   // Verifies that opt_stmt holds a statement, and it is structurally impossible
   // for control flow to leave that statement except via a `return`.
@@ -98,7 +131,7 @@ class TypeChecker {
   void PrintConstants(llvm::raw_ostream& out);
 
   Nonnull<Arena*> arena_;
-  std::set<NamedEntityView> constants_;
+  std::set<ValueNodeView> constants_;
 
   bool trace_;
 };

+ 96 - 37
executable_semantics/interpreter/value.cpp

@@ -28,8 +28,28 @@ auto StructValue::FindField(const std::string& name) const
 }
 
 static auto GetMember(Nonnull<Arena*> arena, Nonnull<const Value*> v,
-                      const std::string& f, SourceLocation source_loc)
-    -> Nonnull<const Value*> {
+                      const FieldPath::Component& field,
+                      SourceLocation source_loc) -> Nonnull<const Value*> {
+  const std::string& f = field.name();
+
+  if (field.witness().has_value()) {
+    Nonnull<const Witness*> witness = *field.witness();
+    switch (witness->kind()) {
+      case Value::Kind::Witness: {
+        if (std::optional<Nonnull<const Declaration*>> mem_decl =
+                FindMember(f, witness->declaration().members());
+            mem_decl.has_value()) {
+          const auto& fun_decl = cast<FunctionDeclaration>(**mem_decl);
+          return arena->New<BoundMethodValue>(&fun_decl, v);
+        } else {
+          FATAL_COMPILATION_ERROR(source_loc)
+              << "member " << f << " not in " << *witness;
+        }
+      }
+      default:
+        FATAL() << "expected Witness, not " << *witness;
+    }
+  }
   switch (v->kind()) {
     case Value::Kind::StructValue: {
       std::optional<Nonnull<const Value*>> field =
@@ -51,8 +71,8 @@ static auto GetMember(Nonnull<Arena*> arena, Nonnull<const Value*> v,
         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;
+          FATAL_RUNTIME_ERROR(source_loc)
+              << "member " << f << " not in " << *v << " or its " << class_type;
         } else if ((*func)->declaration().is_method()) {
           // Found a method. Turn it into a bound method.
           const FunctionValue& m = cast<FunctionValue>(**func);
@@ -90,17 +110,18 @@ static auto GetMember(Nonnull<Arena*> arena, Nonnull<const Value*> v,
 auto Value::GetField(Nonnull<Arena*> arena, const FieldPath& path,
                      SourceLocation source_loc) const -> Nonnull<const Value*> {
   Nonnull<const Value*> value(this);
-  for (const std::string& field : path.components_) {
+  for (const FieldPath::Component& field : path.components_) {
     value = GetMember(arena, value, field, source_loc);
   }
   return value;
 }
 
-static auto SetFieldImpl(Nonnull<Arena*> arena, Nonnull<const Value*> value,
-                         std::vector<std::string>::const_iterator path_begin,
-                         std::vector<std::string>::const_iterator path_end,
-                         Nonnull<const Value*> field_value,
-                         SourceLocation source_loc) -> Nonnull<const Value*> {
+static auto SetFieldImpl(
+    Nonnull<Arena*> arena, Nonnull<const Value*> value,
+    std::vector<FieldPath::Component>::const_iterator path_begin,
+    std::vector<FieldPath::Component>::const_iterator path_end,
+    Nonnull<const Value*> field_value, SourceLocation source_loc)
+    -> Nonnull<const Value*> {
   if (path_begin == path_end) {
     return field_value;
   }
@@ -109,11 +130,11 @@ static auto SetFieldImpl(Nonnull<Arena*> arena, Nonnull<const Value*> value,
       std::vector<NamedValue> elements = cast<StructValue>(*value).elements();
       auto it = std::find_if(elements.begin(), elements.end(),
                              [path_begin](const NamedValue& element) {
-                               return element.name == *path_begin;
+                               return element.name == (*path_begin).name();
                              });
       if (it == elements.end()) {
         FATAL_RUNTIME_ERROR(source_loc)
-            << "field " << *path_begin << " not in " << *value;
+            << "field " << (*path_begin).name() << " not in " << *value;
       }
       it->value = SetFieldImpl(arena, it->value, path_begin + 1, path_end,
                                field_value, source_loc);
@@ -127,10 +148,10 @@ static auto SetFieldImpl(Nonnull<Arena*> arena, Nonnull<const Value*> value,
       std::vector<Nonnull<const Value*>> elements =
           cast<TupleValue>(*value).elements();
       // TODO(geoffromer): update FieldPath to hold integers as well as strings.
-      int index = std::stoi(*path_begin);
+      int index = std::stoi((*path_begin).name());
       if (index < 0 || static_cast<size_t>(index) >= elements.size()) {
-        FATAL_RUNTIME_ERROR(source_loc)
-            << "index " << *path_begin << " out of range in " << *value;
+        FATAL_RUNTIME_ERROR(source_loc) << "index " << (*path_begin).name()
+                                        << " out of range in " << *value;
       }
       elements[index] = SetFieldImpl(arena, elements[index], path_begin + 1,
                                      path_end, field_value, source_loc);
@@ -159,8 +180,8 @@ void Value::Print(llvm::raw_ostream& out) const {
     case Value::Kind::BindingPlaceholderValue: {
       const auto& placeholder = cast<BindingPlaceholderValue>(*this);
       out << "Placeholder<";
-      if (placeholder.named_entity().has_value()) {
-        out << (*placeholder.named_entity()).name();
+      if (placeholder.value_node().has_value()) {
+        out << (*placeholder.value_node());
       } else {
         out << "_";
       }
@@ -266,6 +287,17 @@ void Value::Print(llvm::raw_ostream& out) const {
       out << "class " << class_type.declaration().name();
       break;
     }
+    case Value::Kind::InterfaceType: {
+      const InterfaceType& iface_type = cast<InterfaceType>(*this);
+      out << "interface " << iface_type.declaration().name();
+      break;
+    }
+    case Value::Kind::Witness: {
+      const auto& witness = cast<Witness>(*this);
+      out << "impl " << *witness.declaration().impl_type() << " as "
+          << witness.declaration().interface();
+      break;
+    }
     case Value::Kind::ChoiceType:
       out << "choice " << cast<ChoiceType>(*this).name();
       break;
@@ -289,6 +321,14 @@ void Value::Print(llvm::raw_ostream& out) const {
           << cast<TypeOfClassType>(*this).class_type().declaration().name()
           << ")";
       break;
+    case Value::Kind::TypeOfInterfaceType:
+      out << "typeof("
+          << cast<TypeOfInterfaceType>(*this)
+                 .interface_type()
+                 .declaration()
+                 .name()
+          << ")";
+      break;
     case Value::Kind::TypeOfChoiceType:
       out << "typeof(" << cast<TypeOfChoiceType>(*this).choice_type().name()
           << ")";
@@ -364,6 +404,9 @@ auto TypeEqual(Nonnull<const Value*> t1, Nonnull<const Value*> t2) -> bool {
     case Value::Kind::NominalClassType:
       return cast<NominalClassType>(*t1).declaration().name() ==
              cast<NominalClassType>(*t2).declaration().name();
+    case Value::Kind::InterfaceType:
+      return cast<InterfaceType>(*t1).declaration().name() ==
+             cast<InterfaceType>(*t2).declaration().name();
     case Value::Kind::ChoiceType:
       return cast<ChoiceType>(*t1).name() == cast<ChoiceType>(*t2).name();
     case Value::Kind::TupleValue: {
@@ -391,13 +434,34 @@ auto TypeEqual(Nonnull<const Value*> t1, Nonnull<const Value*> t2) -> bool {
     case Value::Kind::TypeOfClassType:
       return TypeEqual(&cast<TypeOfClassType>(*t1).class_type(),
                        &cast<TypeOfClassType>(*t2).class_type());
+    case Value::Kind::TypeOfInterfaceType:
+      return TypeEqual(&cast<TypeOfInterfaceType>(*t1).interface_type(),
+                       &cast<TypeOfInterfaceType>(*t2).interface_type());
     case Value::Kind::TypeOfChoiceType:
       return TypeEqual(&cast<TypeOfChoiceType>(*t1).choice_type(),
                        &cast<TypeOfChoiceType>(*t2).choice_type());
-    default:
+    case Value::Kind::IntValue:
+    case Value::Kind::BoolValue:
+    case Value::Kind::FunctionValue:
+    case Value::Kind::BoundMethodValue:
+    case Value::Kind::StructValue:
+    case Value::Kind::NominalClassValue:
+    case Value::Kind::AlternativeValue:
+    case Value::Kind::AlternativeConstructorValue:
+    case Value::Kind::StringValue:
+    case Value::Kind::PointerValue:
+    case Value::Kind::LValue:
+    case Value::Kind::BindingPlaceholderValue:
+    case Value::Kind::ContinuationValue:
       FATAL() << "TypeEqual used to compare non-type values\n"
               << *t1 << "\n"
               << *t2;
+    case Value::Kind::Witness:
+      FATAL() << "TypeEqual: unexpected Witness";
+      break;
+    case Value::Kind::AutoType:
+      FATAL() << "TypeEqual: unexpected AutoType";
+      break;
   }
 }
 
@@ -468,11 +532,14 @@ auto ValueEqual(Nonnull<const Value*> v1, Nonnull<const Value*> v2) -> bool {
     case Value::Kind::AutoType:
     case Value::Kind::StructType:
     case Value::Kind::NominalClassType:
+    case Value::Kind::InterfaceType:
+    case Value::Kind::Witness:
     case Value::Kind::ChoiceType:
     case Value::Kind::ContinuationType:
     case Value::Kind::VariableType:
     case Value::Kind::StringType:
     case Value::Kind::TypeOfClassType:
+    case Value::Kind::TypeOfInterfaceType:
     case Value::Kind::TypeOfChoiceType:
       return TypeEqual(v1, v2);
     case Value::Kind::NominalClassValue:
@@ -533,29 +600,21 @@ auto FieldTypes(const NominalClassType& class_type) -> std::vector<NamedValue> {
   return field_types;
 }
 
-auto NominalClassType::FindMember(const std::string& name) const
+auto FindMember(const std::string& name,
+                llvm::ArrayRef<Nonnull<Declaration*>> members)
     -> 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;
+  for (Nonnull<const Declaration*> member : members) {
+    if (std::optional<std::string> mem_name = GetName(*member);
+        mem_name.has_value()) {
+      if (*mem_name == name)
+        return member;
     }
   }
   return std::nullopt;
 }
 
+void ImplBinding::Print(llvm::raw_ostream& out) const {
+  out << "impl " << *type_var_ << " as " << *iface_;
+}
+
 }  // namespace Carbon

+ 73 - 11
executable_semantics/interpreter/value.h

@@ -44,6 +44,7 @@ class Value {
     NominalClassValue,
     AlternativeValue,
     TupleValue,
+    Witness,
     IntType,
     BoolType,
     TypeType,
@@ -52,6 +53,7 @@ class Value {
     AutoType,
     StructType,
     NominalClassType,
+    InterfaceType,
     ChoiceType,
     ContinuationType,  // The type of a continuation.
     VariableType,      // e.g., generic type parameters.
@@ -61,6 +63,7 @@ class Value {
     StringType,
     StringValue,
     TypeOfClassType,
+    TypeOfInterfaceType,
     TypeOfChoiceType,
   };
 
@@ -331,20 +334,20 @@ class BindingPlaceholderValue : public Value {
   explicit BindingPlaceholderValue() : Value(Kind::BindingPlaceholderValue) {}
 
   // Represents a named placeholder.
-  explicit BindingPlaceholderValue(NamedEntityView named_entity)
+  explicit BindingPlaceholderValue(ValueNodeView value_node)
       : Value(Kind::BindingPlaceholderValue),
-        named_entity_(std::move(named_entity)) {}
+        value_node_(std::move(value_node)) {}
 
   static auto classof(const Value* value) -> bool {
     return value->kind() == Kind::BindingPlaceholderValue;
   }
 
-  auto named_entity() const -> const std::optional<NamedEntityView>& {
-    return named_entity_;
+  auto value_node() const -> const std::optional<ValueNodeView>& {
+    return value_node_;
   }
 
  private:
-  std::optional<NamedEntityView> named_entity_;
+  std::optional<ValueNodeView> value_node_;
 };
 
 // The int type.
@@ -382,11 +385,13 @@ class FunctionType : public Value {
  public:
   FunctionType(llvm::ArrayRef<Nonnull<const GenericBinding*>> deduced,
                Nonnull<const Value*> parameters,
-               Nonnull<const Value*> return_type)
+               Nonnull<const Value*> return_type,
+               llvm::ArrayRef<Nonnull<const ImplBinding*>> impl_bindings)
       : Value(Kind::FunctionType),
         deduced_(deduced),
         parameters_(parameters),
-        return_type_(return_type) {}
+        return_type_(return_type),
+        impl_bindings_(impl_bindings) {}
 
   static auto classof(const Value* value) -> bool {
     return value->kind() == Kind::FunctionType;
@@ -397,11 +402,17 @@ class FunctionType : public Value {
   }
   auto parameters() const -> const Value& { return *parameters_; }
   auto return_type() const -> const Value& { return *return_type_; }
+  // The bindings for the witness tables (impls) required by the
+  // bounds on the type parameters of the generic function.
+  auto impl_bindings() const -> llvm::ArrayRef<Nonnull<const ImplBinding*>> {
+    return impl_bindings_;
+  }
 
  private:
   std::vector<Nonnull<const GenericBinding*>> deduced_;
   Nonnull<const Value*> parameters_;
   Nonnull<const Value*> return_type_;
+  std::vector<Nonnull<const ImplBinding*>> impl_bindings_;
 };
 
 // A pointer type.
@@ -463,10 +474,6 @@ class NominalClassType : public Value {
 
   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
@@ -476,6 +483,46 @@ class NominalClassType : public Value {
   Nonnull<const ClassDeclaration*> declaration_;
 };
 
+auto FieldTypes(const NominalClassType&) -> std::vector<NamedValue>;
+// Return the declaration of the member with the given name.
+auto FindMember(const std::string& name,
+                llvm::ArrayRef<Nonnull<Declaration*>> members)
+    -> std::optional<Nonnull<const Declaration*>>;
+
+// An interface type.
+class InterfaceType : public Value {
+ public:
+  InterfaceType(Nonnull<const InterfaceDeclaration*> declaration)
+      : Value(Kind::InterfaceType), declaration_(declaration) {}
+
+  static auto classof(const Value* value) -> bool {
+    return value->kind() == Kind::InterfaceType;
+  }
+
+  auto declaration() const -> const InterfaceDeclaration& {
+    return *declaration_;
+  }
+
+ private:
+  Nonnull<const InterfaceDeclaration*> declaration_;
+};
+
+// The witness table for an impl.
+class Witness : public Value {
+ public:
+  Witness(Nonnull<const ImplDeclaration*> declaration)
+      : Value(Kind::Witness), declaration_(declaration) {}
+
+  static auto classof(const Value* value) -> bool {
+    return value->kind() == Kind::Witness;
+  }
+
+  auto declaration() const -> const ImplDeclaration& { return *declaration_; }
+
+ private:
+  Nonnull<const ImplDeclaration*> declaration_;
+};
+
 auto FieldTypes(const NominalClassType&) -> std::vector<NamedValue>;
 
 // A choice type.
@@ -627,6 +674,21 @@ class TypeOfClassType : public Value {
   Nonnull<const NominalClassType*> class_type_;
 };
 
+class TypeOfInterfaceType : public Value {
+ public:
+  explicit TypeOfInterfaceType(Nonnull<const InterfaceType*> iface_type)
+      : Value(Kind::TypeOfInterfaceType), iface_type_(iface_type) {}
+
+  static auto classof(const Value* value) -> bool {
+    return value->kind() == Kind::TypeOfInterfaceType;
+  }
+
+  auto interface_type() const -> const InterfaceType& { return *iface_type_; }
+
+ private:
+  Nonnull<const InterfaceType*> iface_type_;
+};
+
 // The type of an expression whose value is a choice type. Currently there is no
 // way to explicitly name such a type in Carbon code, but we are tentatively
 // using `typeof(ChoiceName)` as the debug-printing format, in anticipation of

+ 6 - 0
executable_semantics/syntax/lexer.lpp

@@ -42,6 +42,7 @@ AMPERSAND            "&"
 AND                  "and"
 API                  "api"
 ARROW                "->"
+AS                   "as"
 AUTO                 "auto"
 AWAIT                "__await"
 BOOL                 "Bool"
@@ -60,12 +61,14 @@ DOUBLE_ARROW         "=>"
 ELSE                 "else"
 EQUAL                "="
 EQUAL_EQUAL          "=="
+EXTERNAL             "external"
 FALSE                "false"
 FN                   "fn"
 FN_TYPE              "__Fn"
 IF                   "if"
 IMPL                 "impl"
 IMPORT               "import"
+INTERFACE            "interface"
 LEFT_CURLY_BRACE     "{"
 LEFT_PARENTHESIS     "("
 LEFT_SQUARE_BRACKET  "["
@@ -139,6 +142,7 @@ string_literal        \"([^\\\"\n\v\f\r]|\\.)*\"
 {AND}                 { return SIMPLE_TOKEN(AND);                 }
 {API}                 { return SIMPLE_TOKEN(API);                 }
 {ARROW}               { return SIMPLE_TOKEN(ARROW);               }
+{AS}                  { return SIMPLE_TOKEN(AS);                  }
 {AUTO}                { return SIMPLE_TOKEN(AUTO);                }
 {AWAIT}               { return SIMPLE_TOKEN(AWAIT);               }
 {BOOL}                { return SIMPLE_TOKEN(BOOL);                }
@@ -157,12 +161,14 @@ string_literal        \"([^\\\"\n\v\f\r]|\\.)*\"
 {ELSE}                { return SIMPLE_TOKEN(ELSE);                }
 {EQUAL_EQUAL}         { return SIMPLE_TOKEN(EQUAL_EQUAL);         }
 {EQUAL}               { return SIMPLE_TOKEN(EQUAL);               }
+{EXTERNAL}            { return SIMPLE_TOKEN(EXTERNAL);            }
 {FALSE}               { return SIMPLE_TOKEN(FALSE);               }
 {FN_TYPE}             { return SIMPLE_TOKEN(FN_TYPE);             }
 {FN}                  { return SIMPLE_TOKEN(FN);                  }
 {IF}                  { return SIMPLE_TOKEN(IF);                  }
 {IMPL}                { return SIMPLE_TOKEN(IMPL);                }
 {IMPORT}              { return SIMPLE_TOKEN(IMPORT);              }
+{INTERFACE}           { return SIMPLE_TOKEN(INTERFACE);           }
 {LEFT_CURLY_BRACE}    { return SIMPLE_TOKEN(LEFT_CURLY_BRACE);    }
 {LEFT_PARENTHESIS}    { return SIMPLE_TOKEN(LEFT_PARENTHESIS);    }
 {LEFT_SQUARE_BRACKET} { return SIMPLE_TOKEN(LEFT_SQUARE_BRACKET); }

+ 19 - 0
executable_semantics/syntax/parser.ypp

@@ -93,6 +93,7 @@
 %token <std::string> sized_type_literal
 %token <std::string> string_literal
 %type <std::string> designator
+%type <ImplKind> impl_kind
 %type <std::pair<LibraryName, bool>> package_directive
 %type <LibraryName> import_directive
 %type <std::vector<LibraryName>> import_directives
@@ -144,6 +145,7 @@
   AND
   API
   ARROW
+  AS
   AUTO
   AWAIT
   BOOL
@@ -162,12 +164,14 @@
   ELSE
   EQUAL
   EQUAL_EQUAL
+  EXTERNAL
   FALSE
   FN
   FN_TYPE
   IF
   IMPL
   IMPORT
+  INTERFACE
   LEFT_CURLY_BRACE
   LEFT_PARENTHESIS
   LEFT_SQUARE_BRACKET
@@ -742,6 +746,21 @@ declaration:
     }
 | VAR variable_declaration EQUAL expression SEMICOLON
     { $$ = arena->New<VariableDeclaration>(context.source_loc(), $2, $4); }
+| INTERFACE identifier LEFT_CURLY_BRACE declaration_list RIGHT_CURLY_BRACE
+    {
+      auto ty_ty = arena -> New<TypeTypeLiteral>(context.source_loc());
+      auto self =
+          arena -> New<GenericBinding>(context.source_loc(), "Self", ty_ty);
+      $$ = arena->New<InterfaceDeclaration>(context.source_loc(), $2, self, $4);
+    }
+| impl_kind IMPL expression AS expression LEFT_CURLY_BRACE declaration_list RIGHT_CURLY_BRACE
+    { $$ = arena->New<ImplDeclaration>(context.source_loc(), $1, $3, $5, $7); }
+;
+impl_kind:
+  // Internal
+    { $$ = Carbon::ImplKind::InternalImpl; }
+| EXTERNAL
+    { $$ = Carbon::ImplKind::ExternalImpl; }
 ;
 declaration_list:
   // Empty

+ 0 - 1
executable_semantics/testdata/basic_syntax/trace.carbon

@@ -13,7 +13,6 @@
 // CHECK: fn Print (format_str: String) {
 // CHECK: ********** type checking **********
 // CHECK: checking pattern (format_str: String)
-// CHECK: constants: {{.*Main: fun<Main>.*}}
 // CHECK: ********** type checking complete **********
 // CHECK: fn Print (format_str: String) {
 // CHECK: ********** starting execution **********

+ 1 - 1
executable_semantics/testdata/global_variable/fail_init_order.carbon

@@ -7,7 +7,7 @@
 // RUN: %{not} %{executable_semantics} --trace %s 2>&1 | \
 // RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes %s
 // AUTOUPDATE: %{executable_semantics} %s
-// CHECK: RUNTIME ERROR: {{.*}}/executable_semantics/testdata/global_variable/fail_init_order.carbon:17: could not find `y`
+// CHECK: RUNTIME ERROR: {{.*}}/executable_semantics/testdata/global_variable/fail_init_order.carbon:17: could not find `y: i32`
 
 package ExecutableSemanticsTest api;
 

+ 44 - 0
executable_semantics/testdata/interface/external_impl_point_vector.carbon

@@ -0,0 +1,44 @@
+// 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;
+
+interface Vector {
+  fn Add[me: Self](b: Self) -> Self;
+  fn Scale[me: Self](v: i32) -> Self;
+}
+
+class Point {
+  var x: i32;
+  var y: i32;
+}
+
+external impl Point as Vector {
+  fn Add[me: Point](b: Point) -> Point {
+      return {.x = me.x + b.x, .y = me.y + b.y};
+  }
+  fn Scale[me: Point](v: i32) -> Point {
+      return {.x = me.x * v, .y = me.y * v};
+  }
+}
+
+fn AddAndScaleGeneric[T:! Vector](a: T, b: T, s: i32) -> T {
+  var m: auto = a.Add;
+  var n: auto = m(b).Scale;
+  return n(s);
+}
+
+fn Main() -> i32 {
+  var a: Point = {.x = 1, .y = 4};
+  var b: Point = {.x = 2, .y = 3};
+  var p: Point = AddAndScaleGeneric(a, b, 5);
+  return p.x - 15;
+}

+ 43 - 0
executable_semantics/testdata/interface/fail_impl_bad_member.carbon

@@ -0,0 +1,43 @@
+// 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/interface/fail_impl_bad_member.carbon:28: type error in member of implementation: 'fn (i32) -> i32' is not implicitly convertible to 'fn (i32) -> class Point'
+
+package ExecutableSemanticsTest api;
+
+interface Vector {
+  fn Add[me: Self](b: Self) -> Self;
+  fn Scale[me: Self](v: i32) -> Self;
+}
+
+class Point {
+  var x: i32;
+  var y: i32;
+  impl Point as Vector {
+    fn Add[me: Point](b: Point) -> Point {
+        return {.x = me.x + b.x, .y = me.y + b.y};
+    }
+    fn Scale[me: Point](v: i32) -> i32 {
+        return 0;
+    }
+  }
+}
+
+fn AddAndScaleGeneric[T:! Vector](a: T, b: T, s: i32) -> T {
+  var m: auto = a.Add;
+  var n: auto = m(b).Scale;
+  return n(s);
+}
+
+fn Main() -> i32 {
+  var a: Point = {.x = 0, .y = 0};
+  var b: Point = {.x = 2, .y = 3};
+  var p: Point = AddAndScaleGeneric(a, b, 3);
+  return p.x - 6;
+}

+ 40 - 0
executable_semantics/testdata/interface/fail_impl_missing_member.carbon

@@ -0,0 +1,40 @@
+// 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/interface/fail_impl_missing_member.carbon:26: implementation missing Scale
+
+package ExecutableSemanticsTest api;
+
+interface Vector {
+  fn Add[me: Self](b: Self) -> Self;
+  fn Scale[me: Self](v: i32) -> Self;
+}
+
+class Point {
+  var x: i32;
+  var y: i32;
+  impl Point as Vector {
+    fn Add[me: Point](b: Point) -> Point {
+        return {.x = me.x + b.x, .y = me.y + b.y};
+    }
+  }
+}
+
+fn AddAndScaleGeneric[T:! Vector](a: T, b: T, s: i32) -> T {
+  var m: auto = a.Add;
+  var n: auto = m(b).Scale;
+  return n(s);
+}
+
+fn Main() -> i32 {
+  var a: Point = {.x = 0, .y = 0};
+  var b: Point = {.x = 2, .y = 3};
+  var p: Point = AddAndScaleGeneric(a, b, 3);
+  return p.x - 6;
+}

+ 33 - 0
executable_semantics/testdata/interface/fail_no_impl.carbon

@@ -0,0 +1,33 @@
+// 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/interface/fail_no_impl.carbon:31: could not find implementation of interface Vector for class Point
+
+package ExecutableSemanticsTest api;
+
+interface Vector {
+  fn Add[me: Self](b: Self) -> Self;
+  fn Scale[me: Self](v: i32) -> Self;
+}
+
+class Point {
+  var x: i32;
+  var y: i32;
+}
+
+fn AddAndScaleGeneric[T:! Vector](a: T, b: T, s: i32) -> T {
+  return a.Add(b).Scale(s);
+}
+
+fn Main() -> i32 {
+  var a: Point = {.x = 0, .y = 0};
+  var b: Point = {.x = 2, .y = 3};
+  var p: Point = AddAndScaleGeneric(a, b, 3);
+  return p.x - 6;
+}

+ 45 - 0
executable_semantics/testdata/interface/generic_call_generic.carbon

@@ -0,0 +1,45 @@
+// 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;
+
+interface Vector {
+  fn Add[me: Self](b: Self) -> Self;
+  fn Scale[me: Self](v: i32) -> Self;
+}
+
+class Point {
+  var x: i32;
+  var y: i32;
+  impl Point as Vector {
+    fn Add[me: Point](b: Point) -> Point {
+        return {.x = me.x + b.x, .y = me.y + b.y};
+    }
+    fn Scale[me: Point](v: i32) -> Point {
+        return {.x = me.x * v, .y = me.y * v};
+    }
+  }
+}
+
+fn ScaleGeneric[U:! Vector](c: U, s: i32) -> U {
+  return c.Scale(s);
+}
+
+fn AddAndScaleGeneric[T:! Vector](a: T, b: T, s: i32) -> T {
+  return ScaleGeneric(a.Add(b), s);
+}
+
+fn Main() -> i32 {
+  var a: Point = {.x = 1, .y = 1};
+  var b: Point = {.x = 2, .y = 3};
+  var p: Point = AddAndScaleGeneric(a, b, 5);
+  return p.x - 15;
+}

+ 59 - 0
executable_semantics/testdata/interface/generic_with_two_params.carbon

@@ -0,0 +1,59 @@
+// 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;
+
+interface Vector {
+  fn Add[me: Self](b: Self) -> Self;
+  fn Scale[me: Self](v: i32) -> Self;
+}
+
+class Point1 {
+  var x: i32;
+  var y: i32;
+  impl Point1 as Vector {
+    fn Add[me: Point1](b: Point1) -> Point1 {
+        return {.x = me.x + b.x, .y = me.y + b.y};
+    }
+    fn Scale[me: Point1](v: i32) -> Point1 {
+        return {.x = me.x * v, .y = me.y * v};
+    }
+  }
+}
+
+class Point2 {
+  var x: i32;
+  var y: i32;
+  impl Point2 as Vector {
+    fn Add[me: Point2](b: Point2) -> Point2 {
+        return {.x = me.x + b.x + 1, .y = me.y + b.y + 1};
+    }
+    fn Scale[me: Point2](v: i32) -> Point2 {
+        return {.x = me.x * v * 2, .y = me.y * v * 2};
+    }
+  }
+}
+
+fn ScaleGeneric[U:! Vector](c: U, s: i32) -> U {
+  return c.Scale(s);
+}
+
+fn AddAndScaleGeneric[T:! Vector, V:! Vector](a: T, b: V, s: i32) -> (T,V) {
+  return (ScaleGeneric(a.Add(a), s),
+  	  ScaleGeneric(b.Add(b), s));
+}
+
+fn Main() -> i32 {
+  var a: Point1 = {.x = 1, .y = 1};
+  var b: Point2 = {.x = 2, .y = 3};
+  var (p: Point1, q: Point2) = AddAndScaleGeneric(a, b, 5);
+  return q.x - p.x - 40;
+}

+ 43 - 0
executable_semantics/testdata/interface/tuple_vector_add_scale.carbon

@@ -0,0 +1,43 @@
+// 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;
+
+interface Vector {
+  fn Add[me: Self](b: Self) -> Self;
+  fn Scale[me: Self](v: i32) -> Self;
+}
+
+class Point {
+  var x: i32;
+  var y: i32;
+  impl Point as Vector {
+    fn Add[me: Point](b: Point) -> Point {
+        return {.x = me.x + b.x, .y = me.y + b.y};
+    }
+    fn Scale[me: Point](v: i32) -> Point {
+        return {.x = me.x * v, .y = me.y * v};
+    }
+  }
+}
+
+fn AddAndScaleGeneric[T:! Vector](t: (T, T), s: i32) -> T {
+  var m: auto = t[0].Add;
+  var n: auto = m(t[1]).Scale;
+  return n(s);
+}
+
+fn Main() -> i32 {
+  var a: Point = {.x = 1, .y = 1};
+  var b: Point = {.x = 2, .y = 3};
+  var p: Point = AddAndScaleGeneric((a, b), 5);
+  return p.x - 15;
+}

+ 43 - 0
executable_semantics/testdata/interface/vector_point_add_scale.carbon

@@ -0,0 +1,43 @@
+// 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;
+
+interface Vector {
+  fn Add[me: Self](b: Self) -> Self;
+  fn Scale[me: Self](v: i32) -> Self;
+}
+
+class Point {
+  var x: i32;
+  var y: i32;
+  impl Point as Vector {
+    fn Add[me: Point](b: Point) -> Point {
+        return {.x = me.x + b.x, .y = me.y + b.y};
+    }
+    fn Scale[me: Point](v: i32) -> Point {
+        return {.x = me.x * v, .y = me.y * v};
+    }
+  }
+}
+
+fn AddAndScaleGeneric[T:! Vector](a: T, b: T, s: i32) -> T {
+  var m: __Fn(T)->T = a.Add;
+  var n: __Fn(i32)->T = m(b).Scale;
+  return n(s);
+}
+
+fn Main() -> i32 {
+  var a: Point = {.x = 1, .y = 1};
+  var b: Point = {.x = 2, .y = 3};
+  var p: Point = AddAndScaleGeneric(a, b, 5);
+  return p.x - 15;
+}

+ 1 - 1
executable_semantics/testdata/tuple/fail_index_var.carbon

@@ -7,7 +7,7 @@
 // RUN: %{not} %{executable_semantics} --trace %s 2>&1 | \
 // RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes %s
 // AUTOUPDATE: %{executable_semantics} %s
-// CHECK: RUNTIME ERROR: {{.*}}/executable_semantics/testdata/tuple/fail_index_var.carbon:17: could not find `index`
+// CHECK: RUNTIME ERROR: {{.*}}/executable_semantics/testdata/tuple/fail_index_var.carbon:17: could not find `index: i32`
 
 package ExecutableSemanticsTest api;
 

Некоторые файлы не были показаны из-за большого количества измененных файлов