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

Add support for `Self` and `impl as` (#1194)

- Makes `Self` a keyword
- Inside `class Foo { `... `}`, `Self` means `Foo`
- Inside `class Foo(T:! Type, U:! Type) {` ...` }`, `Self` means `Foo(T, U)`
- `impl as Bar` means `impl Self as Bar`
- Inside `external impl Foo as Bar {`...`}`, not in a scope already defining `Self`, `Self` means `Foo`

Implemented by introducing a new kind of declaration, a `SelfDeclaration`, that is automatically added to class and impl declarations.

Co-authored-by: Jon Meow <jperkins@google.com>
josh11b 4 лет назад
Родитель
Сommit
0a93289706
31 измененных файлов с 820 добавлено и 25 удалено
  1. 1 0
      explorer/ast/ast_rtti.txt
  2. 16 2
      explorer/ast/declaration.cpp
  3. 26 1
      explorer/ast/declaration.h
  4. 4 0
      explorer/fuzzing/ast_to_proto.cpp
  5. 1 0
      explorer/interpreter/BUILD
  6. 1 0
      explorer/interpreter/interpreter.cpp
  7. 1 0
      explorer/interpreter/resolve_control_flow.cpp
  8. 21 2
      explorer/interpreter/resolve_names.cpp
  9. 63 17
      explorer/interpreter/type_checker.cpp
  10. 1 1
      explorer/interpreter/type_checker.h
  11. 2 0
      explorer/syntax/lexer.lpp
  12. 19 2
      explorer/syntax/parser.ypp
  13. 20 0
      explorer/testdata/basic_syntax/fail_var_named_self.carbon
  14. 27 0
      explorer/testdata/class/class_function_self.carbon
  15. 23 0
      explorer/testdata/class/fail_class_named_self.carbon
  16. 31 0
      explorer/testdata/class/method_self.carbon
  17. 31 0
      explorer/testdata/generic_class/fail_no_args.carbon
  18. 36 0
      explorer/testdata/generic_class/fail_self_with_arg.carbon
  19. 48 0
      explorer/testdata/generic_class/impl_with_argument.carbon
  20. 48 0
      explorer/testdata/generic_class/impl_with_self.carbon
  21. 31 0
      explorer/testdata/generic_class/use_self.carbon
  22. 19 0
      explorer/testdata/global_variable/fail_named_self.carbon
  23. 49 0
      explorer/testdata/impl/param_impl_with_self.carbon
  24. 45 0
      explorer/testdata/interface/external_impl_use_self.carbon
  25. 45 0
      explorer/testdata/interface/fail_external_impl_omit_self.carbon
  26. 44 0
      explorer/testdata/interface/fail_external_impl_self.carbon
  27. 39 0
      explorer/testdata/interface/impl_self_interface_parameter.carbon
  28. 46 0
      explorer/testdata/interface/omit_self.carbon
  29. 45 0
      explorer/testdata/interface/with_self.carbon
  30. 19 0
      explorer/testdata/let/fail_global_named_self.carbon
  31. 18 0
      explorer/testdata/let/fail_local_named_self.carbon

+ 1 - 0
explorer/ast/ast_rtti.txt

@@ -13,6 +13,7 @@ abstract class Pattern : AstNode;
   class ExpressionPattern : Pattern;
 abstract class Declaration : AstNode;
   class FunctionDeclaration : Declaration;
+  class SelfDeclaration : Declaration;
   class ClassDeclaration : Declaration;
   class ChoiceDeclaration : Declaration;
   class VariableDeclaration : Declaration;

+ 16 - 2
explorer/ast/declaration.cpp

@@ -73,6 +73,11 @@ void Declaration::Print(llvm::raw_ostream& out) const {
       out << ";\n";
       break;
     }
+
+    case DeclarationKind::SelfDeclaration: {
+      out << "Self";
+      break;
+    }
   }
 }
 
@@ -117,6 +122,11 @@ void Declaration::PrintID(llvm::raw_ostream& out) const {
       out << "var " << var.binding();
       break;
     }
+
+    case DeclarationKind::SelfDeclaration: {
+      out << "Self";
+      break;
+    }
   }
 }
 
@@ -134,6 +144,8 @@ auto GetName(const Declaration& declaration) -> std::optional<std::string> {
       return cast<VariableDeclaration>(declaration).binding().name();
     case DeclarationKind::ImplDeclaration:
       return std::nullopt;
+    case DeclarationKind::SelfDeclaration:
+      return cast<SelfDeclaration>(declaration).name();
   }
 }
 
@@ -228,8 +240,10 @@ auto ImplDeclaration::Create(Nonnull<Arena*> arena, SourceLocation source_loc,
                << "illegal AST node in implicit parameter list of impl";
     }
   }
-  return arena->New<ImplDeclaration>(source_loc, kind, impl_type, interface,
-                                     resolved_params, members);
+  Nonnull<SelfDeclaration*> self_decl =
+      arena->New<SelfDeclaration>(impl_type->source_loc());
+  return arena->New<ImplDeclaration>(source_loc, kind, impl_type, self_decl,
+                                     interface, resolved_params, members);
 }
 
 void AlternativeSignature::Print(llvm::raw_ostream& out) const {

+ 26 - 1
explorer/ast/declaration.h

@@ -161,15 +161,32 @@ class FunctionDeclaration : public Declaration {
   std::optional<Nonnull<Block*>> body_;
 };
 
+class SelfDeclaration : public Declaration {
+ public:
+  using ImplementsCarbonValueNode = void;
+
+  explicit SelfDeclaration(SourceLocation source_loc)
+      : Declaration(AstNodeKind::SelfDeclaration, source_loc) {}
+
+  static auto classof(const AstNode* node) -> bool {
+    return InheritsFromSelfDeclaration(node->kind());
+  }
+
+  auto name() const -> const std::string { return "Self"; }
+  auto value_category() const -> ValueCategory { return ValueCategory::Let; }
+};
+
 class ClassDeclaration : public Declaration {
  public:
   using ImplementsCarbonValueNode = void;
 
   ClassDeclaration(SourceLocation source_loc, std::string name,
+                   Nonnull<SelfDeclaration*> self_decl,
                    std::optional<Nonnull<TuplePattern*>> type_params,
                    std::vector<Nonnull<Declaration*>> members)
       : Declaration(AstNodeKind::ClassDeclaration, source_loc),
         name_(std::move(name)),
+        self_decl_(self_decl),
         type_params_(type_params),
         members_(std::move(members)) {}
 
@@ -184,6 +201,8 @@ class ClassDeclaration : public Declaration {
   auto type_params() -> std::optional<Nonnull<TuplePattern*>> {
     return type_params_;
   }
+  auto self() const -> Nonnull<const SelfDeclaration*> { return self_decl_; }
+  auto self() -> Nonnull<SelfDeclaration*> { return self_decl_; }
 
   auto members() const -> llvm::ArrayRef<Nonnull<Declaration*>> {
     return members_;
@@ -193,6 +212,7 @@ class ClassDeclaration : public Declaration {
 
  private:
   std::string name_;
+  Nonnull<SelfDeclaration*> self_decl_;
   std::optional<Nonnull<TuplePattern*>> type_params_;
   std::vector<Nonnull<Declaration*>> members_;
 };
@@ -338,12 +358,14 @@ class ImplDeclaration : public Declaration {
   // Use `Create` instead.
   ImplDeclaration(SourceLocation source_loc, ImplKind kind,
                   Nonnull<Expression*> impl_type,
+                  Nonnull<SelfDeclaration*> self_decl,
                   Nonnull<Expression*> interface,
                   std::vector<Nonnull<GenericBinding*>> deduced_params,
                   std::vector<Nonnull<Declaration*>> members)
       : Declaration(AstNodeKind::ImplDeclaration, source_loc),
         kind_(kind),
         impl_type_(impl_type),
+        self_decl_(self_decl),
         interface_(interface),
         deduced_parameters_(std::move(deduced_params)),
         members_(std::move(members)) {}
@@ -381,10 +403,13 @@ class ImplDeclaration : public Declaration {
   auto impl_bindings() const -> llvm::ArrayRef<Nonnull<const ImplBinding*>> {
     return impl_bindings_;
   }
+  auto self() const -> Nonnull<const SelfDeclaration*> { return self_decl_; }
+  auto self() -> Nonnull<SelfDeclaration*> { return self_decl_; }
 
  private:
   ImplKind kind_;
-  Nonnull<Expression*> impl_type_;  // TODO: make this optional
+  Nonnull<Expression*> impl_type_;
+  Nonnull<SelfDeclaration*> self_decl_;
   Nonnull<Expression*> interface_;
   std::optional<Nonnull<const Value*>> interface_type_;
   std::vector<Nonnull<GenericBinding*>> deduced_parameters_;

+ 4 - 0
explorer/fuzzing/ast_to_proto.cpp

@@ -547,6 +547,10 @@ static auto DeclarationToProto(const Declaration& declaration)
       }
       break;
     }
+
+    case DeclarationKind::SelfDeclaration: {
+      FATAL() << "Unreachable SelfDeclaration in DeclarationToProto().";
+    }
   }
   return declaration_proto;
 }

+ 1 - 0
explorer/interpreter/BUILD

@@ -169,6 +169,7 @@ cc_library(
     srcs = ["resolve_names.cpp"],
     hdrs = ["resolve_names.h"],
     deps = [
+        ":action_and_value",
         "//common:check",
         "//explorer/ast",
         "//explorer/ast:declaration",

+ 1 - 0
explorer/interpreter/interpreter.cpp

@@ -1279,6 +1279,7 @@ auto Interpreter::StepDeclaration() -> ErrorOr<Success> {
     case DeclarationKind::ChoiceDeclaration:
     case DeclarationKind::InterfaceDeclaration:
     case DeclarationKind::ImplDeclaration:
+    case DeclarationKind::SelfDeclaration:
       // These declarations have no run-time effects.
       return todo_.FinishAction();
   }

+ 1 - 0
explorer/interpreter/resolve_control_flow.cpp

@@ -152,6 +152,7 @@ auto ResolveControlFlow(Nonnull<Declaration*> declaration) -> ErrorOr<Success> {
     }
     case DeclarationKind::ChoiceDeclaration:
     case DeclarationKind::VariableDeclaration:
+    case DeclarationKind::SelfDeclaration:
       // do nothing
       break;
   }

+ 21 - 2
explorer/interpreter/resolve_names.cpp

@@ -49,13 +49,19 @@ static auto AddExposedNames(const Declaration& declaration,
       RETURN_IF_ERROR(enclosing_scope.Add(choice.name(), &choice));
       break;
     }
-    case DeclarationKind::VariableDeclaration:
+    case DeclarationKind::VariableDeclaration: {
       auto& var = cast<VariableDeclaration>(declaration);
       if (var.binding().name() != AnonymousName) {
         RETURN_IF_ERROR(
             enclosing_scope.Add(var.binding().name(), &var.binding()));
       }
       break;
+    }
+    case DeclarationKind::SelfDeclaration: {
+      auto& self = cast<SelfDeclaration>(declaration);
+      RETURN_IF_ERROR(enclosing_scope.Add("Self", &self));
+      break;
+    }
   }
   return Success();
 }
@@ -324,7 +330,15 @@ static auto ResolveNames(Declaration& declaration, StaticScope& enclosing_scope)
         RETURN_IF_ERROR(impl_scope.Add(binding->name(), binding));
       }
       RETURN_IF_ERROR(ResolveNames(*impl.impl_type(), impl_scope));
-      RETURN_IF_ERROR(ResolveNames(impl.interface(), enclosing_scope));
+      // Only add `Self` to the impl_scope if it is not already in the enclosing
+      // scope. Add `Self` after we resolve names for the impl_type, so you
+      // can't write something like `impl Vector(Self) as ...`. Add `Self`
+      // before resolving names in the interface, so you can write something
+      // like `impl VeryLongTypeName as AddWith(Self)`
+      if (!enclosing_scope.Resolve("Self", impl.source_loc()).ok()) {
+        RETURN_IF_ERROR(AddExposedNames(*impl.self(), impl_scope));
+      }
+      RETURN_IF_ERROR(ResolveNames(impl.interface(), impl_scope));
       for (Nonnull<Declaration*> member : impl.members()) {
         RETURN_IF_ERROR(AddExposedNames(*member, impl_scope));
       }
@@ -359,6 +373,7 @@ static auto ResolveNames(Declaration& declaration, StaticScope& enclosing_scope)
       StaticScope class_scope;
       class_scope.AddParent(&enclosing_scope);
       RETURN_IF_ERROR(class_scope.Add(class_decl.name(), &class_decl));
+      RETURN_IF_ERROR(AddExposedNames(*class_decl.self(), class_scope));
       if (class_decl.type_params().has_value()) {
         RETURN_IF_ERROR(ResolveNames(**class_decl.type_params(), class_scope));
       }
@@ -400,6 +415,10 @@ static auto ResolveNames(Declaration& declaration, StaticScope& enclosing_scope)
       }
       break;
     }
+
+    case DeclarationKind::SelfDeclaration: {
+      FATAL() << "Unreachable: resolving names for `Self` declaration";
+    }
   }
   return Success();
 }

+ 63 - 17
explorer/interpreter/type_checker.cpp

@@ -1757,11 +1757,13 @@ auto TypeChecker::DeclareClassDeclaration(Nonnull<ClassDeclaration*> class_decl,
   if (trace_stream_) {
     **trace_stream_ << "** declaring class " << class_decl->name() << "\n";
   }
+  Nonnull<SelfDeclaration*> self = class_decl->self();
   if (class_decl->type_params().has_value()) {
+    Nonnull<TuplePattern*> type_params = *class_decl->type_params();
     ImplScope class_scope;
     class_scope.AddParent(&enclosing_scope);
-    RETURN_IF_ERROR(TypeCheckPattern(*class_decl->type_params(), std::nullopt,
-                                     class_scope, ValueCategory::Let));
+    RETURN_IF_ERROR(TypeCheckPattern(type_params, std::nullopt, class_scope,
+                                     ValueCategory::Let));
     if (trace_stream_) {
       **trace_stream_ << class_scope;
     }
@@ -1773,6 +1775,27 @@ auto TypeChecker::DeclareClassDeclaration(Nonnull<ClassDeclaration*> class_decl,
     class_decl->set_static_type(
         arena_->New<TypeOfParameterizedEntityName>(param_name));
 
+    // For class declaration `class MyType(T:! Type, U:! AnInterface)`, `Self`
+    // should have the value `MyType(T, U)`.
+    BindingMap generic_args;
+    for (Nonnull<Pattern*> field : type_params->fields()) {
+      // TODO(#1229): Nothing is currently enforcing that the deduced parameter
+      // list only contains generic bindings. We need to decide if Carbon should
+      // allow expressions like:
+      //     class B((T:! Type, U:! Type), V:! Type) {}
+      CHECK(field->kind() == PatternKind::GenericBinding);
+      auto& binding = cast<GenericBinding>(*field);
+      // binding.symbolic_identity() set by call to `TypeCheckPattern(...)`
+      // above.
+      generic_args[&binding] = *binding.symbolic_identity();
+    }
+    // `self_type` is like `class_type` but with the type parameters bound to
+    // their symbolic identity.
+    Nonnull<NominalClassType*> self_type =
+        arena_->New<NominalClassType>(class_decl, generic_args);
+    SetConstantValue(self, self_type);
+    self->set_static_type(arena_->New<TypeOfClassType>(self_type));
+
     for (Nonnull<Declaration*> m : class_decl->members()) {
       RETURN_IF_ERROR(DeclareDeclaration(m, class_scope));
     }
@@ -1782,8 +1805,14 @@ auto TypeChecker::DeclareClassDeclaration(Nonnull<ClassDeclaration*> class_decl,
     // before we start processing the members.
     Nonnull<NominalClassType*> class_type =
         arena_->New<NominalClassType>(class_decl);
+    Nonnull<TypeOfClassType*> static_type =
+        arena_->New<TypeOfClassType>(class_type);
     SetConstantValue(class_decl, class_type);
-    class_decl->set_static_type(arena_->New<TypeOfClassType>(class_type));
+    class_decl->set_static_type(static_type);
+    // For the class declaration `class MyType`, `Self` should have the same
+    // value as `MyType`.
+    SetConstantValue(self, class_type);
+    self->set_static_type(static_type);
 
     for (Nonnull<Declaration*> m : class_decl->members()) {
       RETURN_IF_ERROR(DeclareDeclaration(m, enclosing_scope));
@@ -1890,20 +1919,6 @@ auto TypeChecker::DeclareImplDeclaration(Nonnull<ImplDeclaration*> impl_decl,
   if (trace_stream_) {
     **trace_stream_ << "declaring " << *impl_decl << "\n";
   }
-  RETURN_IF_ERROR(TypeCheckExp(&impl_decl->interface(), enclosing_scope));
-  ASSIGN_OR_RETURN(Nonnull<const Value*> written_iface_type,
-                   InterpExp(&impl_decl->interface(), arena_, trace_stream_));
-
-  const auto* iface_type = dyn_cast<InterfaceType>(written_iface_type);
-  if (!iface_type) {
-    return CompilationError(impl_decl->interface().source_loc())
-           << "expected constraint after `as`, found value of type "
-           << *written_iface_type;
-  }
-
-  const auto& iface_decl = iface_type->declaration();
-  impl_decl->set_interface_type(iface_type);
-
   ImplScope impl_scope;
   impl_scope.AddParent(&enclosing_scope);
   std::vector<Nonnull<const ImplBinding*>> impl_bindings;
@@ -1920,6 +1935,30 @@ auto TypeChecker::DeclareImplDeclaration(Nonnull<ImplDeclaration*> impl_decl,
   RETURN_IF_ERROR(TypeCheckExp(impl_decl->impl_type(), impl_scope));
   ASSIGN_OR_RETURN(Nonnull<const Value*> impl_type_value,
                    InterpExp(impl_decl->impl_type(), arena_, trace_stream_));
+
+  // Set `Self` to `impl_type`. We do this whether or not it `Self` resolves to
+  // it or the `Self` from an enclosing scope. This needs to be done before
+  // processing the interface, in case the interface expression uses `Self`.
+  Nonnull<SelfDeclaration*> self = impl_decl->self();
+  self->set_constant_value(impl_type_value);
+  // Static type set in call to `TypeCheckExp(...)` above.
+  self->set_static_type(&impl_decl->impl_type()->static_type());
+
+  // Check and interpret the interface.
+  RETURN_IF_ERROR(TypeCheckExp(&impl_decl->interface(), enclosing_scope));
+  ASSIGN_OR_RETURN(Nonnull<const Value*> written_iface_type,
+                   InterpExp(&impl_decl->interface(), arena_, trace_stream_));
+
+  const auto* iface_type = dyn_cast<InterfaceType>(written_iface_type);
+  if (!iface_type) {
+    return CompilationError(impl_decl->interface().source_loc())
+           << "expected constraint after `as`, found value of type "
+           << *written_iface_type;
+  }
+
+  const auto& iface_decl = iface_type->declaration();
+  impl_decl->set_interface_type(iface_type);
+
   // Bring this impl into the enclosing scope.
   auto impl_id =
       arena_->New<IdentifierExpression>(impl_decl->source_loc(), "impl");
@@ -2061,6 +2100,9 @@ auto TypeChecker::TypeCheckDeclaration(Nonnull<Declaration*> d,
       }
       return Success();
     }
+    case DeclarationKind::SelfDeclaration: {
+      FATAL() << "Unreachable TypeChecker `Self` declaration";
+    }
   }
   return Success();
 }
@@ -2115,6 +2157,10 @@ auto TypeChecker::DeclareDeclaration(Nonnull<Declaration*> d,
       var.set_static_type(declared_type);
       break;
     }
+
+    case DeclarationKind::SelfDeclaration: {
+      FATAL() << "Unreachable TypeChecker declare `Self` declaration";
+    }
   }
   return Success();
 }

+ 1 - 1
explorer/interpreter/type_checker.h

@@ -55,7 +55,7 @@ class TypeChecker {
   // 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.
+  //    directly used in this function but is passed to InterpExp.
   auto TypeCheckExp(Nonnull<Expression*> e, const ImplScope& impl_scope)
       -> ErrorOr<Success>;
 

+ 2 - 0
explorer/syntax/lexer.lpp

@@ -82,6 +82,7 @@ RIGHT_CURLY_BRACE    "}"
 RIGHT_PARENTHESIS    ")"
 RIGHT_SQUARE_BRACKET "]"
 RUN                  "__run"
+SELF                 "Self"
 SEMICOLON            ";"
 SLASH                "/"
 STRING               "String"
@@ -182,6 +183,7 @@ string_literal        \"([^\\\"\n\v\f\r]|\\.)*\"
 {PLUS}                { return SIMPLE_TOKEN(PLUS);                }
 {RETURN}              { return SIMPLE_TOKEN(RETURN);              }
 {RUN}                 { return SIMPLE_TOKEN(RUN);                 }
+{SELF}                { return SIMPLE_TOKEN(SELF);                }
 {SEMICOLON}           { return SIMPLE_TOKEN(SEMICOLON);           }
 {SLASH}               { return SIMPLE_TOKEN(SLASH);               }
 {STRING}              { return SIMPLE_TOKEN(STRING);              }

+ 19 - 2
explorer/syntax/parser.ypp

@@ -98,6 +98,7 @@
 %token <std::string> string_literal
 %type <std::string> designator
 %type <ImplKind> impl_kind
+%type <Nonnull<Expression*>> impl_type
 %type <std::pair<LibraryName, bool>> package_directive
 %type <LibraryName> import_directive
 %type <std::vector<LibraryName>> import_directives
@@ -221,6 +222,7 @@
   RIGHT_PARENTHESIS
   RIGHT_SQUARE_BRACKET
   RUN
+  SELF
   SEMICOLON
   SLASH
   STRING
@@ -303,6 +305,9 @@ primary_expression:
           << "Only i32 is supported for now: " << $1;
       $$ = arena->New<IntTypeLiteral>(context.source_loc());
     }
+| SELF
+    // FIXME: Should we create a new TypeLiteral for `Self`?
+    { $$ = arena->New<IdentifierExpression>(context.source_loc(), "Self"); }
 | STRING
     { $$ = arena->New<StringTypeLiteral>(context.source_loc()); }
 | BOOL
@@ -891,7 +896,11 @@ declaration:
   function_declaration
     { $$ = $1; }
 | CLASS identifier type_params LEFT_CURLY_BRACE declaration_list RIGHT_CURLY_BRACE
-    { $$ = arena->New<ClassDeclaration>(context.source_loc(), $2, $3, $5); }
+    {
+      $$ = arena->New<ClassDeclaration>(
+          context.source_loc(), $2,
+          arena->New<SelfDeclaration>(context.source_loc()), $3, $5);
+    }
 | CHOICE identifier LEFT_CURLY_BRACE alternative_list RIGHT_CURLY_BRACE
     { $$ = arena->New<ChoiceDeclaration>(context.source_loc(), $2, $4); }
 | VAR variable_declaration SEMICOLON
@@ -911,13 +920,16 @@ declaration:
     }
 | INTERFACE identifier type_params LEFT_CURLY_BRACE declaration_list RIGHT_CURLY_BRACE
     {
+      // TODO: Type of `Self` should be the interface being declared, not
+      // `Type`.
       auto ty_ty = arena -> New<TypeTypeLiteral>(context.source_loc());
+      // FIXME: Should this be switched to use a `SelfDeclaration` instead?
       auto self =
           arena -> New<GenericBinding>(context.source_loc(), "Self", ty_ty);
       $$ = arena->New<InterfaceDeclaration>(context.source_loc(), $2, $3, self,
                                             $5);
     }
-| impl_kind IMPL impl_deduced_params expression AS expression LEFT_CURLY_BRACE declaration_list RIGHT_CURLY_BRACE
+| impl_kind IMPL impl_deduced_params impl_type AS expression LEFT_CURLY_BRACE declaration_list RIGHT_CURLY_BRACE
     {
       ErrorOr<ImplDeclaration*> impl = ImplDeclaration::Create(
           arena, context.source_loc(), $1, $4, $6, $3, $8);
@@ -935,6 +947,11 @@ impl_kind:
 | EXTERNAL
     { $$ = Carbon::ImplKind::ExternalImpl; }
 ;
+impl_type:
+  // Self
+    { $$ = arena->New<IdentifierExpression>(context.source_loc(), "Self"); }
+| expression
+;
 declaration_list:
   // Empty
     { $$ = {}; }

+ 20 - 0
explorer/testdata/basic_syntax/fail_var_named_self.carbon

@@ -0,0 +1,20 @@
+// 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;
+
+fn Main() -> i32 {
+  // Error: can't use keyword `Self` as the name of a variable.
+  // TODO: Current error message is unclear, better would be to say
+  // something like: unexpected `Self`, expecting identifier
+  // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/basic_syntax/fail_var_named_self.carbon:[[@LINE+1]]: syntax error, unexpected COLON, expecting EQUAL
+  var Self : i32 = 0;
+  return Self;
+}

+ 27 - 0
explorer/testdata/class/class_function_self.carbon

@@ -0,0 +1,27 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// RUN: %{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: 0
+
+package ExplorerTest api;
+
+class Point {
+  // Allowed: `Self` here means `Point`.
+  fn Origin() -> Self {
+    return {.x = 0, .y = 0};
+  }
+
+  var x: i32;
+  var y: i32;
+}
+
+fn Main() -> i32 {
+  var p: Point = Point.Origin();
+  return p.x;
+}

+ 23 - 0
explorer/testdata/class/fail_class_named_self.carbon

@@ -0,0 +1,23 @@
+// 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;
+
+// Error: Can't use keyword `Self` as the name of a class.
+// CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/class/fail_class_named_self.carbon:[[@LINE+1]]: syntax error, unexpected SELF, expecting identifier
+class Self {
+  var x: i32;
+  var y: i32;
+}
+
+fn Main() -> i32 {
+  var p: Self = {.x = 1, .y = 2};
+  return p.y - p.x - 1;
+}

+ 31 - 0
explorer/testdata/class/method_self.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: 0
+
+package ExplorerTest api;
+
+class Point {
+  fn Origin() -> Point {
+    return {.x = 0, .y = 0};
+  }
+
+  // Allowed: `Self` here means `Point`.
+  fn GetX[me: Self]() -> i32 {
+    return me.x;
+  }
+
+  var x: i32;
+  var y: i32;
+}
+
+fn Main() -> i32 {
+  var p: Point = Point.Origin();
+  return p.GetX();
+}

+ 31 - 0
explorer/testdata/generic_class/fail_no_args.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: %{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 Point(T:! Type) {
+  fn Origin(zero: T) -> Point(T) {
+    return {.x = zero, .y = zero};
+  }
+
+  // Error: wrote `Point` instead of `Point(T)`, `Point` by itself is not a type.
+  // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/generic_class/fail_no_args.carbon:[[@LINE+1]]: Expected a type, but got Point
+  fn GetX[me: Point]() -> T {
+    return me.x;
+  }
+
+  var x: T;
+  var y: T;
+}
+
+fn Main() -> i32 {
+  var p: Point(i32) = Point(i32).Origin(0);
+  return p.GetX();
+}

+ 36 - 0
explorer/testdata/generic_class/fail_self_with_arg.carbon

@@ -0,0 +1,36 @@
+// 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 Point(T:! Type) {
+
+  // Error: wrote `Self(T)` instead of `Self`.
+  // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/generic_class/fail_self_with_arg.carbon:[[@LINE+4]]: in call, expected a function
+  // CHECK: Self(T)
+  // CHECK: not an operator of type typeof(class Point(T = T:! Type))
+  // CHECK-EMPTY:
+  fn Origin(zero: T) -> Self(T) {
+    return {.x = zero, .y = zero};
+  }
+
+  // Error: wrote `Self(T)` instead of `Self`.
+  fn GetX[me: Self(T)]() -> T {
+    return me.x;
+  }
+
+  var x: T;
+  var y: T;
+}
+
+fn Main() -> i32 {
+  var p: Point(i32) = Point(i32).Origin(0);
+  return p.GetX();
+}

+ 48 - 0
explorer/testdata/generic_class/impl_with_argument.carbon

@@ -0,0 +1,48 @@
+// 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: 0
+
+package ExplorerTest api;
+
+interface Vector {
+  fn Zero() -> Self;
+  fn Add[me: Self](b: Self) -> Self;
+  fn Scale[me: Self](v: i32) -> Self;
+}
+
+class Point(T:! Type) {
+  var x: T;
+  var y: T;
+}
+
+// Can implement `Vector` for just `Point(i32)`, not all `Point(T)`.
+external impl Point(i32) as Vector {
+  fn Zero() -> Point(i32) {
+    return {.x = 0, .y = 0};
+  }
+
+  fn Add[me: Point(i32)](b: Point(i32)) -> Point(i32) {
+    return {.x = me.x + b.x, .y = me.y + b.y};
+  }
+
+  fn Scale[me: Point(i32)](v: i32) -> Point(i32) {
+    return {.x = me.x * v, .y = me.y * v};
+  }
+}
+
+fn AddAndScaleGeneric[T:! Vector](a: T, s: i32) -> T {
+  return a.Add(T.Zero()).Scale(s);
+}
+
+fn Main() -> i32 {
+  var a: Point(i32) = {.x = 2, .y = 1};
+  var p: Point(i32) = AddAndScaleGeneric(a, 5);
+  return p.x - 10;
+}

+ 48 - 0
explorer/testdata/generic_class/impl_with_self.carbon

@@ -0,0 +1,48 @@
+// 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: 0
+
+package ExplorerTest api;
+
+interface Vector {
+  fn Zero() -> Self;
+  fn Add[me: Self](b: Self) -> Self;
+  fn Scale[me: Self](v: i32) -> Self;
+}
+
+class Point(T:! Type) {
+  var x: T;
+  var y: T;
+}
+
+external impl Point(i32) as Vector {
+  // Allowed: `Self` means `Point(i32)` here.
+  fn Zero() -> Self {
+    return {.x = 0, .y = 0};
+  }
+
+  fn Add[me: Self](b: Self) -> Self {
+    return {.x = me.x + b.x, .y = me.y + b.y};
+  }
+
+  fn Scale[me: Self](v: i32) -> Self {
+    return {.x = me.x * v, .y = me.y * v};
+  }
+}
+
+fn AddAndScaleGeneric[T:! Vector](a: T, s: i32) -> T {
+  return a.Add(T.Zero()).Scale(s);
+}
+
+fn Main() -> i32 {
+  var a: Point(i32) = {.x = 2, .y = 1};
+  var p: Point(i32) = AddAndScaleGeneric(a, 5);
+  return p.x - 10;
+}

+ 31 - 0
explorer/testdata/generic_class/use_self.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: 0
+
+package ExplorerTest api;
+
+class Point(T:! Type) {
+  // Allowed: `Self` means `Point(T)` here.
+  fn Origin(zero: T) -> Self {
+    return {.x = zero, .y = zero};
+  }
+
+  fn GetX[me: Self]() -> T {
+    return me.x;
+  }
+
+  var x: T;
+  var y: T;
+}
+
+fn Main() -> i32 {
+  var p: Point(i32) = Point(i32).Origin(0);
+  return p.GetX();
+}

+ 19 - 0
explorer/testdata/global_variable/fail_named_self.carbon

@@ -0,0 +1,19 @@
+// 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;
+
+// Error: global variable may not be named with keyword `Self`.
+// CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/global_variable/fail_named_self.carbon:[[@LINE+1]]: syntax error, unexpected SELF, expecting identifier
+var Self: i32 = 0;
+
+fn Main() -> i32 {
+  return Self;
+}

+ 49 - 0
explorer/testdata/impl/param_impl_with_self.carbon

@@ -0,0 +1,49 @@
+// 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: 0
+
+package ExplorerTest api;
+
+interface Number {
+  fn Zero() -> Self;
+  fn Add[me: Self](other: Self) -> Self;
+}
+
+class Point(T:! Number) {
+  var x: T;
+  var y: T;
+}
+
+external impl i32 as Number {
+  fn Zero() -> i32 { return 0; }
+  fn Add[me: i32](other: i32) -> i32 { return me + other; }
+}
+
+external impl forall [U:! Number] Point(U) as Number {
+  // Allowed: `Self` means `Point(U)` here.
+  fn Zero() -> Self { return {.x = U.Zero(), .y = U.Zero() }; }
+  fn Add[me: Self](other: Self) -> Self {
+    return {.x = me.x.Add(other.x), .y = me.y.Add(other.y)};
+  }
+}
+
+fn Sum[E:! Number](x: E, y: E) -> E {
+  var total: E = E.Zero();
+  total = total.Add(x);
+  total = total.Add(y);
+  return total;
+}
+
+fn Main() -> i32 {
+  var p: Point(i32) = {.x = 1, .y = 2};
+  var q: Point(i32) = {.x = 4, .y = 3};
+  var r: Point(i32) = Sum(p, q);
+  return r.x - r.y;
+}

+ 45 - 0
explorer/testdata/interface/external_impl_use_self.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: %{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: 0
+
+package ExplorerTest 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 {
+  // Allowed: `Self` means `Point` here.
+  fn Add[me: Self](b: Self) -> Self {
+    return {.x = me.x + b.x, .y = me.y + b.y};
+  }
+  fn Scale[me: Self](v: i32) -> Self {
+    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;
+}

+ 45 - 0
explorer/testdata/interface/fail_external_impl_omit_self.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: %{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 Vector {
+  fn Add[me: Self](b: Self) -> Self;
+  fn Scale[me: Self](v: i32) -> Self;
+}
+
+class Point {
+  var x: i32;
+  var y: i32;
+}
+
+// Error: need to specify which type implementing `Vector` for.
+// CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/interface/fail_external_impl_omit_self.carbon:[[@LINE+1]]: could not resolve 'Self'
+external impl 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;
+}

+ 44 - 0
explorer/testdata/interface/fail_external_impl_self.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: %{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 Vector {
+  fn Add[me: Self](b: Self) -> Self;
+  fn Scale[me: Self](v: i32) -> Self;
+}
+
+class Point {
+  var x: i32;
+  var y: i32;
+}
+
+// CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/interface/fail_external_impl_self.carbon:[[@LINE+1]]: could not resolve 'Self'
+external impl Self 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;
+}

+ 39 - 0
explorer/testdata/interface/impl_self_interface_parameter.carbon

@@ -0,0 +1,39 @@
+// 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: 0
+
+package ExplorerTest api;
+
+interface AddWith(T:! Type) {
+  fn Op[me: Self](b: T) -> Self;
+}
+
+class Point {
+  var x: i32;
+  var y: i32;
+}
+
+// Allowed: `Self` means `Point` after `as`
+impl Point as AddWith(Self) {
+  fn Op[me: Point](b: Point) -> Point {
+    return {.x = me.x + b.x, .y = me.y + b.y};
+  }
+}
+
+fn DoAddGeneric[T:! Type, U:! AddWith(T)](a: U, b: T) -> U {
+  return a.Op(b);
+}
+
+fn Main() -> i32 {
+  var a: Point = {.x = 2, .y = 1};
+  var b: Point = {.x = 4, .y = 2};
+  var p: Point = DoAddGeneric(a, b);
+  return p.x - p.y - 3;
+}

+ 46 - 0
explorer/testdata/interface/omit_self.carbon

@@ -0,0 +1,46 @@
+// 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: 0
+
+package ExplorerTest api;
+
+interface Vector {
+  fn Zero() -> Self;
+  fn Add[me: Self](b: Self) -> Self;
+  fn Scale[me: Self](v: i32) -> Self;
+}
+
+class Point {
+  var x: i32;
+  var y: i32;
+  // Allowed: means the same as `impl Self as Vector`
+  //          or `impl Point as Vector`.
+  impl as Vector {
+    fn Zero() -> Point {
+      return {.x = 0, .y = 0};
+    }
+    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, s: i32) -> T {
+  return a.Add(T.Zero()).Scale(s);
+}
+
+fn Main() -> i32 {
+  var a: Point = {.x = 2, .y = 1};
+  var p: Point = AddAndScaleGeneric(a, 5);
+  return p.x - 10;
+}

+ 45 - 0
explorer/testdata/interface/with_self.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: %{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: 0
+
+package ExplorerTest api;
+
+interface Vector {
+  fn Zero() -> Self;
+  fn Add[me: Self](b: Self) -> Self;
+  fn Scale[me: Self](v: i32) -> Self;
+}
+
+class Point {
+  var x: i32;
+  var y: i32;
+  // Allowed: `Self` means `Point` here.
+  impl Self as Vector {
+    fn Zero() -> Self {
+      return {.x = 0, .y = 0};
+    }
+    fn Add[me: Self](b: Self) -> Self {
+      return {.x = me.x + b.x, .y = me.y + b.y};
+    }
+    fn Scale[me: Self](v: i32) -> Self {
+      return {.x = me.x * v, .y = me.y * v};
+    }
+  }
+}
+
+fn AddAndScaleGeneric[T:! Vector](a: T, s: i32) -> T {
+  return a.Add(T.Zero()).Scale(s);
+}
+
+fn Main() -> i32 {
+  var a: Point = {.x = 2, .y = 1};
+  var p: Point = AddAndScaleGeneric(a, 5);
+  return p.x - 10;
+}

+ 19 - 0
explorer/testdata/let/fail_global_named_self.carbon

@@ -0,0 +1,19 @@
+// 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;
+
+// Error: Can't use keyword `Self` as the name of a global.
+// CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/let/fail_global_named_self.carbon:[[@LINE+1]]: syntax error, unexpected SELF, expecting identifier
+let Self: i32 = 10;
+
+fn Main() -> i32 {
+  return 0;
+}

+ 18 - 0
explorer/testdata/let/fail_local_named_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;
+
+fn Main() -> i32 {
+  // Error: Can't use keyword `Self` as the name of a local.
+  // CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/let/fail_local_named_self.carbon:[[@LINE+1]]: syntax error, unexpected COLON, expecting EQUAL
+  let Self: auto = 10;
+  return 0;
+}