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

Implement support for `extends` and `impl as` declarations in interfaces (#2279)

Richard Smith 3 лет назад
Родитель
Сommit
7f7b9a4086

+ 11 - 0
common/fuzzing/carbon.proto

@@ -378,6 +378,15 @@ message LetDeclaration {
   // `let` declarations in general.
 }
 
+message InterfaceExtendsDeclaration {
+  optional Expression base = 1;
+}
+
+message InterfaceImplDeclaration {
+  optional Expression impl_type = 1;
+  optional Expression constraint = 2;
+}
+
 message InterfaceDeclaration {
   optional string name = 1;
   repeated Declaration members = 2;
@@ -429,6 +438,8 @@ message Declaration {
     MixinDeclaration mixin = 9;
     MixDeclaration mix = 10;
     DestructorDeclaration destructor = 11;
+    InterfaceExtendsDeclaration interface_extends = 12;
+    InterfaceImplDeclaration interface_impl = 13;
   }
 }
 

+ 18 - 0
common/fuzzing/proto_to_carbon.cpp

@@ -829,6 +829,24 @@ static auto DeclarationToCarbon(const Fuzzing::Declaration& declaration,
       break;
     }
 
+    case Fuzzing::Declaration::kInterfaceExtends: {
+      const auto& extends = declaration.interface_extends();
+      out << "extends ";
+      ExpressionToCarbon(extends.base(), out);
+      out << ";";
+      break;
+    }
+
+    case Fuzzing::Declaration::kInterfaceImpl: {
+      const auto& impl = declaration.interface_impl();
+      out << "impl ";
+      ExpressionToCarbon(impl.impl_type(), out);
+      out << " as ";
+      ExpressionToCarbon(impl.constraint(), out);
+      out << ";";
+      break;
+    }
+
     case Fuzzing::Declaration::kInterface: {
       const auto& interface = declaration.interface();
       out << "interface ";

+ 2 - 0
explorer/ast/ast_rtti.txt

@@ -23,6 +23,8 @@ abstract class Declaration : AstNode;
   class ChoiceDeclaration : Declaration;
   class VariableDeclaration : Declaration;
   class InterfaceDeclaration : Declaration;
+  class InterfaceExtendsDeclaration : Declaration;
+  class InterfaceImplDeclaration : Declaration;
   class AssociatedConstantDeclaration : Declaration;
   class ImplDeclaration : Declaration;
   class AliasDeclaration : Declaration;

+ 18 - 1
explorer/ast/declaration.cpp

@@ -91,10 +91,13 @@ void Declaration::Print(llvm::raw_ostream& out) const {
       break;
     }
 
-    case DeclarationKind::AssociatedConstantDeclaration:
+    case DeclarationKind::InterfaceExtendsDeclaration:
+    case DeclarationKind::InterfaceImplDeclaration:
+    case DeclarationKind::AssociatedConstantDeclaration: {
       PrintID(out);
       out << ";\n";
       break;
+    }
 
     case DeclarationKind::SelfDeclaration: {
       out << "Self";
@@ -165,6 +168,18 @@ void Declaration::PrintID(llvm::raw_ostream& out) const {
       break;
     }
 
+    case DeclarationKind::InterfaceExtendsDeclaration: {
+      const auto& extends = cast<InterfaceExtendsDeclaration>(*this);
+      out << "extends " << *extends.base();
+      break;
+    }
+
+    case DeclarationKind::InterfaceImplDeclaration: {
+      const auto& impl = cast<InterfaceImplDeclaration>(*this);
+      out << "impl " << *impl.impl_type() << " as " << *impl.constraint();
+      break;
+    }
+
     case DeclarationKind::AssociatedConstantDeclaration: {
       const auto& let = cast<AssociatedConstantDeclaration>(*this);
       out << "let " << let.binding();
@@ -207,6 +222,8 @@ auto GetName(const Declaration& declaration)
       return cast<VariableDeclaration>(declaration).binding().name();
     case DeclarationKind::AssociatedConstantDeclaration:
       return cast<AssociatedConstantDeclaration>(declaration).binding().name();
+    case DeclarationKind::InterfaceExtendsDeclaration:
+    case DeclarationKind::InterfaceImplDeclaration:
     case DeclarationKind::ImplDeclaration:
       return std::nullopt;
     case DeclarationKind::SelfDeclaration:

+ 44 - 0
explorer/ast/declaration.h

@@ -523,6 +523,50 @@ class InterfaceDeclaration : public Declaration {
   std::optional<Nonnull<const ConstraintType*>> constraint_type_;
 };
 
+// An `extends` declaration in an interface.
+class InterfaceExtendsDeclaration : public Declaration {
+ public:
+  InterfaceExtendsDeclaration(SourceLocation source_loc,
+                              Nonnull<Expression*> base)
+      : Declaration(AstNodeKind::InterfaceExtendsDeclaration, source_loc),
+        base_(base) {}
+
+  static auto classof(const AstNode* node) -> bool {
+    return InheritsFromInterfaceExtendsDeclaration(node->kind());
+  }
+
+  auto base() const -> const Expression* { return base_; }
+  auto base() -> Expression* { return base_; }
+
+ private:
+  Nonnull<Expression*> base_;
+};
+
+// An `impl ... as` declaration in an interface.
+class InterfaceImplDeclaration : public Declaration {
+ public:
+  InterfaceImplDeclaration(SourceLocation source_loc,
+                           Nonnull<Expression*> impl_type,
+                           Nonnull<Expression*> constraint)
+      : Declaration(AstNodeKind::InterfaceImplDeclaration, source_loc),
+        impl_type_(impl_type),
+        constraint_(constraint) {}
+
+  static auto classof(const AstNode* node) -> bool {
+    return InheritsFromInterfaceImplDeclaration(node->kind());
+  }
+
+  auto impl_type() const -> const Expression* { return impl_type_; }
+  auto impl_type() -> Expression* { return impl_type_; }
+
+  auto constraint() const -> const Expression* { return constraint_; }
+  auto constraint() -> Expression* { return constraint_; }
+
+ private:
+  Nonnull<Expression*> impl_type_;
+  Nonnull<Expression*> constraint_;
+};
+
 class AssociatedConstantDeclaration : public Declaration {
  public:
   AssociatedConstantDeclaration(SourceLocation source_loc,

+ 1 - 6
explorer/data/prelude.carbon

@@ -15,12 +15,7 @@ interface As(T:! Type) {
 
 // Implicitly convert `Self` to `T`.
 interface ImplicitAs(T:! Type) {
-  fn Convert[me: Self]() -> T;
-}
-
-// TODO: ImplicitAs(T) should extend As(T).
-impl forall [T:! Type, U:! ImplicitAs(T)] U as As(T) {
-  fn Convert[me: Self]() -> T { return me.Convert(); }
+  extends As(T);
 }
 
 // Every type implicitly converts to itself.

+ 15 - 0
explorer/fuzzing/ast_to_proto.cpp

@@ -704,6 +704,21 @@ static auto DeclarationToProto(const Declaration& declaration)
       break;
     }
 
+    case DeclarationKind::InterfaceExtendsDeclaration: {
+      const auto& extends = cast<InterfaceExtendsDeclaration>(declaration);
+      auto* extends_proto = declaration_proto.mutable_interface_extends();
+      *extends_proto->mutable_base() = ExpressionToProto(*extends.base());
+      break;
+    }
+
+    case DeclarationKind::InterfaceImplDeclaration: {
+      const auto& impl = cast<InterfaceImplDeclaration>(declaration);
+      auto* impl_proto = declaration_proto.mutable_interface_impl();
+      *impl_proto->mutable_impl_type() = ExpressionToProto(*impl.impl_type());
+      *impl_proto->mutable_constraint() = ExpressionToProto(*impl.constraint());
+      break;
+    }
+
     case DeclarationKind::AssociatedConstantDeclaration: {
       const auto& assoc = cast<AssociatedConstantDeclaration>(declaration);
       auto* let_proto = declaration_proto.mutable_let();

+ 2 - 0
explorer/interpreter/interpreter.cpp

@@ -1950,6 +1950,8 @@ auto Interpreter::StepDeclaration() -> ErrorOr<Success> {
     case DeclarationKind::MixDeclaration:
     case DeclarationKind::ChoiceDeclaration:
     case DeclarationKind::InterfaceDeclaration:
+    case DeclarationKind::InterfaceExtendsDeclaration:
+    case DeclarationKind::InterfaceImplDeclaration:
     case DeclarationKind::AssociatedConstantDeclaration:
     case DeclarationKind::ImplDeclaration:
     case DeclarationKind::SelfDeclaration:

+ 2 - 0
explorer/interpreter/resolve_control_flow.cpp

@@ -179,6 +179,8 @@ auto ResolveControlFlow(Nonnull<Declaration*> declaration) -> ErrorOr<Success> {
     }
     case DeclarationKind::ChoiceDeclaration:
     case DeclarationKind::VariableDeclaration:
+    case DeclarationKind::InterfaceExtendsDeclaration:
+    case DeclarationKind::InterfaceImplDeclaration:
     case DeclarationKind::AssociatedConstantDeclaration:
     case DeclarationKind::SelfDeclaration:
     case DeclarationKind::AliasDeclaration:

+ 18 - 8
explorer/interpreter/resolve_names.cpp

@@ -29,10 +29,6 @@ static auto AddExposedNames(const Declaration& declaration,
                               StaticScope::NameStatus::KnownButNotDeclared));
       break;
     }
-    case DeclarationKind::ImplDeclaration: {
-      // Nothing to do here
-      break;
-    }
     case DeclarationKind::DestructorDeclaration: {
       // TODO: Remove this code. With this code, it is possible to create not
       // useful carbon code.
@@ -62,10 +58,6 @@ static auto AddExposedNames(const Declaration& declaration,
                               StaticScope::NameStatus::KnownButNotDeclared));
       break;
     }
-    case DeclarationKind::MixDeclaration: {
-      // Nothing to do here
-      break;
-    }
     case DeclarationKind::ChoiceDeclaration: {
       auto& choice = cast<ChoiceDeclaration>(declaration);
       CARBON_RETURN_IF_ERROR(
@@ -101,6 +93,13 @@ static auto AddExposedNames(const Declaration& declaration,
           alias.name(), &alias, StaticScope::NameStatus::KnownButNotDeclared));
       break;
     }
+    case DeclarationKind::ImplDeclaration:
+    case DeclarationKind::MixDeclaration:
+    case DeclarationKind::InterfaceExtendsDeclaration:
+    case DeclarationKind::InterfaceImplDeclaration: {
+      // These declarations don't have a name to expose.
+      break;
+    }
   }
   return Success();
 }
@@ -642,6 +641,17 @@ static auto ResolveNames(Declaration& declaration, StaticScope& enclosing_scope,
       }
       break;
     }
+    case DeclarationKind::InterfaceExtendsDeclaration: {
+      auto& extends = cast<InterfaceExtendsDeclaration>(declaration);
+      CARBON_RETURN_IF_ERROR(ResolveNames(*extends.base(), enclosing_scope));
+      break;
+    }
+    case DeclarationKind::InterfaceImplDeclaration: {
+      auto& impl = cast<InterfaceImplDeclaration>(declaration);
+      CARBON_RETURN_IF_ERROR(ResolveNames(*impl.impl_type(), enclosing_scope));
+      CARBON_RETURN_IF_ERROR(ResolveNames(*impl.constraint(), enclosing_scope));
+      break;
+    }
     case DeclarationKind::AssociatedConstantDeclaration: {
       auto& let = cast<AssociatedConstantDeclaration>(declaration);
       CARBON_RETURN_IF_ERROR(ResolveNames(let.binding(), enclosing_scope));

+ 2 - 0
explorer/interpreter/resolve_unformed.cpp

@@ -300,6 +300,8 @@ static auto ResolveUnformed(Nonnull<const Declaration*> declaration)
     case DeclarationKind::ImplDeclaration:
     case DeclarationKind::ChoiceDeclaration:
     case DeclarationKind::VariableDeclaration:
+    case DeclarationKind::InterfaceExtendsDeclaration:
+    case DeclarationKind::InterfaceImplDeclaration:
     case DeclarationKind::AssociatedConstantDeclaration:
     case DeclarationKind::SelfDeclaration:
     case DeclarationKind::AliasDeclaration:

+ 107 - 36
explorer/interpreter/type_checker.cpp

@@ -1788,28 +1788,18 @@ auto TypeChecker::DeduceCallBindings(
   return Success();
 }
 
-struct ConstraintLookupResult {
-  Nonnull<const InterfaceType*> interface;
-  Nonnull<const Declaration*> member;
-};
-
-/// Look up a member name in a constraint, which might be a single interface or
-/// a compound constraint.
-static auto LookupInConstraint(SourceLocation source_loc,
-                               std::string_view lookup_kind,
-                               Nonnull<const Value*> type,
-                               std::string_view member_name)
+auto TypeChecker::LookupInConstraint(SourceLocation source_loc,
+                                     std::string_view lookup_kind,
+                                     Nonnull<const Value*> type,
+                                     std::string_view member_name)
     -> ErrorOr<ConstraintLookupResult> {
   // Find the set of lookup contexts.
   llvm::ArrayRef<ConstraintType::LookupContext> lookup_contexts;
-  ConstraintType::LookupContext interface_context[1];
   if (const auto* iface_type = dyn_cast<InterfaceType>(type)) {
-    // For an interface, look into that interface alone.
-    // TODO: Also look into any interfaces extended by it.
-    // TODO: Maybe just convert to a constraint type to reduce duplication?
-    interface_context[0].context = iface_type;
-    lookup_contexts = interface_context;
-  } else if (const auto* constraint_type = dyn_cast<ConstraintType>(type)) {
+    CARBON_ASSIGN_OR_RETURN(type,
+                            MakeConstraintForInterface(source_loc, iface_type));
+  }
+  if (const auto* constraint_type = dyn_cast<ConstraintType>(type)) {
     // For a constraint, look in all of its lookup contexts.
     lookup_contexts = constraint_type->lookup_contexts();
   } else {
@@ -4232,30 +4222,90 @@ auto TypeChecker::DeclareInterfaceDeclaration(
   for (Nonnull<Declaration*> m : iface_decl->members()) {
     CARBON_RETURN_IF_ERROR(DeclareDeclaration(m, iface_scope_info));
 
-    if (auto* assoc = dyn_cast<AssociatedConstantDeclaration>(m)) {
-      auto* assoc_value = arena_->New<AssociatedConstant>(
-          &iface_decl->self()->value(), iface_type, assoc, impl_witness);
-      assoc->binding().set_symbolic_identity(assoc_value);
-
-      // The type specified for the associated constant becomes a constraint
-      // for the interface: `let X:! Interface` adds a `Self.X is Interface`
-      // constraint that `impl`s must satisfy and users of the interface can
-      // rely on.
-      Nonnull<const Value*> constraint = &assoc->static_type();
-      if (auto* interface_type = dyn_cast<InterfaceType>(constraint)) {
-        CARBON_ASSIGN_OR_RETURN(
-            constraint,
-            MakeConstraintForInterface(assoc->source_loc(), interface_type));
+    // TODO: This should probably live in `DeclareDeclaration`, but it needs
+    // to update state that's not available from there.
+    switch (m->kind()) {
+      case DeclarationKind::InterfaceExtendsDeclaration: {
+        // For an `extends C;` declaration, add `Self is C` to our constraint.
+        auto* extends = cast<InterfaceExtendsDeclaration>(m);
+        CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> base,
+                                TypeCheckTypeExp(extends->base(), iface_scope));
+        auto* constraint_type = dyn_cast<ConstraintType>(base);
+        if (auto* interface_type = dyn_cast<InterfaceType>(base)) {
+          CARBON_ASSIGN_OR_RETURN(
+              constraint_type, MakeConstraintForInterface(extends->source_loc(),
+                                                          interface_type));
+        }
+        if (!constraint_type) {
+          return ProgramError(extends->source_loc())
+                 << "an interface can only extend a constraint, found "
+                 << *base;
+        }
+        CARBON_RETURN_IF_ERROR(builder.AddAndSubstitute(
+            *this, constraint_type, builder.GetSelfType(),
+            builder.GetSelfWitness(), Bindings(),
+            /*add_lookup_contexts=*/true));
+        break;
       }
-      if (auto* constraint_type = dyn_cast<ConstraintType>(constraint)) {
+
+      case DeclarationKind::InterfaceImplDeclaration: {
+        // For an `impl X as Y;` declaration, add `X is Y` to our constraint.
+        auto* impl = cast<InterfaceImplDeclaration>(m);
+        CARBON_ASSIGN_OR_RETURN(
+            Nonnull<const Value*> impl_type,
+            TypeCheckTypeExp(impl->impl_type(), iface_scope));
+        CARBON_ASSIGN_OR_RETURN(
+            Nonnull<const Value*> constraint,
+            TypeCheckTypeExp(impl->constraint(), iface_scope));
+        auto* constraint_type = dyn_cast<ConstraintType>(constraint);
+        if (auto* interface_type = dyn_cast<InterfaceType>(constraint)) {
+          CARBON_ASSIGN_OR_RETURN(
+              constraint_type,
+              MakeConstraintForInterface(impl->source_loc(), interface_type));
+        }
+        if (!constraint_type) {
+          return ProgramError(impl->source_loc())
+                 << "expected a constraint after `as`, found " << *constraint;
+        }
         CARBON_RETURN_IF_ERROR(
-            builder.AddAndSubstitute(*this, constraint_type, assoc_value,
+            builder.AddAndSubstitute(*this, constraint_type, impl_type,
                                      builder.GetSelfWitness(), Bindings(),
                                      /*add_lookup_contexts=*/false));
-        // Add any new impl constraints to the scope.
-        builder.BringImplsIntoScope(*this, &iface_scope, &impl_tracker);
+        break;
+      }
+
+      case DeclarationKind::AssociatedConstantDeclaration: {
+        auto* assoc = cast<AssociatedConstantDeclaration>(m);
+        auto* assoc_value = arena_->New<AssociatedConstant>(
+            &iface_decl->self()->value(), iface_type, assoc, impl_witness);
+        assoc->binding().set_symbolic_identity(assoc_value);
+
+        // The type specified for the associated constant becomes a
+        // constraint for the interface: `let X:! Interface` adds a `Self.X
+        // is Interface` constraint that `impl`s must satisfy and users of
+        // the interface can rely on.
+        Nonnull<const Value*> constraint = &assoc->static_type();
+        if (auto* interface_type = dyn_cast<InterfaceType>(constraint)) {
+          CARBON_ASSIGN_OR_RETURN(
+              constraint,
+              MakeConstraintForInterface(assoc->source_loc(), interface_type));
+        }
+        if (auto* constraint_type = dyn_cast<ConstraintType>(constraint)) {
+          CARBON_RETURN_IF_ERROR(
+              builder.AddAndSubstitute(*this, constraint_type, assoc_value,
+                                       builder.GetSelfWitness(), Bindings(),
+                                       /*add_lookup_contexts=*/false));
+        }
+        break;
+      }
+
+      default: {
+        break;
       }
     }
+
+    // Add any new impl constraints to the scope.
+    builder.BringImplsIntoScope(*this, &iface_scope, &impl_tracker);
   }
 
   iface_decl->set_constraint_type(std::move(builder).Build(arena_));
@@ -4365,6 +4415,9 @@ auto TypeChecker::CheckImplIsComplete(Nonnull<const InterfaceType*> iface_type,
                << "implementation provides multiple values for " << *expected
                << ": " << **found_value << " and " << **second_value;
       }
+    } else if (isa<InterfaceImplDeclaration, InterfaceExtendsDeclaration>(m)) {
+      // These get translated into constraints so there's nothing we need to
+      // check here.
     } else {
       // Every member function must be declared.
       std::optional<std::string_view> mem_name = GetName(*m);
@@ -4820,6 +4873,14 @@ auto TypeChecker::TypeCheckDeclaration(
       }
       return Success();
     }
+    case DeclarationKind::InterfaceExtendsDeclaration: {
+      // Checked in DeclareInterfaceDeclaration.
+      return Success();
+    }
+    case DeclarationKind::InterfaceImplDeclaration: {
+      // Checked in DeclareInterfaceDeclaration.
+      return Success();
+    }
     case DeclarationKind::AssociatedConstantDeclaration:
       return Success();
     case DeclarationKind::SelfDeclaration: {
@@ -4901,6 +4962,16 @@ auto TypeChecker::DeclareDeclaration(Nonnull<Declaration*> d,
       break;
     }
 
+    case DeclarationKind::InterfaceExtendsDeclaration: {
+      // The semantic effects are handled by DeclareInterfaceDeclaration.
+      break;
+    }
+
+    case DeclarationKind::InterfaceImplDeclaration: {
+      // The semantic effects are handled by DeclareInterfaceDeclaration.
+      break;
+    }
+
     case DeclarationKind::AssociatedConstantDeclaration: {
       auto& let = cast<AssociatedConstantDeclaration>(*d);
       CARBON_ASSIGN_OR_RETURN(

+ 14 - 0
explorer/interpreter/type_checker.h

@@ -111,6 +111,12 @@ class TypeChecker {
     std::vector<Nonnull<const GenericBinding*>> bindings;
   };
 
+  // Result from a lookup in a constraint.
+  struct ConstraintLookupResult {
+    Nonnull<const InterfaceType*> interface;
+    Nonnull<const Declaration*> member;
+  };
+
   // Traverses the AST rooted at `e`, populating the static_type() of all nodes
   // and ensuring they follow Carbon's typing rules.
   //
@@ -415,6 +421,14 @@ class TypeChecker {
   auto GetTypeForAssociatedConstant(
       Nonnull<const AssociatedConstant*> assoc) const -> Nonnull<const Value*>;
 
+  // Look up a member name in a constraint, which might be a single interface or
+  // a compound constraint.
+  auto LookupInConstraint(SourceLocation source_loc,
+                          std::string_view lookup_kind,
+                          Nonnull<const Value*> type,
+                          std::string_view member_name)
+      -> ErrorOr<ConstraintLookupResult>;
+
   // Given `type.(interface.member)`, look for a rewrite in the declared type
   // of `type`.
   auto LookupRewriteInTypeOf(Nonnull<const Value*> type,

+ 12 - 0
explorer/syntax/parser.ypp

@@ -1271,6 +1271,18 @@ interface_body:
       $$.push_back(
           arena->New<AssociatedConstantDeclaration>(context.source_loc(), $3));
     }
+| interface_body EXTENDS expression SEMICOLON
+    {
+      $$ = $1;
+      $$.push_back(arena->New<InterfaceExtendsDeclaration>(context.source_loc(),
+                                                           $3));
+    }
+| interface_body IMPL impl_type AS type_or_where_expression SEMICOLON
+    {
+      $$ = $1;
+      $$.push_back(arena->New<InterfaceImplDeclaration>(context.source_loc(),
+                                                        $3, $5));
+    }
 ;
 impl_body:
   // Empty

+ 36 - 0
explorer/testdata/interface/extends.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: %{explorer} %s 2>&1 | %{FileCheck-strict} %s
+// RUN: %{explorer-trace} %s 2>&1 | %{FileCheck-allow-unmatched} %s
+// AUTOUPDATE: %{explorer} %s
+// CHECK:Carrot.G
+// CHECK:result: 5
+
+package ExplorerTest api;
+
+interface Apple(T:! Type) {
+  fn F[me: Self]() -> T;
+}
+
+interface Banana {
+  extends Apple(i32);
+  fn G[me: Self]();
+}
+
+class Carrot {
+  var n: i32;
+}
+
+// This impl also provides an `impl Carrot as Apple(i32)`.
+external impl Carrot as Banana {
+  fn F[me: Self]() -> i32 { return me.n; }
+  fn G[me: Self]() { Print("Carrot.G"); }
+}
+
+fn Main() -> i32 {
+  var c: Carrot = {.n = 5};
+  c.(Banana.G)();
+  return c.(Apple(i32).F)();
+}

+ 1 - 1
explorer/testdata/interface/fail_bad_member_kind.carbon

@@ -9,7 +9,7 @@
 package ExplorerTest api;
 
 interface Bad {
-  // CHECK:SYNTAX ERROR: {{.*}}/explorer/testdata/interface/fail_bad_member_kind.carbon:[[@LINE+1]]: syntax error, unexpected VAR, expecting FN or LET or RIGHT_CURLY_BRACE
+  // CHECK:SYNTAX ERROR: {{.*}}/explorer/testdata/interface/fail_bad_member_kind.carbon:[[@LINE+1]]: syntax error, unexpected VAR
   var V: i32;
 }
 

+ 18 - 0
explorer/testdata/interface/fail_extends_not_constraint.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 | %{FileCheck-strict} %s
+// RUN: %{not} %{explorer-trace} %s 2>&1 | %{FileCheck-allow-unmatched} %s
+// AUTOUPDATE: %{explorer} %s
+
+package ExplorerTest api;
+
+interface Vector {
+  // CHECK:COMPILATION ERROR: {{.*}}/explorer/testdata/interface/fail_extends_not_constraint.carbon:[[@LINE+1]]: an interface can only extend a constraint, found i32
+  extends i32;
+}
+
+fn Main() -> i32 {
+  return 0;
+}

+ 18 - 0
explorer/testdata/interface/fail_impl_as_not_constraint.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 | %{FileCheck-strict} %s
+// RUN: %{not} %{explorer-trace} %s 2>&1 | %{FileCheck-allow-unmatched} %s
+// AUTOUPDATE: %{explorer} %s
+
+package ExplorerTest api;
+
+interface Vector {
+  // CHECK:COMPILATION ERROR: {{.*}}/explorer/testdata/interface/fail_impl_as_not_constraint.carbon:[[@LINE+1]]: expected a constraint after `as`, found i32
+  impl Self as i32;
+}
+
+fn Main() -> i32 {
+  return 0;
+}

+ 20 - 0
explorer/testdata/interface/fail_impl_as_not_type.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 | %{FileCheck-strict} %s
+// RUN: %{not} %{explorer-trace} %s 2>&1 | %{FileCheck-allow-unmatched} %s
+// AUTOUPDATE: %{explorer} %s
+
+package ExplorerTest api;
+
+interface A {}
+
+interface B {
+  // CHECK:COMPILATION ERROR: {{.*}}/explorer/testdata/interface/fail_impl_as_not_type.carbon:[[@LINE+1]]: Expected a type, but got 5
+  impl 5 as A;
+}
+
+fn Main() -> i32 {
+  return 0;
+}

+ 42 - 0
explorer/testdata/interface/impl_as.carbon

@@ -0,0 +1,42 @@
+// 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 | %{FileCheck-strict} %s
+// RUN: %{explorer-trace} %s 2>&1 | %{FileCheck-allow-unmatched} %s
+// AUTOUPDATE: %{explorer} %s
+// CHECK:Carrot.G
+// CHECK:result: 5
+
+package ExplorerTest api;
+
+interface Apple(T:! Type) {
+  fn F[me: Self]() -> T;
+}
+
+interface Banana {
+  impl as Apple(i32);
+  fn G[me: Self]();
+}
+
+class Carrot {
+  var n: i32;
+}
+
+external impl Carrot as Apple(i32) {
+  fn F[me: Self]() -> i32 { return me.n; }
+}
+
+external impl Carrot as Banana {
+  fn G[me: Self]() { Print("Carrot.G"); }
+}
+
+fn H[T:! Banana](x: T) -> i32 {
+  x.G();
+  return x.(Apple(i32).F)();
+}
+
+fn Main() -> i32 {
+  var c: Carrot = {.n = 5};
+  return H(c);
+}

+ 30 - 0
explorer/testdata/interface/impl_as_not_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 | %{FileCheck-strict} %s
+// RUN: %{explorer-trace} %s 2>&1 | %{FileCheck-allow-unmatched} %s
+// AUTOUPDATE: %{explorer} %s
+// CHECK:result: 5
+
+package ExplorerTest api;
+
+interface ConvertsFromInt {
+  impl i32 as ImplicitAs(Self);
+}
+
+fn ConvertIntTo(T:! ConvertsFromInt, n: i32) -> T {
+  return n;
+}
+
+class IntHolder {
+  var n: i32;
+}
+external impl i32 as ImplicitAs(IntHolder) {
+  fn Convert[me: Self]() -> IntHolder { return {.n = me}; }
+}
+external impl IntHolder as ConvertsFromInt {}
+
+fn Main() -> i32 {
+  return ConvertIntTo(IntHolder, 5).n;
+}