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

Basic support for `.Self` within `:!` bindings and `where` expressions. (#1311)

`.Self` is modeled as a new kind of expression, `DotSelfExpression`. Name resolution associates each occurrence of a `DotSelfExpression` with a generic binding, much like for an `IdentifierExpression`.

Both `:!` bindings and `where` expressions bring `.Self` into scope. The tentative intent is that if there are multiple `.Self`s in scope and they refer to different bindings, the result of using a `.Self` expression is an ambiguity error, but that is not implemented in this patch. Instead, like for `IdentifierExpression`s, we find the innermost enclosing definition of `.Self`.

`.Foo` expressions are rewritten to `.Self.Foo`, but this isn't enough to make them do anything useful, because associated constants aren't supported in general yet.
Richard Smith 3 лет назад
Родитель
Сommit
2fef76e58c

+ 5 - 0
common/fuzzing/carbon.proto

@@ -79,6 +79,10 @@ message IdentifierExpression {
   optional string name = 1;
 }
 
+message DesignatorExpression {
+  optional string name = 1;
+}
+
 message IntrinsicExpression {
   enum Intrinsic {
     UnknownIntrinsic = 0;
@@ -170,6 +174,7 @@ message Expression {
     ArrayTypeLiteral array_type_literal = 21;
     CompoundMemberAccessExpression compound_member_access = 22;
     WhereExpression where = 23;
+    DesignatorExpression designator = 24;
   }
 }
 

+ 7 - 0
common/fuzzing/proto_to_carbon.cpp

@@ -275,6 +275,13 @@ static auto ExpressionToCarbon(const Fuzzing::Expression& expression,
       break;
     }
 
+    case Fuzzing::Expression::kDesignator: {
+      const auto& designator = expression.designator();
+      out << ".";
+      IdentifierToCarbon(designator.name(), out);
+      break;
+    }
+
     case Fuzzing::Expression::kIntrinsic: {
       const auto& intrinsic = expression.intrinsic();
       switch (intrinsic.intrinsic()) {

+ 4 - 24
explorer/ast/BUILD

@@ -64,7 +64,6 @@ cc_test(
         ":ast_test_matchers",
         ":declaration",
         ":expression",
-        ":pattern",
         ":statement",
         "//common:gtest_main",
         "//explorer/common:arena",
@@ -79,7 +78,7 @@ cc_library(
     ],
     deps = [
         ":ast_node",
-        ":pattern",
+        ":expression",
         ":value_category",
         "//common:check",
         "//common:ostream",
@@ -98,7 +97,7 @@ cc_library(
     deps = [
         ":ast_node",
         ":impl_binding",
-        ":pattern",
+        ":expression",
         ":return_term",
         ":statement",
         ":static_scope",
@@ -125,8 +124,8 @@ cc_library(
 
 cc_library(
     name = "expression",
-    srcs = ["expression.cpp"],
-    hdrs = ["expression.h"],
+    srcs = ["expression.cpp", "pattern.cpp"],
+    hdrs = ["expression.h", "pattern.h"],
     deps = [
         ":ast_node",
         ":paren_contents",
@@ -168,30 +167,12 @@ cc_library(
     ],
 )
 
-cc_library(
-    name = "pattern",
-    srcs = ["pattern.cpp"],
-    hdrs = ["pattern.h"],
-    deps = [
-        ":ast_node",
-        ":expression",
-        ":static_scope",
-        ":value_category",
-        "//common:ostream",
-        "//explorer/common:arena",
-        "//explorer/common:error_builders",
-        "//explorer/common:source_location",
-        "@llvm-project//llvm:Support",
-    ],
-)
-
 cc_test(
     name = "pattern_test",
     srcs = ["pattern_test.cpp"],
     deps = [
         ":expression",
         ":paren_contents",
-        ":pattern",
         "//common:gtest_main",
         "//explorer/common:arena",
         "@com_google_googletest//:gtest",
@@ -222,7 +203,6 @@ cc_library(
     deps = [
         ":ast_node",
         ":expression",
-        ":pattern",
         ":return_term",
         ":static_scope",
         ":value_category",

+ 1 - 0
explorer/ast/ast_rtti.txt

@@ -57,6 +57,7 @@ abstract class Expression : AstNode;
   class TypeTypeLiteral : Expression;
   class ValueLiteral : Expression;
   class IdentifierExpression : Expression;
+  class DotSelfExpression : Expression;
   class IntrinsicExpression : Expression;
   class IfExpression : Expression;
   class WhereExpression : Expression;

+ 6 - 1
explorer/ast/expression.cpp

@@ -7,6 +7,7 @@
 #include <map>
 #include <optional>
 
+#include "explorer/ast/pattern.h"
 #include "explorer/common/arena.h"
 #include "explorer/common/error_builders.h"
 #include "llvm/ADT/StringExtras.h"
@@ -184,7 +185,7 @@ void Expression::Print(llvm::raw_ostream& out) const {
     }
     case ExpressionKind::WhereExpression: {
       const auto& where = cast<WhereExpression>(*this);
-      out << where.base() << " where ";
+      out << where.self_binding().type() << " where ";
       llvm::ListSeparator sep(" and ");
       for (const WhereClause* clause : where.clauses()) {
         out << sep << *clause;
@@ -213,6 +214,7 @@ void Expression::Print(llvm::raw_ostream& out) const {
       break;
     }
     case ExpressionKind::IdentifierExpression:
+    case ExpressionKind::DotSelfExpression:
     case ExpressionKind::IntLiteral:
     case ExpressionKind::BoolLiteral:
     case ExpressionKind::BoolTypeLiteral:
@@ -232,6 +234,9 @@ void Expression::PrintID(llvm::raw_ostream& out) const {
     case ExpressionKind::IdentifierExpression:
       out << cast<IdentifierExpression>(*this).name();
       break;
+    case ExpressionKind::DotSelfExpression:
+      out << ".Self";
+      break;
     case ExpressionKind::IntLiteral:
       out << cast<IntLiteral>(*this).value();
       break;

+ 44 - 5
explorer/ast/expression.h

@@ -28,6 +28,7 @@ class MemberName;
 class VariableType;
 class InterfaceType;
 class ImplBinding;
+class GenericBinding;
 
 class Expression : public AstNode {
  public:
@@ -154,6 +155,40 @@ class IdentifierExpression : public Expression {
   std::optional<ValueNodeView> value_node_;
 };
 
+// A `.Self` expression within either a `:!` binding or a standalone `where`
+// expression.
+//
+// In a `:!` binding, the type of `.Self` is always `Type`. For example, in
+// `A:! AddableWith(.Self)`, the expression `.Self` refers to the same type as
+// `A`, but with type `Type`.
+//
+// In a `where` binding, the type of `.Self` is the constraint preceding the
+// `where` keyword. For example, in `Foo where .Result is Bar(.Self)`, the type
+// of `.Self` is `Foo`.
+class DotSelfExpression : public Expression {
+ public:
+  explicit DotSelfExpression(SourceLocation source_loc)
+      : Expression(AstNodeKind::DotSelfExpression, source_loc) {}
+
+  static auto classof(const AstNode* node) -> bool {
+    return InheritsFromDotSelfExpression(node->kind());
+  }
+
+  // The self binding. Cannot be called before name resolution.
+  auto self_binding() const -> const GenericBinding& { return **self_binding_; }
+  auto self_binding() -> GenericBinding& { return **self_binding_; }
+
+  // Sets the self binding. Called only once, during name resolution.
+  void set_self_binding(Nonnull<GenericBinding*> self_binding) {
+    CARBON_CHECK(!self_binding_.has_value());
+    self_binding_ = self_binding;
+  }
+
+ private:
+  std::string name_;
+  std::optional<Nonnull<GenericBinding*>> self_binding_;
+};
+
 class SimpleMemberAccessExpression : public Expression {
  public:
   explicit SimpleMemberAccessExpression(SourceLocation source_loc,
@@ -741,20 +776,24 @@ class EqualsWhereClause : public WhereClause {
 };
 
 // A `where` expression: `AddableWith(i32) where .Result == i32`.
+//
+// The first operand is rewritten to a generic binding, for example
+// `.Self:! AddableWith(i32)`, which may be used in the clauses.
 class WhereExpression : public Expression {
  public:
-  explicit WhereExpression(SourceLocation source_loc, Nonnull<Expression*> base,
+  explicit WhereExpression(SourceLocation source_loc,
+                           Nonnull<GenericBinding*> self_binding,
                            std::vector<Nonnull<WhereClause*>> clauses)
       : Expression(AstNodeKind::WhereExpression, source_loc),
-        base_(base),
+        self_binding_(self_binding),
         clauses_(std::move(clauses)) {}
 
   static auto classof(const AstNode* node) -> bool {
     return InheritsFromWhereExpression(node->kind());
   }
 
-  auto base() const -> const Expression& { return *base_; }
-  auto base() -> Expression& { return *base_; }
+  auto self_binding() const -> const GenericBinding& { return *self_binding_; }
+  auto self_binding() -> GenericBinding& { return *self_binding_; }
 
   auto clauses() const -> llvm::ArrayRef<Nonnull<const WhereClause*>> {
     return clauses_;
@@ -762,7 +801,7 @@ class WhereExpression : public Expression {
   auto clauses() -> llvm::ArrayRef<Nonnull<WhereClause*>> { return clauses_; }
 
  private:
-  Nonnull<Expression*> base_;
+  Nonnull<GenericBinding*> self_binding_;
   std::vector<Nonnull<WhereClause*>> clauses_;
 };
 

+ 16 - 0
explorer/ast/pattern.h

@@ -78,6 +78,12 @@ class Pattern : public AstNode {
   // and after typechecking it's guaranteed to be true.
   auto has_value() const -> bool { return value_.has_value(); }
 
+  // Determines whether the pattern has already been type-checked. Should
+  // only be used by type-checking.
+  auto is_type_checked() const -> bool {
+    return static_type_.has_value() && value_.has_value();
+  }
+
  protected:
   // Constructs a Pattern representing syntax at the given line number.
   // `kind` must be the enumerator corresponding to the most-derived type being
@@ -277,12 +283,22 @@ class GenericBinding : public Pattern {
   // Set the original generic binding.
   void set_original(Nonnull<const GenericBinding*> orig) { original_ = orig; }
 
+  // Returns whether this binding has been named as a type within its own type
+  // expression via `.Self`. Set by type-checking.
+  auto named_as_type_via_dot_self() const -> bool {
+    return named_as_type_via_dot_self_;
+  }
+  // Set that this binding was named as a type within its own type expression
+  // via `.Self`. May only be called during type-checking.
+  void set_named_as_type_via_dot_self() { named_as_type_via_dot_self_ = true; }
+
  private:
   std::string name_;
   Nonnull<Expression*> type_;
   std::optional<Nonnull<const Value*>> symbolic_identity_;
   std::optional<Nonnull<const ImplBinding*>> impl_binding_;
   std::optional<Nonnull<const GenericBinding*>> original_;
+  bool named_as_type_via_dot_self_ = false;
 };
 
 // Converts paren_contents to a Pattern, interpreting the parentheses as

+ 2 - 2
explorer/ast/static_scope.h

@@ -163,7 +163,7 @@ class StaticScope {
 
   // Make `parent` a parent of this scope.
   // REQUIRES: `parent` is not already a parent of this scope.
-  void AddParent(Nonnull<StaticScope*> parent) {
+  void AddParent(Nonnull<const StaticScope*> parent) {
     parent_scopes_.push_back(parent);
   }
 
@@ -188,7 +188,7 @@ class StaticScope {
   std::unordered_map<std::string, Entry> declared_names_;
 
   // A list of scopes used for name lookup within this scope.
-  std::vector<Nonnull<StaticScope*>> parent_scopes_;
+  std::vector<Nonnull<const StaticScope*>> parent_scopes_;
 };
 
 }  // namespace Carbon

+ 16 - 1
explorer/fuzzing/ast_to_proto.cpp

@@ -13,6 +13,7 @@
 namespace Carbon {
 
 using ::llvm::cast;
+using ::llvm::isa;
 
 static auto ExpressionToProto(const Expression& expression)
     -> Fuzzing::Expression;
@@ -108,6 +109,13 @@ static auto ExpressionToProto(const Expression& expression)
     case ExpressionKind::SimpleMemberAccessExpression: {
       const auto& simple_member_access =
           cast<SimpleMemberAccessExpression>(expression);
+      if (isa<DotSelfExpression>(simple_member_access.object())) {
+        // The parser rewrites `.Foo` into `.Self.Foo`. Undo this
+        // transformation.
+        auto* designator_proto = expression_proto.mutable_designator();
+        designator_proto->set_name(simple_member_access.member());
+        break;
+      }
       auto* simple_member_access_proto =
           expression_proto.mutable_simple_member_access();
       simple_member_access_proto->set_field(simple_member_access.member());
@@ -182,7 +190,8 @@ static auto ExpressionToProto(const Expression& expression)
     case ExpressionKind::WhereExpression: {
       const auto& where = cast<WhereExpression>(expression);
       auto* where_proto = expression_proto.mutable_where();
-      *where_proto->mutable_base() = ExpressionToProto(where.base());
+      *where_proto->mutable_base() =
+          ExpressionToProto(where.self_binding().type());
       for (const WhereClause* where : where.clauses()) {
         Fuzzing::WhereClause clause_proto;
         switch (where->kind()) {
@@ -208,6 +217,12 @@ static auto ExpressionToProto(const Expression& expression)
       break;
     }
 
+    case ExpressionKind::DotSelfExpression: {
+      auto* designator_proto = expression_proto.mutable_designator();
+      designator_proto->set_name("Self");
+      break;
+    }
+
     case ExpressionKind::IntrinsicExpression: {
       const auto& intrinsic = cast<IntrinsicExpression>(expression);
       auto* intrinsic_proto = expression_proto.mutable_intrinsic();

+ 0 - 3
explorer/interpreter/BUILD

@@ -26,7 +26,6 @@ cc_library(
         "//common:ostream",
         "//explorer/ast:declaration",
         "//explorer/ast:expression",
-        "//explorer/ast:pattern",
         "//explorer/ast:statement",
         "//explorer/common:arena",
         "//explorer/common:error_builders",
@@ -141,7 +140,6 @@ cc_library(
         "//explorer/ast",
         "//explorer/ast:declaration",
         "//explorer/ast:expression",
-        "//explorer/ast:pattern",
         "//explorer/common:arena",
         "//explorer/common:error_builders",
         "@llvm-project//llvm:Support",
@@ -174,7 +172,6 @@ cc_library(
         "//explorer/ast",
         "//explorer/ast:declaration",
         "//explorer/ast:expression",
-        "//explorer/ast:pattern",
         "//explorer/ast:return_term",
         "//explorer/ast:statement",
         "//explorer/ast:static_scope",

+ 9 - 0
explorer/interpreter/interpreter.cpp

@@ -408,6 +408,7 @@ auto Interpreter::StepLvalue() -> ErrorOr<Success> {
     case ExpressionKind::IntrinsicExpression:
     case ExpressionKind::IfExpression:
     case ExpressionKind::WhereExpression:
+    case ExpressionKind::DotSelfExpression:
     case ExpressionKind::ArrayTypeLiteral:
     case ExpressionKind::InstantiateImpl:
       CARBON_FATAL() << "Can't treat expression as lvalue: " << exp;
@@ -927,6 +928,14 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
       }
       return todo_.FinishAction(value);
     }
+    case ExpressionKind::DotSelfExpression: {
+      // `.Self` always symbolically resolves to the self binding, even if it's
+      // not yet been type-checked.
+      CARBON_CHECK(act.pos() == 0);
+      const auto& dot_self = cast<DotSelfExpression>(exp);
+      return todo_.FinishAction(
+          arena_->New<VariableType>(&dot_self.self_binding()));
+    }
     case ExpressionKind::IntLiteral:
       CARBON_CHECK(act.pos() == 0);
       // { {n :: C, E, F} :: S, H} -> { {n' :: C, E, F} :: S, H}

+ 22 - 9
explorer/interpreter/resolve_names.cpp

@@ -166,6 +166,15 @@ static auto ResolveNames(Expression& expression,
       identifier.set_value_node(value_node);
       break;
     }
+    case ExpressionKind::DotSelfExpression: {
+      auto& dot_self = cast<DotSelfExpression>(expression);
+      CARBON_ASSIGN_OR_RETURN(
+          const auto value_node,
+          enclosing_scope.Resolve(".Self", dot_self.source_loc()));
+      dot_self.set_self_binding(const_cast<GenericBinding*>(
+          &cast<GenericBinding>(value_node.base())));
+      break;
+    }
     case ExpressionKind::IntrinsicExpression:
       CARBON_RETURN_IF_ERROR(ResolveNames(
           cast<IntrinsicExpression>(expression).args(), enclosing_scope));
@@ -182,13 +191,14 @@ static auto ResolveNames(Expression& expression,
     }
     case ExpressionKind::WhereExpression: {
       auto& where = cast<WhereExpression>(expression);
-      // TODO: Introduce `.Self` into scope?
-      // StaticScope where_scope;
-      // where_scope.AddParent(&enclosing_scope);
-      // where_scope.Add(".Self", ???);
-      CARBON_RETURN_IF_ERROR(ResolveNames(where.base(), enclosing_scope));
+      CARBON_RETURN_IF_ERROR(
+          ResolveNames(where.self_binding().type(), enclosing_scope));
+      // Introduce `.Self` into scope on the right of the `where` keyword.
+      StaticScope where_scope;
+      where_scope.AddParent(&enclosing_scope);
+      CARBON_RETURN_IF_ERROR(where_scope.Add(".Self", &where.self_binding()));
       for (Nonnull<WhereClause*> clause : where.clauses()) {
-        CARBON_RETURN_IF_ERROR(ResolveNames(*clause, enclosing_scope));
+        CARBON_RETURN_IF_ERROR(ResolveNames(*clause, where_scope));
       }
       break;
     }
@@ -253,7 +263,11 @@ static auto ResolveNames(Pattern& pattern, StaticScope& enclosing_scope)
     }
     case PatternKind::GenericBinding: {
       auto& binding = cast<GenericBinding>(pattern);
-      CARBON_RETURN_IF_ERROR(ResolveNames(binding.type(), enclosing_scope));
+      // `.Self` is in scope in the context of the type.
+      StaticScope self_scope;
+      self_scope.AddParent(&enclosing_scope);
+      CARBON_RETURN_IF_ERROR(self_scope.Add(".Self", &binding));
+      CARBON_RETURN_IF_ERROR(ResolveNames(binding.type(), self_scope));
       if (binding.name() != AnonymousName) {
         CARBON_RETURN_IF_ERROR(enclosing_scope.Add(binding.name(), &binding));
       }
@@ -425,8 +439,7 @@ static auto ResolveNames(Declaration& declaration, StaticScope& enclosing_scope)
       StaticScope function_scope;
       function_scope.AddParent(&enclosing_scope);
       for (Nonnull<GenericBinding*> binding : function.deduced_parameters()) {
-        CARBON_RETURN_IF_ERROR(ResolveNames(binding->type(), function_scope));
-        CARBON_RETURN_IF_ERROR(function_scope.Add(binding->name(), binding));
+        CARBON_RETURN_IF_ERROR(ResolveNames(*binding, function_scope));
       }
       if (function.is_method()) {
         CARBON_RETURN_IF_ERROR(

+ 42 - 21
explorer/interpreter/type_checker.cpp

@@ -757,11 +757,6 @@ class ConstraintTypeBuilder {
             source_loc, ".Self", arena->New<TypeTypeLiteral>(source_loc))) {}
   ConstraintTypeBuilder(Nonnull<const GenericBinding*> self_binding)
       : self_binding_(self_binding) {}
-  ConstraintTypeBuilder(Nonnull<const ConstraintType*> constraint)
-      : self_binding_(constraint->self_binding()),
-        impl_constraints_(constraint->impl_constraints()),
-        equality_constraints_(constraint->equality_constraints()),
-        lookup_contexts_(constraint->lookup_contexts()) {}
 
   // Produce a type that refers to the `.Self` type of the constraint.
   auto MakeSelfType(Nonnull<Arena*> arena) const
@@ -1090,8 +1085,6 @@ auto TypeChecker::MatchImpl(const InterfaceType& iface,
     return std::nullopt;
   }
 
-  // TODO: If the `interface` is a ConstraintType, for every impl_constraint,
-  // try deduction against that.
   if (ErrorOr<Success> e = ArgumentDeduction(
           source_loc, "match", impl.deduced, deduced_args, impl.interface,
           &iface, /*allow_implicit_conversion=*/false, impl_scope);
@@ -1259,6 +1252,8 @@ auto TypeChecker::DeduceCallBindings(
       impl_bindings, impl_scope, call.source_loc(), generic_bindings, impls));
   call.set_impls(impls);
 
+  // TODO: Ensure any equality constraints are satisfied.
+
   // Convert the arguments to the parameter type.
   Nonnull<const Value*> param_type = Substitute(generic_bindings, params_type);
   CARBON_ASSIGN_OR_RETURN(
@@ -1778,6 +1773,17 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
       ident.set_value_category(ident.value_node().value_category());
       return Success();
     }
+    case ExpressionKind::DotSelfExpression: {
+      auto& dot_self = cast<DotSelfExpression>(*e);
+      if (dot_self.self_binding().is_type_checked()) {
+        dot_self.set_static_type(&dot_self.self_binding().static_type());
+      } else {
+        dot_self.set_static_type(arena_->New<TypeType>());
+        dot_self.self_binding().set_named_as_type_via_dot_self();
+      }
+      dot_self.set_value_category(ValueCategory::Let);
+      return Success();
+    }
     case ExpressionKind::IntLiteral:
       e->set_value_category(ValueCategory::Let);
       e->set_static_type(arena_->New<IntType>());
@@ -2074,28 +2080,38 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
     }
     case ExpressionKind::WhereExpression: {
       auto& where = cast<WhereExpression>(*e);
-      CARBON_RETURN_IF_ERROR(TypeCheckExp(&where.base(), impl_scope));
+      ImplScope inner_impl_scope;
+      inner_impl_scope.AddParent(&impl_scope);
+      CARBON_RETURN_IF_ERROR(TypeCheckPattern(&where.self_binding(),
+                                              std::nullopt, inner_impl_scope,
+                                              ValueCategory::Let));
       for (Nonnull<WhereClause*> clause : where.clauses()) {
-        CARBON_RETURN_IF_ERROR(TypeCheckWhereClause(clause, impl_scope));
-      }
-
-      const ConstraintType* base;
-      const Value& base_type = where.base().static_type();
-      if (auto* constraint_type_type =
-              dyn_cast<TypeOfConstraintType>(&base_type)) {
-        base = &constraint_type_type->constraint_type();
-      } else if (auto* interface_type_type =
-                     dyn_cast<TypeOfInterfaceType>(&base_type)) {
-        base = MakeConstraintForInterface(
-            e->source_loc(), &interface_type_type->interface_type());
+        CARBON_RETURN_IF_ERROR(TypeCheckWhereClause(clause, inner_impl_scope));
+      }
+
+      std::optional<Nonnull<const ConstraintType*>> base;
+      const Value& base_type = where.self_binding().static_type();
+      if (auto* constraint_type = dyn_cast<ConstraintType>(&base_type)) {
+        base = constraint_type;
+      } else if (auto* interface_type = dyn_cast<InterfaceType>(&base_type)) {
+        base = MakeConstraintForInterface(e->source_loc(), interface_type);
+      } else if (isa<TypeType>(base_type)) {
+        // Start with an unconstrained type.
       } else {
         return CompilationError(e->source_loc())
                << "expected constraint as first operand of `where` expression, "
                << "found " << base_type;
       }
 
+      // Start with the given constraint, if any.
+      ConstraintTypeBuilder builder(&where.self_binding());
+      if (base) {
+        BindingMap map;
+        map[(*base)->self_binding()] = builder.MakeSelfType(arena_);
+        builder.Add(cast<ConstraintType>(Substitute(map, *base)));
+      }
+
       // Apply the `where` clauses.
-      ConstraintTypeBuilder builder(base);
       for (Nonnull<const WhereClause*> clause : where.clauses()) {
         switch (clause->kind()) {
           case WhereClauseKind::IsWhereClause: {
@@ -2340,6 +2356,11 @@ auto TypeChecker::TypeCheckPattern(
                << binding;
       }
       binding.set_static_type(type);
+      if (binding.named_as_type_via_dot_self() && !IsTypeOfType(type)) {
+        return CompilationError(binding.type().source_loc())
+               << "`.Self` used in type of non-type binding `" << binding.name()
+               << "`";
+      }
       CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> val,
                               InterpPattern(&binding, arena_, trace_stream_));
       binding.set_symbolic_identity(val);

+ 0 - 1
explorer/syntax/BUILD

@@ -81,7 +81,6 @@ cc_library(
         "//explorer/ast:declaration",
         "//explorer/ast:expression",
         "//explorer/ast:paren_contents",
-        "//explorer/ast:pattern",
         "//explorer/ast:value_category",
         "//explorer/common:arena",
         "//explorer/common:error_builders",

+ 14 - 1
explorer/syntax/parser.ypp

@@ -301,6 +301,15 @@ api_or_impl:
 primary_expression:
   identifier
     { $$ = arena->New<IdentifierExpression>(context.source_loc(), $1); }
+| designator
+    {
+      // `.Foo` is rewritten to `.Self.Foo`.
+      $$ = arena->New<SimpleMemberAccessExpression>(
+          context.source_loc(),
+          arena->New<DotSelfExpression>(context.source_loc()), $1);
+    }
+| PERIOD SELF
+    { $$ = arena->New<DotSelfExpression>(context.source_loc()); }
 | integer_literal
     { $$ = arena->New<IntLiteral>(context.source_loc(), $1); }
 | string_literal
@@ -544,7 +553,11 @@ where_clause_list:
 ;
 where_expression:
   type_expression WHERE where_clause_list
-    { $$ = arena->New<WhereExpression>(context.source_loc(), $1, $3); }
+    {
+      auto* self =
+          arena -> New<GenericBinding>(context.source_loc(), ".Self", $1);
+      $$ = arena->New<WhereExpression>(context.source_loc(), self, $3);
+    }
 ;
 statement_expression:
   ref_deref_expression

+ 30 - 0
explorer/testdata/constraint/binding_self.carbon

@@ -0,0 +1,30 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// RUN: %{explorer} %s 2>&1 | \
+// RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
+// RUN: %{explorer} --parser_debug --trace_file=- %s 2>&1 | \
+// RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes %s
+// AUTOUPDATE: %{explorer} %s
+// CHECK: result: 12
+
+package ExplorerTest api;
+
+interface SwizzleWith(T:! Type) {
+  fn Op[me: Self](x: T) -> i32;
+}
+
+impl i32 as SwizzleWith(i32) {
+  fn Op[me: Self](x: Self) -> Self { return me * 10 + x; }
+}
+
+fn F[T:! SwizzleWith(.Self)](v: T, w: T) -> i32 {
+  return v.Op(w);
+}
+
+fn Main() -> i32 {
+  var one: i32 = 1;
+  var two: i32 = 2;
+  return F(one, two);
+}

+ 1 - 1
explorer/testdata/constraint/combine_equality.carbon

@@ -15,7 +15,7 @@ impl i32 as I {}
 
 fn F(A:! i32, B:! i32, C:! i32, D:! i32, E:! i32,
      T:! I where A == B and C == D and C == E and B == D) {
-  // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/constraint/combine_equality.carbon:[[@LINE+1]]: member access, F not in constraint interface I where .Self:! Type is interface I, A:! i32 == B:! i32 == E:! i32 == C:! i32 == D:! i32
+  // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/constraint/combine_equality.carbon:[[@LINE+1]]: member access, F not in constraint interface I where .Self:! I is interface I, A:! i32 == B:! i32 == E:! i32 == C:! i32 == D:! i32
   T.F();
 }
 

+ 31 - 0
explorer/testdata/constraint/dot_self_is_other.carbon

@@ -0,0 +1,31 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// RUN: %{explorer} %s 2>&1 | \
+// RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
+// RUN: %{explorer} --parser_debug --trace_file=- %s 2>&1 | \
+// RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes %s
+// AUTOUPDATE: %{explorer} %s
+// CHECK: result: 12
+
+package ExplorerTest api;
+
+interface A { fn F() -> i32; }
+interface B { fn F() -> i32; }
+
+impl i32 as A { fn F() -> i32 { return 1; } }
+impl i32 as B { fn F() -> i32 { return 2; } }
+
+fn GetA[T:! A](x: T) -> i32 { return x.F(); }
+fn GetAB[T:! B where .Self is A](x: T) -> i32 {
+  // Note that there's no ambiguity in the use of the name F here.
+  // `x.F` is `x.(B.F)` because the constraint only treats `B`, not
+  // `A`, as a lookup context.
+  return 10 * GetA(x) + x.F();
+}
+
+fn Main() -> i32 {
+  var v: i32 = 0;
+  return GetAB(v);
+}

+ 22 - 0
explorer/testdata/constraint/fail_dot_self_after_scope.carbon

@@ -0,0 +1,22 @@
+// 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} %{explorer} %s 2>&1 | \
+// RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
+// RUN: %{not} %{explorer} --parser_debug --trace_file=- %s 2>&1 | \
+// RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes %s
+// AUTOUPDATE: %{explorer} %s
+
+package ExplorerTest api;
+
+interface FrobWith(T:! Type) {}
+
+fn F[T:! FrobWith(.Self)]
+  // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/constraint/fail_dot_self_after_scope.carbon:[[@LINE+1]]: could not resolve '.Self'
+  (U: .Self) {
+}
+
+fn Main() -> i32 {
+  return 0;
+}

+ 22 - 0
explorer/testdata/constraint/fail_dot_self_after_scope_2.carbon

@@ -0,0 +1,22 @@
+// 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} %{explorer} %s 2>&1 | \
+// RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
+// RUN: %{not} %{explorer} --parser_debug --trace_file=- %s 2>&1 | \
+// RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes %s
+// AUTOUPDATE: %{explorer} %s
+
+package ExplorerTest api;
+
+interface FrobWith(T:! Type) {}
+
+fn F[T:! FrobWith(.Self),
+     // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/constraint/fail_dot_self_after_scope_2.carbon:[[@LINE+1]]: `.Self` used in type of non-type binding `U`
+     U:! .Self]() {
+}
+
+fn Main() -> i32 {
+  return 0;
+}

+ 16 - 0
explorer/testdata/constraint/fail_dot_self_not_in_scope.carbon

@@ -0,0 +1,16 @@
+// 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} %{explorer} %s 2>&1 | \
+// RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
+// RUN: %{not} %{explorer} --parser_debug --trace_file=- %s 2>&1 | \
+// RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes %s
+// AUTOUPDATE: %{explorer} %s
+
+package ExplorerTest api;
+
+// CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/constraint/fail_dot_self_not_in_scope.carbon:[[@LINE+1]]: could not resolve '.Self'
+fn Main() -> .Self {
+  return 0;
+}

+ 16 - 0
explorer/testdata/constraint/fail_infinite_self.carbon

@@ -0,0 +1,16 @@
+// 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} %{explorer} %s 2>&1 | \
+// RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
+// RUN: %{not} %{explorer} --parser_debug --trace_file=- %s 2>&1 | \
+// RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes %s
+// AUTOUPDATE: %{explorer} %s
+
+package ExplorerTest api;
+
+// CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/constraint/fail_infinite_self.carbon:[[@LINE+1]]: `.Self` used in type of non-type binding `T`
+fn F[T:! .Self]() {}
+
+fn Main() -> i32 { return 0; }

+ 16 - 0
explorer/testdata/constraint/fail_infinite_self_ptr.carbon

@@ -0,0 +1,16 @@
+// 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} %{explorer} %s 2>&1 | \
+// RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
+// RUN: %{not} %{explorer} --parser_debug --trace_file=- %s 2>&1 | \
+// RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes %s
+// AUTOUPDATE: %{explorer} %s
+
+package ExplorerTest api;
+
+// CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/constraint/fail_infinite_self_ptr.carbon:[[@LINE+1]]: `.Self` used in type of non-type binding `T`
+fn F[T:! .Self*]() {}
+
+fn Main() -> i32 { return 0; }

+ 18 - 0
explorer/testdata/constraint/fail_nontype_self.carbon

@@ -0,0 +1,18 @@
+// 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} %{explorer} %s 2>&1 | \
+// RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
+// RUN: %{not} %{explorer} --parser_debug --trace_file=- %s 2>&1 | \
+// RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes %s
+// AUTOUPDATE: %{explorer} %s
+
+package ExplorerTest api;
+
+class X(T:! Type) {}
+
+// CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/constraint/fail_nontype_self.carbon:[[@LINE+1]]: `.Self` used in type of non-type binding `T`
+fn F[T:! X(.Self)](x: T) {}
+
+fn Main() -> i32 { return 0; }

+ 26 - 0
explorer/testdata/constraint/qualified_lookup_in_where.carbon

@@ -0,0 +1,26 @@
+// 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: %{explorer} %s 2>&1 | \
+// RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
+// RUN: %{explorer} --parser_debug --trace_file=- %s 2>&1 | \
+// RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes %s
+// AUTOUPDATE: %{explorer} %s
+// CHECK: result: 122
+
+package ExplorerTest api;
+
+interface A { fn F() -> i32; }
+interface B { fn F() -> i32; }
+
+impl i32 as A { fn F() -> i32 { return 1; } }
+impl i32 as B { fn F() -> i32 { return 2; } }
+
+fn GetAB(T:! B where .Self is A) -> i32 {
+  return 100 * T.(A.F)() + 10 * T.(B.F)() + T.F();
+}
+
+fn Main() -> i32 {
+  return GetAB(i32);
+}

+ 34 - 0
explorer/testdata/constraint/where_self.carbon

@@ -0,0 +1,34 @@
+// 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: %{explorer} %s 2>&1 | \
+// RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
+// RUN: %{explorer} --parser_debug --trace_file=- %s 2>&1 | \
+// RUN:   %{FileCheck} --match-full-lines --allow-unused-prefixes %s
+// AUTOUPDATE: %{explorer} %s
+// CHECK: result: 12
+
+package ExplorerTest api;
+
+interface SwizzleWith(T:! Type) {
+  fn Op[me: Self](x: T) -> i32;
+}
+
+// TODO: There should be some way to write this that includes
+// `SwizzleWith(.Self)` in the list of lookup contexts.
+alias Swizzle = Type where .Self is SwizzleWith(.Self);
+
+impl i32 as SwizzleWith(i32) {
+  fn Op[me: Self](x: Self) -> Self { return me * 10 + x; }
+}
+
+fn F[T:! Swizzle](v: T, w: T) -> i32 {
+  return v.(SwizzleWith(T).Op)(w);
+}
+
+fn Main() -> i32 {
+  var one: i32 = 1;
+  var two: i32 = 2;
+  return F(one, two);
+}