diff --git a/common/fuzzing/carbon.proto b/common/fuzzing/carbon.proto index 019e07938bb1..280ca80fc406 100644 --- a/common/fuzzing/carbon.proto +++ b/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; } } diff --git a/common/fuzzing/proto_to_carbon.cpp b/common/fuzzing/proto_to_carbon.cpp index 9360ea260d48..732c60c2386f 100644 --- a/common/fuzzing/proto_to_carbon.cpp +++ b/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 "; diff --git a/explorer/ast/ast_rtti.txt b/explorer/ast/ast_rtti.txt index e78df7302b97..646550d382f1 100644 --- a/explorer/ast/ast_rtti.txt +++ b/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; diff --git a/explorer/ast/declaration.cpp b/explorer/ast/declaration.cpp index 3ae2f90c2601..cf5d16334c78 100644 --- a/explorer/ast/declaration.cpp +++ b/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(*this); + out << "extends " << *extends.base(); + break; + } + + case DeclarationKind::InterfaceImplDeclaration: { + const auto& impl = cast(*this); + out << "impl " << *impl.impl_type() << " as " << *impl.constraint(); + break; + } + case DeclarationKind::AssociatedConstantDeclaration: { const auto& let = cast(*this); out << "let " << let.binding(); @@ -207,6 +222,8 @@ auto GetName(const Declaration& declaration) return cast(declaration).binding().name(); case DeclarationKind::AssociatedConstantDeclaration: return cast(declaration).binding().name(); + case DeclarationKind::InterfaceExtendsDeclaration: + case DeclarationKind::InterfaceImplDeclaration: case DeclarationKind::ImplDeclaration: return std::nullopt; case DeclarationKind::SelfDeclaration: diff --git a/explorer/ast/declaration.h b/explorer/ast/declaration.h index 5955c94aed8b..3f909e293017 100644 --- a/explorer/ast/declaration.h +++ b/explorer/ast/declaration.h @@ -523,6 +523,50 @@ class InterfaceDeclaration : public Declaration { std::optional> constraint_type_; }; +// An `extends` declaration in an interface. +class InterfaceExtendsDeclaration : public Declaration { + public: + InterfaceExtendsDeclaration(SourceLocation source_loc, + Nonnull 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 base_; +}; + +// An `impl ... as` declaration in an interface. +class InterfaceImplDeclaration : public Declaration { + public: + InterfaceImplDeclaration(SourceLocation source_loc, + Nonnull impl_type, + Nonnull 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 impl_type_; + Nonnull constraint_; +}; + class AssociatedConstantDeclaration : public Declaration { public: AssociatedConstantDeclaration(SourceLocation source_loc, diff --git a/explorer/data/prelude.carbon b/explorer/data/prelude.carbon index dbb26ef641a4..a8b3be95deab 100644 --- a/explorer/data/prelude.carbon +++ b/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. diff --git a/explorer/fuzzing/ast_to_proto.cpp b/explorer/fuzzing/ast_to_proto.cpp index 89795d82920b..9a1e311063d4 100644 --- a/explorer/fuzzing/ast_to_proto.cpp +++ b/explorer/fuzzing/ast_to_proto.cpp @@ -704,6 +704,21 @@ static auto DeclarationToProto(const Declaration& declaration) break; } + case DeclarationKind::InterfaceExtendsDeclaration: { + const auto& extends = cast(declaration); + auto* extends_proto = declaration_proto.mutable_interface_extends(); + *extends_proto->mutable_base() = ExpressionToProto(*extends.base()); + break; + } + + case DeclarationKind::InterfaceImplDeclaration: { + const auto& impl = cast(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(declaration); auto* let_proto = declaration_proto.mutable_let(); diff --git a/explorer/interpreter/interpreter.cpp b/explorer/interpreter/interpreter.cpp index 9fc9ddacb165..2f18230cb003 100644 --- a/explorer/interpreter/interpreter.cpp +++ b/explorer/interpreter/interpreter.cpp @@ -1950,6 +1950,8 @@ auto Interpreter::StepDeclaration() -> ErrorOr { case DeclarationKind::MixDeclaration: case DeclarationKind::ChoiceDeclaration: case DeclarationKind::InterfaceDeclaration: + case DeclarationKind::InterfaceExtendsDeclaration: + case DeclarationKind::InterfaceImplDeclaration: case DeclarationKind::AssociatedConstantDeclaration: case DeclarationKind::ImplDeclaration: case DeclarationKind::SelfDeclaration: diff --git a/explorer/interpreter/resolve_control_flow.cpp b/explorer/interpreter/resolve_control_flow.cpp index 6ac0dd21a60b..68a4de2a3e95 100644 --- a/explorer/interpreter/resolve_control_flow.cpp +++ b/explorer/interpreter/resolve_control_flow.cpp @@ -179,6 +179,8 @@ auto ResolveControlFlow(Nonnull declaration) -> ErrorOr { } case DeclarationKind::ChoiceDeclaration: case DeclarationKind::VariableDeclaration: + case DeclarationKind::InterfaceExtendsDeclaration: + case DeclarationKind::InterfaceImplDeclaration: case DeclarationKind::AssociatedConstantDeclaration: case DeclarationKind::SelfDeclaration: case DeclarationKind::AliasDeclaration: diff --git a/explorer/interpreter/resolve_names.cpp b/explorer/interpreter/resolve_names.cpp index fa34340f3b74..499605510dde 100644 --- a/explorer/interpreter/resolve_names.cpp +++ b/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(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(declaration); + CARBON_RETURN_IF_ERROR(ResolveNames(*extends.base(), enclosing_scope)); + break; + } + case DeclarationKind::InterfaceImplDeclaration: { + auto& impl = cast(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(declaration); CARBON_RETURN_IF_ERROR(ResolveNames(let.binding(), enclosing_scope)); diff --git a/explorer/interpreter/resolve_unformed.cpp b/explorer/interpreter/resolve_unformed.cpp index 91ba69e41442..3560216f12b0 100644 --- a/explorer/interpreter/resolve_unformed.cpp +++ b/explorer/interpreter/resolve_unformed.cpp @@ -300,6 +300,8 @@ static auto ResolveUnformed(Nonnull 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: diff --git a/explorer/interpreter/type_checker.cpp b/explorer/interpreter/type_checker.cpp index 3847147afd10..e895c6211960 100644 --- a/explorer/interpreter/type_checker.cpp +++ b/explorer/interpreter/type_checker.cpp @@ -1788,28 +1788,18 @@ auto TypeChecker::DeduceCallBindings( return Success(); } -struct ConstraintLookupResult { - Nonnull interface; - Nonnull 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 type, - std::string_view member_name) +auto TypeChecker::LookupInConstraint(SourceLocation source_loc, + std::string_view lookup_kind, + Nonnull type, + std::string_view member_name) -> ErrorOr { // Find the set of lookup contexts. llvm::ArrayRef lookup_contexts; - ConstraintType::LookupContext interface_context[1]; if (const auto* iface_type = dyn_cast(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(type)) { + CARBON_ASSIGN_OR_RETURN(type, + MakeConstraintForInterface(source_loc, iface_type)); + } + if (const auto* constraint_type = dyn_cast(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 m : iface_decl->members()) { CARBON_RETURN_IF_ERROR(DeclareDeclaration(m, iface_scope_info)); - if (auto* assoc = dyn_cast(m)) { - auto* assoc_value = arena_->New( - &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 constraint = &assoc->static_type(); - if (auto* interface_type = dyn_cast(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(m); + CARBON_ASSIGN_OR_RETURN(Nonnull base, + TypeCheckTypeExp(extends->base(), iface_scope)); + auto* constraint_type = dyn_cast(base); + if (auto* interface_type = dyn_cast(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(constraint)) { + + case DeclarationKind::InterfaceImplDeclaration: { + // For an `impl X as Y;` declaration, add `X is Y` to our constraint. + auto* impl = cast(m); + CARBON_ASSIGN_OR_RETURN( + Nonnull impl_type, + TypeCheckTypeExp(impl->impl_type(), iface_scope)); + CARBON_ASSIGN_OR_RETURN( + Nonnull constraint, + TypeCheckTypeExp(impl->constraint(), iface_scope)); + auto* constraint_type = dyn_cast(constraint); + if (auto* interface_type = dyn_cast(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(m); + auto* assoc_value = arena_->New( + &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 constraint = &assoc->static_type(); + if (auto* interface_type = dyn_cast(constraint)) { + CARBON_ASSIGN_OR_RETURN( + constraint, + MakeConstraintForInterface(assoc->source_loc(), interface_type)); + } + if (auto* constraint_type = dyn_cast(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 iface_type, << "implementation provides multiple values for " << *expected << ": " << **found_value << " and " << **second_value; } + } else if (isa(m)) { + // These get translated into constraints so there's nothing we need to + // check here. } else { // Every member function must be declared. std::optional 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 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(*d); CARBON_ASSIGN_OR_RETURN( diff --git a/explorer/interpreter/type_checker.h b/explorer/interpreter/type_checker.h index e53a87be4c2e..b2abeebf6a40 100644 --- a/explorer/interpreter/type_checker.h +++ b/explorer/interpreter/type_checker.h @@ -111,6 +111,12 @@ class TypeChecker { std::vector> bindings; }; + // Result from a lookup in a constraint. + struct ConstraintLookupResult { + Nonnull interface; + Nonnull 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 assoc) const -> Nonnull; + // 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 type, + std::string_view member_name) + -> ErrorOr; + // Given `type.(interface.member)`, look for a rewrite in the declared type // of `type`. auto LookupRewriteInTypeOf(Nonnull type, diff --git a/explorer/syntax/parser.ypp b/explorer/syntax/parser.ypp index 5f73eb132d11..5d09b2881e32 100644 --- a/explorer/syntax/parser.ypp +++ b/explorer/syntax/parser.ypp @@ -1271,6 +1271,18 @@ interface_body: $$.push_back( arena->New(context.source_loc(), $3)); } +| interface_body EXTENDS expression SEMICOLON + { + $$ = $1; + $$.push_back(arena->New(context.source_loc(), + $3)); + } +| interface_body IMPL impl_type AS type_or_where_expression SEMICOLON + { + $$ = $1; + $$.push_back(arena->New(context.source_loc(), + $3, $5)); + } ; impl_body: // Empty diff --git a/explorer/testdata/interface/extends.carbon b/explorer/testdata/interface/extends.carbon new file mode 100644 index 000000000000..15b65cc83fa8 --- /dev/null +++ b/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)(); +} diff --git a/explorer/testdata/interface/fail_bad_member_kind.carbon b/explorer/testdata/interface/fail_bad_member_kind.carbon index 6b75a0db4f37..7e7b709a9e0e 100644 --- a/explorer/testdata/interface/fail_bad_member_kind.carbon +++ b/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; } diff --git a/explorer/testdata/interface/fail_extends_not_constraint.carbon b/explorer/testdata/interface/fail_extends_not_constraint.carbon new file mode 100644 index 000000000000..5be7bee96a0c --- /dev/null +++ b/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; +} diff --git a/explorer/testdata/interface/fail_impl_as_not_constraint.carbon b/explorer/testdata/interface/fail_impl_as_not_constraint.carbon new file mode 100644 index 000000000000..193e79b2ea16 --- /dev/null +++ b/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; +} diff --git a/explorer/testdata/interface/fail_impl_as_not_type.carbon b/explorer/testdata/interface/fail_impl_as_not_type.carbon new file mode 100644 index 000000000000..7790822647db --- /dev/null +++ b/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; +} diff --git a/explorer/testdata/interface/impl_as.carbon b/explorer/testdata/interface/impl_as.carbon new file mode 100644 index 000000000000..38f55f92a173 --- /dev/null +++ b/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); +} diff --git a/explorer/testdata/interface/impl_as_not_self.carbon b/explorer/testdata/interface/impl_as_not_self.carbon new file mode 100644 index 000000000000..a9e76dbdb16a --- /dev/null +++ b/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; +}