Explorer: Mixin phase 1 (#2069)

* Replay changes from pre-force-push mixin branch

* Base MixinPseudoType off of the new InterfaceType

* Add more test cases.

Also removed an unnecessary check that would have already been
handled by the parser.

* WIP detect member clashes during mixing mixins

* Implement fuzzer changes

* Implement member name clash check when mixing mixins

* Modify parser and lexer for experimental mixin feature

* Add comments

* Update explorer/testdata/mixin/simple-mix-in-mixin.carbon

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

* Update explorer/testdata/mixin/use-mixin-method-in-class-method.carbon

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

* Make code review changes

Co-authored-by: josh11b <josh11b@users.noreply.github.com>
This commit is contained in:
Darshal Shetty
2022-09-06 17:08:05 -04:00
committed by GitHub
co-authored by josh11b
parent 661a3c3907
commit 037c99e2fb
30 changed files with 1219 additions and 28 deletions
+16
View File
@@ -389,6 +389,20 @@ message AliasDeclaration {
optional Expression target = 2;
}
// EXPERIMENTAL MIXIN FEATURE
message MixinDeclaration {
optional string name = 1;
repeated Declaration members = 2;
// Type params not implemented yet
// optional TuplePattern params = 3;
optional GenericBinding self = 4;
}
// EXPERIMENTAL MIXIN FEATURE
message MixDeclaration {
optional Expression mixin = 1;
}
message Declaration {
oneof kind {
FunctionDeclaration function = 1;
@@ -399,6 +413,8 @@ message Declaration {
ImplDeclaration impl = 6;
AliasDeclaration alias = 7;
LetDeclaration let = 8;
MixinDeclaration mixin = 9;
MixDeclaration mix = 10;
}
}
+30
View File
@@ -721,6 +721,36 @@ static auto DeclarationToCarbon(const Fuzzing::Declaration& declaration,
break;
}
// EXPERIMENTAL MIXIN FEATURE
case Fuzzing::Declaration::kMixin: {
const auto& mixin_declaration = declaration.mixin();
out << "__mixin ";
IdentifierToCarbon(mixin_declaration.name(), out);
// type params are not implemented yet
// if (mixin_declaration.has_params()) {
// TuplePatternToCarbon(mixin_declaration.params(), out);
//}
out << "{\n";
for (const auto& member : mixin_declaration.members()) {
DeclarationToCarbon(member, out);
out << "\n";
}
out << "}";
// TODO: need to handle interface.self()?
break;
}
// EXPERIMENTAL MIXIN FEATURE
case Fuzzing::Declaration::kMix: {
const auto& mix_declaration = declaration.mix();
out << "__mix ";
ExpressionToCarbon(mix_declaration.mixin(), out);
out << ";";
break;
}
case Fuzzing::Declaration::kChoice: {
const auto& choice = declaration.choice();
out << "choice ";
+2
View File
@@ -16,6 +16,8 @@ abstract class Declaration : AstNode;
class FunctionDeclaration : Declaration;
class SelfDeclaration : Declaration;
class ClassDeclaration : Declaration;
class MixinDeclaration : Declaration;
class MixDeclaration : Declaration;
class ChoiceDeclaration : Declaration;
class VariableDeclaration : Declaration;
class InterfaceDeclaration : Declaration;
+34 -2
View File
@@ -52,7 +52,22 @@ void Declaration::Print(llvm::raw_ostream& out) const {
out << "}\n";
break;
}
case DeclarationKind::MixinDeclaration: {
const auto& mixin_decl = cast<MixinDeclaration>(*this);
PrintID(out);
out << "{\n";
for (Nonnull<Declaration*> m : mixin_decl.members()) {
out << *m;
}
out << "}\n";
break;
}
case DeclarationKind::MixDeclaration: {
const auto& mix_decl = cast<MixDeclaration>(*this);
PrintID(out);
out << mix_decl.mixin() << ";";
break;
}
case DeclarationKind::ChoiceDeclaration: {
const auto& choice = cast<ChoiceDeclaration>(*this);
PrintID(out);
@@ -122,7 +137,18 @@ void Declaration::PrintID(llvm::raw_ostream& out) const {
out << "class " << class_decl.name();
break;
}
case DeclarationKind::MixinDeclaration: {
const auto& mixin_decl = cast<MixinDeclaration>(*this);
out << "__mixin " << mixin_decl.name();
if (mixin_decl.self()->type().kind() != ExpressionKind::TypeTypeLiteral) {
out << " for " << mixin_decl.self()->type();
}
break;
}
case DeclarationKind::MixDeclaration: {
out << "__mix ";
break;
}
case DeclarationKind::ChoiceDeclaration: {
const auto& choice = cast<ChoiceDeclaration>(*this);
out << "choice " << choice.name();
@@ -161,6 +187,12 @@ auto GetName(const Declaration& declaration)
return cast<FunctionDeclaration>(declaration).name();
case DeclarationKind::ClassDeclaration:
return cast<ClassDeclaration>(declaration).name();
case DeclarationKind::MixinDeclaration: {
return cast<MixinDeclaration>(declaration).name();
}
case DeclarationKind::MixDeclaration: {
return std::nullopt;
}
case DeclarationKind::ChoiceDeclaration:
return cast<ChoiceDeclaration>(declaration).name();
case DeclarationKind::InterfaceDeclaration:
+65
View File
@@ -26,6 +26,7 @@
namespace Carbon {
class MixinPseudoType;
class ConstraintType;
// Abstract base class of all AST nodes representing patterns.
@@ -233,6 +234,70 @@ class ClassDeclaration : public Declaration {
std::vector<Nonnull<Declaration*>> members_;
};
// EXPERIMENTAL MIXIN FEATURE
class MixinDeclaration : public Declaration {
public:
using ImplementsCarbonValueNode = void;
MixinDeclaration(SourceLocation source_loc, std::string name,
std::optional<Nonnull<TuplePattern*>> params,
Nonnull<GenericBinding*> self,
std::vector<Nonnull<Declaration*>> members)
: Declaration(AstNodeKind::MixinDeclaration, source_loc),
name_(std::move(name)),
params_(std::move(params)),
self_(self),
members_(std::move(members)) {}
static auto classof(const AstNode* node) -> bool {
return InheritsFromMixinDeclaration(node->kind());
}
auto name() const -> const std::string& { return name_; }
auto params() const -> std::optional<Nonnull<const TuplePattern*>> {
return params_;
}
auto params() -> std::optional<Nonnull<TuplePattern*>> { return params_; }
auto self() const -> Nonnull<const GenericBinding*> { return self_; }
auto self() -> Nonnull<GenericBinding*> { return self_; }
auto members() const -> llvm::ArrayRef<Nonnull<Declaration*>> {
return members_;
}
auto value_category() const -> ValueCategory { return ValueCategory::Let; }
private:
std::string name_;
std::optional<Nonnull<TuplePattern*>> params_;
Nonnull<GenericBinding*> self_;
std::vector<Nonnull<Declaration*>> members_;
};
// EXPERIMENTAL MIXIN FEATURE
class MixDeclaration : public Declaration {
public:
MixDeclaration(SourceLocation source_loc,
std::optional<Nonnull<Expression*>> mixin_type)
: Declaration(AstNodeKind::MixDeclaration, source_loc),
mixin_(mixin_type) {}
static auto classof(const AstNode* node) -> bool {
return InheritsFromMixDeclaration(node->kind());
}
auto mixin() const -> const Expression& { return **mixin_; }
auto mixin() -> Expression& { return **mixin_; }
auto mixin_value() const -> const MixinPseudoType& { return *mixin_value_; }
void set_mixin_value(Nonnull<const MixinPseudoType*> mixin_value) {
mixin_value_ = mixin_value;
}
private:
std::optional<Nonnull<Expression*>> mixin_;
Nonnull<const MixinPseudoType*> mixin_value_;
};
class AlternativeSignature : public AstNode {
public:
AlternativeSignature(SourceLocation source_loc, std::string name,
+23
View File
@@ -607,6 +607,29 @@ static auto DeclarationToProto(const Declaration& declaration)
break;
}
case DeclarationKind::MixinDeclaration: {
const auto& mixin = cast<MixinDeclaration>(declaration);
auto* mixin_proto = declaration_proto.mutable_mixin();
mixin_proto->set_name(mixin.name());
for (const auto& member : mixin.members()) {
*mixin_proto->add_members() = DeclarationToProto(*member);
}
// Type params not implemented yet
// if (mixin.params().has_value()) {
// *mixin_proto->mutable_params() =
// TuplePatternToProto(**mixin.params());
//}
*mixin_proto->mutable_self() = GenericBindingToProto(*mixin.self());
break;
}
case DeclarationKind::MixDeclaration: {
const auto& mix = cast<MixDeclaration>(declaration);
auto* mix_proto = declaration_proto.mutable_mix();
*mix_proto->mutable_mixin() = ExpressionToProto(mix.mixin());
break;
}
case DeclarationKind::ChoiceDeclaration: {
const auto& choice = cast<ChoiceDeclaration>(declaration);
auto* choice_proto = declaration_proto.mutable_choice();
+4
View File
@@ -625,6 +625,7 @@ auto Interpreter::Convert(Nonnull<const Value*> value,
case Value::Kind::PointerType:
case Value::Kind::AutoType:
case Value::Kind::NominalClassType:
case Value::Kind::MixinPseudoType:
case Value::Kind::InterfaceType:
case Value::Kind::ConstraintType:
case Value::Kind::ImplWitness:
@@ -640,6 +641,7 @@ auto Interpreter::Convert(Nonnull<const Value*> value,
case Value::Kind::StringType:
case Value::Kind::StringValue:
case Value::Kind::TypeOfClassType:
case Value::Kind::TypeOfMixinPseudoType:
case Value::Kind::TypeOfInterfaceType:
case Value::Kind::TypeOfConstraintType:
case Value::Kind::TypeOfChoiceType:
@@ -1824,6 +1826,8 @@ auto Interpreter::StepDeclaration() -> ErrorOr<Success> {
}
case DeclarationKind::FunctionDeclaration:
case DeclarationKind::ClassDeclaration:
case DeclarationKind::MixinDeclaration:
case DeclarationKind::MixDeclaration:
case DeclarationKind::ChoiceDeclaration:
case DeclarationKind::InterfaceDeclaration:
case DeclarationKind::AssociatedConstantDeclaration:
@@ -154,6 +154,13 @@ auto ResolveControlFlow(Nonnull<Declaration*> declaration) -> ErrorOr<Success> {
}
break;
}
case DeclarationKind::MixinDeclaration: {
auto& mixin_decl = cast<MixinDeclaration>(*declaration);
for (Nonnull<Declaration*> member : mixin_decl.members()) {
CARBON_RETURN_IF_ERROR(ResolveControlFlow(member));
}
break;
}
case DeclarationKind::InterfaceDeclaration: {
auto& iface_decl = cast<InterfaceDeclaration>(*declaration);
for (Nonnull<Declaration*> member : iface_decl.members()) {
@@ -173,6 +180,7 @@ auto ResolveControlFlow(Nonnull<Declaration*> declaration) -> ErrorOr<Success> {
case DeclarationKind::AssociatedConstantDeclaration:
case DeclarationKind::SelfDeclaration:
case DeclarationKind::AliasDeclaration:
case DeclarationKind::MixDeclaration:
// do nothing
break;
}
+31
View File
@@ -46,6 +46,17 @@ static auto AddExposedNames(const Declaration& declaration,
StaticScope::NameStatus::KnownButNotDeclared));
break;
}
case DeclarationKind::MixinDeclaration: {
auto& mixin_decl = cast<MixinDeclaration>(declaration);
CARBON_RETURN_IF_ERROR(
enclosing_scope.Add(mixin_decl.name(), &mixin_decl,
StaticScope::NameStatus::KnownButNotDeclared));
break;
}
case DeclarationKind::MixDeclaration: {
// Nothing to do here
break;
}
case DeclarationKind::ChoiceDeclaration: {
auto& choice = cast<ChoiceDeclaration>(declaration);
CARBON_RETURN_IF_ERROR(
@@ -560,6 +571,26 @@ static auto ResolveNames(Declaration& declaration, StaticScope& enclosing_scope,
ResolveMemberNames(class_decl.members(), class_scope, bodies));
break;
}
case DeclarationKind::MixinDeclaration: {
auto& mixin_decl = cast<MixinDeclaration>(declaration);
StaticScope mixin_scope;
mixin_scope.AddParent(&enclosing_scope);
enclosing_scope.MarkDeclared(mixin_decl.name());
if (mixin_decl.params().has_value()) {
CARBON_RETURN_IF_ERROR(
ResolveNames(**mixin_decl.params(), mixin_scope));
}
enclosing_scope.MarkUsable(mixin_decl.name());
CARBON_RETURN_IF_ERROR(mixin_scope.Add("Self", mixin_decl.self()));
CARBON_RETURN_IF_ERROR(
ResolveMemberNames(mixin_decl.members(), mixin_scope, bodies));
break;
}
case DeclarationKind::MixDeclaration: {
auto& mix_decl = cast<MixDeclaration>(declaration);
CARBON_RETURN_IF_ERROR(ResolveNames(mix_decl.mixin(), enclosing_scope));
break;
}
case DeclarationKind::ChoiceDeclaration: {
auto& choice = cast<ChoiceDeclaration>(declaration);
StaticScope choice_scope;
@@ -294,6 +294,8 @@ static auto ResolveUnformed(Nonnull<const Declaration*> declaration)
break;
}
case DeclarationKind::ClassDeclaration:
case DeclarationKind::MixDeclaration:
case DeclarationKind::MixinDeclaration:
case DeclarationKind::InterfaceDeclaration:
case DeclarationKind::ImplDeclaration:
case DeclarationKind::ChoiceDeclaration:
+278 -23
View File
@@ -194,6 +194,7 @@ static auto IsTypeOfType(Nonnull<const Value*> value) -> bool {
case Value::Kind::PointerType:
case Value::Kind::StructType:
case Value::Kind::NominalClassType:
case Value::Kind::MixinPseudoType:
case Value::Kind::ChoiceType:
case Value::Kind::ContinuationType:
case Value::Kind::StringType:
@@ -210,6 +211,7 @@ static auto IsTypeOfType(Nonnull<const Value*> value) -> bool {
case Value::Kind::InterfaceType:
case Value::Kind::ConstraintType:
case Value::Kind::TypeOfClassType:
case Value::Kind::TypeOfMixinPseudoType:
case Value::Kind::TypeOfInterfaceType:
case Value::Kind::TypeOfConstraintType:
case Value::Kind::TypeOfChoiceType:
@@ -289,6 +291,11 @@ static auto IsType(Nonnull<const Value*> value, bool concrete = false) -> bool {
// ... is T.(I(Type).V) considered to be a type?
return IsTypeOfType(&assoc.constant().static_type());
}
case Value::Kind::MixinPseudoType:
case Value::Kind::TypeOfMixinPseudoType:
// Mixin type is a second-class type that cannot be used
// within a type annotation expression.
return false;
}
}
@@ -886,6 +893,9 @@ auto TypeChecker::ArgumentDeduction(
}
return Success();
}
case Value::Kind::MixinPseudoType:
case Value::Kind::TypeOfMixinPseudoType:
CARBON_CHECK(false) << "Type expression must not contain Mixin types";
}
}
@@ -1149,8 +1159,10 @@ auto TypeChecker::Substitute(
case Value::Kind::ChoiceType:
case Value::Kind::ContinuationType:
case Value::Kind::StringType:
case Value::Kind::MixinPseudoType:
return type;
case Value::Kind::TypeOfClassType:
case Value::Kind::TypeOfMixinPseudoType:
case Value::Kind::TypeOfInterfaceType:
case Value::Kind::TypeOfConstraintType:
case Value::Kind::TypeOfChoiceType:
@@ -1577,19 +1589,21 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
}
case Value::Kind::NominalClassType: {
const auto& t_class = cast<NominalClassType>(object_type);
if (std::optional<Nonnull<const Declaration*>> member = FindMember(
access.member_name(), t_class.declaration().members());
member.has_value()) {
if (auto type_member = FindMixedMemberAndType(
access.member_name(), t_class.declaration().members(),
&t_class);
type_member.has_value()) {
auto [member_type, member] = type_member.value();
Nonnull<const Value*> field_type =
Substitute(t_class.type_args(), &(*member)->static_type());
access.set_member(Member(member.value()));
Substitute(t_class.type_args(), member_type);
access.set_member(Member(member));
access.set_static_type(field_type);
switch ((*member)->kind()) {
switch (member->kind()) {
case DeclarationKind::VariableDeclaration:
access.set_value_category(access.object().value_category());
break;
case DeclarationKind::FunctionDeclaration: {
auto func_decl = cast<FunctionDeclaration>(*member);
auto func_decl = cast<FunctionDeclaration>(member);
if (func_decl->is_method() && func_decl->me_pattern().kind() ==
PatternKind::AddrPattern) {
access.set_is_field_addr_me_method();
@@ -1764,19 +1778,20 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
case Value::Kind::NominalClassType: {
const NominalClassType& class_type =
cast<NominalClassType>(*type);
if (std::optional<Nonnull<const Declaration*>> member =
FindMember(access.member_name(),
class_type.declaration().members());
member.has_value()) {
access.set_member(Member(member.value()));
switch ((*member)->kind()) {
if (auto type_member = FindMixedMemberAndType(
access.member_name(), class_type.declaration().members(),
&class_type);
type_member.has_value()) {
auto [member_type, member] = type_member.value();
access.set_member(Member(member));
switch (member->kind()) {
case DeclarationKind::FunctionDeclaration: {
const auto& func = cast<FunctionDeclaration>(*member);
if (func->is_method()) {
if (func.is_method()) {
break;
}
Nonnull<const Value*> field_type = Substitute(
class_type.type_args(), &(*member)->static_type());
class_type.type_args(), &member->static_type());
access.set_static_type(field_type);
access.set_value_category(ValueCategory::Let);
return Success();
@@ -1785,7 +1800,7 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
break;
}
access.set_static_type(
arena_->New<TypeOfMemberName>(Member(*member)));
arena_->New<TypeOfMemberName>(Member(member)));
access.set_value_category(ValueCategory::Let);
return Success();
} else {
@@ -3431,8 +3446,13 @@ auto TypeChecker::TypeCheckClassDeclaration(
if (trace_stream_) {
**trace_stream_ << class_scope;
}
auto [it, inserted] =
collected_members_.insert({class_decl, CollectedMembersMap()});
CARBON_CHECK(inserted) << "Adding class " << class_decl->name()
<< " to collected_members_ must not fail";
for (Nonnull<Declaration*> m : class_decl->members()) {
CARBON_RETURN_IF_ERROR(TypeCheckDeclaration(m, class_scope));
CARBON_RETURN_IF_ERROR(TypeCheckDeclaration(m, class_scope, class_decl));
CARBON_RETURN_IF_ERROR(CollectMember(class_decl, m));
}
if (trace_stream_) {
**trace_stream_ << "** finished checking class " << class_decl->name()
@@ -3441,6 +3461,132 @@ auto TypeChecker::TypeCheckClassDeclaration(
return Success();
}
// EXPERIMENTAL MIXIN FEATURE
auto TypeChecker::DeclareMixinDeclaration(Nonnull<MixinDeclaration*> mixin_decl,
const ScopeInfo& scope_info)
-> ErrorOr<Success> {
if (trace_stream_) {
**trace_stream_ << "** declaring mixin " << mixin_decl->name() << "\n";
}
ImplScope mixin_scope;
mixin_scope.AddParent(scope_info.innermost_scope);
if (mixin_decl->params().has_value()) {
CARBON_RETURN_IF_ERROR(TypeCheckPattern(*mixin_decl->params(), std::nullopt,
mixin_scope, ValueCategory::Let));
if (trace_stream_) {
**trace_stream_ << mixin_scope;
}
Nonnull<ParameterizedEntityName*> param_name =
arena_->New<ParameterizedEntityName>(mixin_decl, *mixin_decl->params());
SetConstantValue(mixin_decl, param_name);
mixin_decl->set_static_type(
arena_->New<TypeOfParameterizedEntityName>(param_name));
} else {
Nonnull<MixinPseudoType*> mixin_type =
arena_->New<MixinPseudoType>(mixin_decl);
SetConstantValue(mixin_decl, mixin_type);
mixin_decl->set_static_type(arena_->New<TypeOfMixinPseudoType>(mixin_type));
}
// Process the Self parameter.
CARBON_RETURN_IF_ERROR(TypeCheckPattern(mixin_decl->self(), std::nullopt,
mixin_scope, ValueCategory::Let));
ScopeInfo mixin_scope_info = ScopeInfo::ForNonClassScope(&mixin_scope);
for (Nonnull<Declaration*> m : mixin_decl->members()) {
CARBON_RETURN_IF_ERROR(DeclareDeclaration(m, mixin_scope_info));
}
if (trace_stream_) {
**trace_stream_ << "** finished declaring mixin " << mixin_decl->name()
<< "\n";
}
return Success();
}
// EXPERIMENTAL MIXIN FEATURE
/*
** Checks to see if mixin_decl is already within collected_members_. If it is,
** then the mixin has already been type checked before either while type
** checking a previous mix declaration or while type checking the original mixin
** declaration. If not, then every member declaration is type checked and then
** added to collected_members_ under the mixin_decl key.
*/
auto TypeChecker::TypeCheckMixinDeclaration(
Nonnull<const MixinDeclaration*> mixin_decl, const ImplScope& impl_scope)
-> ErrorOr<Success> {
auto [it, inserted] =
collected_members_.insert({mixin_decl, CollectedMembersMap()});
if (!inserted) {
// This declaration has already been type checked before
if (trace_stream_) {
**trace_stream_ << "** skipped checking mixin " << mixin_decl->name()
<< "\n";
}
return Success();
}
if (trace_stream_) {
**trace_stream_ << "** checking mixin " << mixin_decl->name() << "\n";
}
ImplScope mixin_scope;
mixin_scope.AddParent(&impl_scope);
if (mixin_decl->params().has_value()) {
BringPatternImplsIntoScope(*mixin_decl->params(), mixin_scope);
}
if (trace_stream_) {
**trace_stream_ << mixin_scope;
}
for (Nonnull<Declaration*> m : mixin_decl->members()) {
CARBON_RETURN_IF_ERROR(TypeCheckDeclaration(m, mixin_scope, mixin_decl));
CARBON_RETURN_IF_ERROR(CollectMember(mixin_decl, m));
}
if (trace_stream_) {
**trace_stream_ << "** finished checking mixin " << mixin_decl->name()
<< "\n";
}
return Success();
}
// EXPERIMENTAL MIXIN FEATURE
/*
** Type checks the mixin mentioned in the mix declaration.
** TypeCheckMixinDeclaration ensures that the members of that mixin are
** available in collected_members_. The mixin members are then collected as
** members of the enclosing class or mixin declaration.
*/
auto TypeChecker::TypeCheckMixDeclaration(
Nonnull<MixDeclaration*> mix_decl, const ImplScope& impl_scope,
std::optional<Nonnull<const Declaration*>> enclosing_decl)
-> ErrorOr<Success> {
if (trace_stream_) {
**trace_stream_ << "** checking " << *mix_decl << "\n";
}
// TODO(darshal): Check if the imports (interface mentioned in the 'for'
// clause) of the mixin being mixed are being impl'd in the enclosed
// class/mixin declaration This raises the question of how to handle impl
// declarations in mixin declarations
CARBON_CHECK(enclosing_decl.has_value());
Nonnull<const Declaration*> encl_decl = enclosing_decl.value();
auto& mixin_decl = mix_decl->mixin_value().declaration();
CARBON_RETURN_IF_ERROR(TypeCheckMixinDeclaration(&mixin_decl, impl_scope));
CollectedMembersMap& mix_members = FindCollectedMembers(&mixin_decl);
// Merge members collected in the enclosing declaration with the members
// collected for the mixin declaration associated with the mix declaration
for (auto [mix_member_name, mix_member] : mix_members) {
CARBON_RETURN_IF_ERROR(CollectMember(encl_decl, mix_member));
}
if (trace_stream_) {
**trace_stream_ << "** finished checking " << *mix_decl << "\n";
}
return Success();
}
auto TypeChecker::DeclareInterfaceDeclaration(
Nonnull<InterfaceDeclaration*> iface_decl, const ScopeInfo& scope_info)
-> ErrorOr<Success> {
@@ -3521,7 +3667,7 @@ auto TypeChecker::TypeCheckInterfaceDeclaration(
**trace_stream_ << iface_scope;
}
for (Nonnull<Declaration*> m : iface_decl->members()) {
CARBON_RETURN_IF_ERROR(TypeCheckDeclaration(m, iface_scope));
CARBON_RETURN_IF_ERROR(TypeCheckDeclaration(m, iface_scope, iface_decl));
}
if (trace_stream_) {
**trace_stream_ << "** finished checking interface " << iface_decl->name()
@@ -3813,7 +3959,7 @@ auto TypeChecker::TypeCheckImplDeclaration(Nonnull<ImplDeclaration*> impl_decl,
BringAssociatedConstantsIntoScope(constraint, self, result.interface,
member_scope);
CARBON_RETURN_IF_ERROR(TypeCheckDeclaration(m, member_scope));
CARBON_RETURN_IF_ERROR(TypeCheckDeclaration(m, member_scope, impl_decl));
}
if (trace_stream_) {
**trace_stream_ << "finished checking impl\n";
@@ -3884,6 +4030,8 @@ static bool IsValidTypeForAliasTarget(Nonnull<const Value*> type) {
case Value::Kind::BoolValue:
case Value::Kind::StructValue:
case Value::Kind::NominalClassValue:
case Value::Kind::MixinPseudoType:
case Value::Kind::TypeOfMixinPseudoType:
case Value::Kind::AlternativeValue:
case Value::Kind::TupleValue:
case Value::Kind::ImplWitness:
@@ -3955,7 +4103,8 @@ auto TypeChecker::TypeCheck(AST& ast) -> ErrorOr<Success> {
DeclareDeclaration(declaration, top_level_scope_info));
}
for (Nonnull<Declaration*> decl : ast.declarations) {
CARBON_RETURN_IF_ERROR(TypeCheckDeclaration(decl, impl_scope));
CARBON_RETURN_IF_ERROR(
TypeCheckDeclaration(decl, impl_scope, std::nullopt));
// Check to see if this declaration is a builtin.
// TODO: Only do this when type-checking the prelude.
builtins_.Register(decl);
@@ -3964,8 +4113,9 @@ auto TypeChecker::TypeCheck(AST& ast) -> ErrorOr<Success> {
return Success();
}
auto TypeChecker::TypeCheckDeclaration(Nonnull<Declaration*> d,
const ImplScope& impl_scope)
auto TypeChecker::TypeCheckDeclaration(
Nonnull<Declaration*> d, const ImplScope& impl_scope,
std::optional<Nonnull<const Declaration*>> enclosing_decl)
-> ErrorOr<Success> {
if (trace_stream_) {
**trace_stream_ << "checking " << DeclarationKindName(d->kind()) << "\n";
@@ -3989,6 +4139,16 @@ auto TypeChecker::TypeCheckDeclaration(Nonnull<Declaration*> d,
CARBON_RETURN_IF_ERROR(
TypeCheckClassDeclaration(&cast<ClassDeclaration>(*d), impl_scope));
return Success();
case DeclarationKind::MixinDeclaration: {
CARBON_RETURN_IF_ERROR(
TypeCheckMixinDeclaration(&cast<MixinDeclaration>(*d), impl_scope));
return Success();
}
case DeclarationKind::MixDeclaration: {
CARBON_RETURN_IF_ERROR(TypeCheckMixDeclaration(
&cast<MixDeclaration>(*d), impl_scope, enclosing_decl));
return Success();
}
case DeclarationKind::ChoiceDeclaration:
CARBON_RETURN_IF_ERROR(
TypeCheckChoiceDeclaration(&cast<ChoiceDeclaration>(*d), impl_scope));
@@ -4052,7 +4212,19 @@ auto TypeChecker::DeclareDeclaration(Nonnull<Declaration*> d,
CARBON_RETURN_IF_ERROR(DeclareClassDeclaration(&class_decl, scope_info));
break;
}
case DeclarationKind::MixinDeclaration: {
auto& mixin_decl = cast<MixinDeclaration>(*d);
CARBON_RETURN_IF_ERROR(DeclareMixinDeclaration(&mixin_decl, scope_info));
break;
}
case DeclarationKind::MixDeclaration: {
auto& mix_decl = cast<MixDeclaration>(*d);
CARBON_ASSIGN_OR_RETURN(
Nonnull<const Value*> mixin,
InterpExp(&mix_decl.mixin(), arena_, trace_stream_));
mix_decl.set_mixin_value(cast<MixinPseudoType>(mixin));
break;
}
case DeclarationKind::ChoiceDeclaration: {
auto& choice = cast<ChoiceDeclaration>(*d);
CARBON_RETURN_IF_ERROR(DeclareChoiceDeclaration(&choice, scope_info));
@@ -4118,4 +4290,87 @@ void TypeChecker::PrintConstants(llvm::raw_ostream& out) {
}
}
auto TypeChecker::FindMixedMemberAndType(
const std::string_view& name, llvm::ArrayRef<Nonnull<Declaration*>> members,
const Nonnull<const Value*> enclosing_type)
-> std::optional<
std::pair<Nonnull<const Value*>, Nonnull<const Declaration*>>> {
for (Nonnull<const Declaration*> member : members) {
if (llvm::isa<MixDeclaration>(member)) {
const auto& mix_decl = cast<MixDeclaration>(*member);
Nonnull<const MixinPseudoType*> mixin = &mix_decl.mixin_value();
const auto res =
FindMixedMemberAndType(name, mixin->declaration().members(), mixin);
if (res.has_value()) {
if (isa<NominalClassType>(enclosing_type)) {
BindingMap temp_map;
temp_map[mixin->declaration().self()] = enclosing_type;
const auto mix_member_type = Substitute(temp_map, res.value().first);
return std::make_pair(mix_member_type, res.value().second);
} else {
return res;
}
}
} else if (std::optional<std::string_view> mem_name = GetName(*member);
mem_name.has_value()) {
if (*mem_name == name) {
return std::make_pair(&member->static_type(), member);
}
}
}
return std::nullopt;
}
auto TypeChecker::CollectMember(Nonnull<const Declaration*> enclosing_decl,
Nonnull<const Declaration*> member_decl)
-> ErrorOr<Success> {
CARBON_CHECK(isa<MixinDeclaration>(enclosing_decl) ||
isa<ClassDeclaration>(enclosing_decl))
<< "Can't collect members for " << *enclosing_decl;
auto member_name = GetName(*member_decl);
if (!member_name.has_value()) {
// No need to collect members without a name
return Success();
}
auto encl_decl_name = GetName(*enclosing_decl);
CARBON_CHECK(encl_decl_name.has_value());
auto enclosing_decl_name = encl_decl_name.value();
auto enclosing_decl_loc = enclosing_decl->source_loc();
CollectedMembersMap& encl_members = FindCollectedMembers(enclosing_decl);
auto [it, inserted] = encl_members.insert({member_name.value(), member_decl});
if (!inserted) {
if (member_decl == it->second) {
return CompilationError(enclosing_decl_loc)
<< "Member named " << member_name.value() << " (declared at "
<< member_decl->source_loc() << ")"
<< " is being mixed multiple times into " << enclosing_decl_name;
} else {
return CompilationError(enclosing_decl_loc)
<< "Member named " << member_name.value() << " (declared at "
<< member_decl->source_loc() << ") cannot be mixed into "
<< enclosing_decl_name
<< " because it clashes with an existing member"
<< " with the same name (declared at " << it->second->source_loc()
<< ") ";
}
}
return Success();
}
auto TypeChecker::FindCollectedMembers(Nonnull<const Declaration*> decl)
-> CollectedMembersMap& {
switch (decl->kind()) {
case DeclarationKind::MixinDeclaration:
case DeclarationKind::ClassDeclaration: {
auto it = collected_members_.find(decl);
CARBON_CHECK(it != collected_members_.end());
return it->second;
}
default:
CARBON_FATAL() << "Can't collect members for " << *decl;
}
}
} // namespace Carbon
+54 -2
View File
@@ -19,6 +19,12 @@
namespace Carbon {
using CollectedMembersMap =
std::unordered_map<std::string_view, Nonnull<const Declaration*>>;
using GlobalMembersMap =
std::unordered_map<Nonnull<const Declaration*>, CollectedMembersMap>;
class TypeChecker {
public:
explicit TypeChecker(Nonnull<Arena*> arena,
@@ -64,6 +70,20 @@ class TypeChecker {
SourceLocation source_loc) const
-> std::optional<Nonnull<Expression*>>;
/*
** Finds the direct or indirect member of a class or mixin by its name and
** returns the member's declaration and type. Indirect members are members of
** mixins that are mixed by member mix declarations. If the member is an
** indirect member from a mix declaration, then the Self type variable within
** the member's type is substituted with the type of the enclosing declaration
** containing the mix declaration.
*/
auto FindMixedMemberAndType(const std::string_view& name,
llvm::ArrayRef<Nonnull<Declaration*>> members,
const Nonnull<const Value*> enclosing_type)
-> std::optional<
std::pair<Nonnull<const Value*>, Nonnull<const Declaration*>>>;
// Given the witnesses for the components of a constraint, form a witness for
// the constraint.
auto MakeConstraintWitness(
@@ -179,6 +199,9 @@ class TypeChecker {
auto DeclareClassDeclaration(Nonnull<ClassDeclaration*> class_decl,
const ScopeInfo& scope_info) -> ErrorOr<Success>;
auto DeclareMixinDeclaration(Nonnull<MixinDeclaration*> mixin_decl,
const ScopeInfo& scope_info) -> ErrorOr<Success>;
auto DeclareInterfaceDeclaration(Nonnull<InterfaceDeclaration*> iface_decl,
const ScopeInfo& scope_info)
-> ErrorOr<Success>;
@@ -246,8 +269,10 @@ class TypeChecker {
// declaration, such as the body of a function.
// Dispatches to one of the following functions.
// Assumes that DeclareDeclaration has already been invoked on `d`.
auto TypeCheckDeclaration(Nonnull<Declaration*> d,
const ImplScope& impl_scope) -> ErrorOr<Success>;
auto TypeCheckDeclaration(
Nonnull<Declaration*> d, const ImplScope& impl_scope,
std::optional<Nonnull<const Declaration*>> enclosing_decl)
-> ErrorOr<Success>;
// Type check the body of the function.
auto TypeCheckFunctionDeclaration(Nonnull<FunctionDeclaration*> f,
@@ -259,6 +284,16 @@ class TypeChecker {
const ImplScope& impl_scope)
-> ErrorOr<Success>;
// Type check all the members of the mixin.
auto TypeCheckMixinDeclaration(Nonnull<const MixinDeclaration*> mixin_decl,
const ImplScope& impl_scope)
-> ErrorOr<Success>;
auto TypeCheckMixDeclaration(
Nonnull<MixDeclaration*> mix_decl, const ImplScope& impl_scope,
std::optional<Nonnull<const Declaration*>> enclosing_decl)
-> ErrorOr<Success>;
// Type check all the members of the interface.
auto TypeCheckInterfaceDeclaration(Nonnull<InterfaceDeclaration*> iface_decl,
const ImplScope& impl_scope)
@@ -404,10 +439,27 @@ class TypeChecker {
void PrintConstants(llvm::raw_ostream& out);
/*
** Adds a member of a declaration to collected_members_
*/
auto CollectMember(Nonnull<const Declaration*> enclosing_decl,
Nonnull<const Declaration*> member_decl)
-> ErrorOr<Success>;
/*
** Fetches all direct and indirect members of a class or mixin declaration
** stored within collected_members_
*/
auto FindCollectedMembers(Nonnull<const Declaration*> decl)
-> CollectedMembersMap&;
Nonnull<Arena*> arena_;
std::set<ValueNodeView> constants_;
Builtins builtins_;
// Maps a mixin/class declaration to all of its direct and indirect members.
GlobalMembersMap collected_members_;
std::optional<Nonnull<llvm::raw_ostream*>> trace_stream_;
};
+63
View File
@@ -400,6 +400,20 @@ void Value::Print(llvm::raw_ostream& out) const {
}
break;
}
case Value::Kind::MixinPseudoType: {
const auto& mixin_type = cast<MixinPseudoType>(*this);
out << "mixin ";
PrintNameWithBindings(out, &mixin_type.declaration(), mixin_type.args());
if (!mixin_type.witnesses().empty()) {
out << " witnesses ";
llvm::ListSeparator sep;
for (const auto& [impl_bind, witness] : mixin_type.witnesses()) {
out << sep << *witness;
}
}
// TODO: print the import interface
break;
}
case Value::Kind::InterfaceType: {
const auto& iface_type = cast<InterfaceType>(*this);
out << "interface ";
@@ -491,6 +505,14 @@ void Value::Print(llvm::raw_ostream& out) const {
case Value::Kind::TypeOfClassType:
out << "typeof(" << cast<TypeOfClassType>(*this).class_type() << ")";
break;
case Value::Kind::TypeOfMixinPseudoType:
out << "typeof("
<< cast<TypeOfMixinPseudoType>(*this)
.mixin_type()
.declaration()
.name()
<< ")";
break;
case Value::Kind::TypeOfInterfaceType:
out << "typeof("
<< cast<TypeOfInterfaceType>(*this)
@@ -731,6 +753,8 @@ auto TypeEqual(Nonnull<const Value*> t1, Nonnull<const Value*> t2,
case Value::Kind::MemberName:
case Value::Kind::TypeOfParameterizedEntityName:
case Value::Kind::TypeOfMemberName:
case Value::Kind::MixinPseudoType:
case Value::Kind::TypeOfMixinPseudoType:
CARBON_FATAL() << "TypeEqual used to compare non-type values\n"
<< *t1 << "\n"
<< *t2;
@@ -830,6 +854,7 @@ auto ValueStructurallyEqual(
case Value::Kind::AutoType:
case Value::Kind::StructType:
case Value::Kind::NominalClassType:
case Value::Kind::MixinPseudoType:
case Value::Kind::InterfaceType:
case Value::Kind::ConstraintType:
case Value::Kind::ImplWitness:
@@ -839,6 +864,7 @@ auto ValueStructurallyEqual(
case Value::Kind::VariableType:
case Value::Kind::StringType:
case Value::Kind::TypeOfClassType:
case Value::Kind::TypeOfMixinPseudoType:
case Value::Kind::TypeOfInterfaceType:
case Value::Kind::TypeOfConstraintType:
case Value::Kind::TypeOfChoiceType:
@@ -950,6 +976,43 @@ auto NominalClassType::FindFunction(std::string_view name) const
-> std::optional<Nonnull<const FunctionValue*>> {
for (const auto& member : declaration().members()) {
switch (member->kind()) {
case DeclarationKind::MixDeclaration: {
const auto& mix_decl = cast<MixDeclaration>(*member);
Nonnull<const MixinPseudoType*> mixin = &mix_decl.mixin_value();
const auto res = mixin->FindFunction(name);
if (res.has_value()) {
return res;
}
break;
}
case DeclarationKind::FunctionDeclaration: {
const auto& fun = cast<FunctionDeclaration>(*member);
if (fun.name() == name) {
return &cast<FunctionValue>(**fun.constant_value());
}
break;
}
default:
break;
}
}
return std::nullopt;
}
// TODO: Find out a way to remove code duplication
auto MixinPseudoType::FindFunction(const std::string_view& name) const
-> std::optional<Nonnull<const FunctionValue*>> {
for (const auto& member : declaration().members()) {
switch (member->kind()) {
case DeclarationKind::MixDeclaration: {
const auto& mix_decl = cast<MixDeclaration>(*member);
Nonnull<const MixinPseudoType*> mixin = &mix_decl.mixin_value();
const auto res = mixin->FindFunction(name);
if (res.has_value()) {
return res;
}
break;
}
case DeclarationKind::FunctionDeclaration: {
const auto& fun = cast<FunctionDeclaration>(*member);
if (fun.name() == name) {
+52
View File
@@ -58,6 +58,7 @@ class Value {
AutoType,
StructType,
NominalClassType,
MixinPseudoType,
InterfaceType,
ConstraintType,
ChoiceType,
@@ -73,6 +74,7 @@ class Value {
StringType,
StringValue,
TypeOfClassType,
TypeOfMixinPseudoType,
TypeOfInterfaceType,
TypeOfConstraintType,
TypeOfChoiceType,
@@ -630,6 +632,41 @@ class NominalClassType : public Value {
Nonnull<const Bindings*> bindings_ = Bindings::None();
};
class MixinPseudoType : public Value {
public:
explicit MixinPseudoType(Nonnull<const MixinDeclaration*> declaration)
: Value(Kind::MixinPseudoType), declaration_(declaration) {
CARBON_CHECK(!declaration->params().has_value())
<< "missing arguments for parameterized mixin type";
}
explicit MixinPseudoType(Nonnull<const MixinDeclaration*> declaration,
Nonnull<const Bindings*> bindings)
: Value(Kind::MixinPseudoType),
declaration_(declaration),
bindings_(bindings) {}
static auto classof(const Value* value) -> bool {
return value->kind() == Kind::MixinPseudoType;
}
auto declaration() const -> const MixinDeclaration& { return *declaration_; }
auto bindings() const -> const Bindings& { return *bindings_; }
auto args() const -> const BindingMap& { return bindings_->args(); }
auto witnesses() const -> const ImplWitnessMap& {
return bindings_->witnesses();
}
auto FindFunction(const std::string_view& name) const
-> std::optional<Nonnull<const FunctionValue*>>;
private:
Nonnull<const MixinDeclaration*> declaration_;
Nonnull<const Bindings*> bindings_ = Bindings::None();
};
// Return the declaration of the member with the given name.
auto FindMember(std::string_view name,
llvm::ArrayRef<Nonnull<Declaration*>> members)
@@ -1097,6 +1134,21 @@ class TypeOfClassType : public Value {
Nonnull<const NominalClassType*> class_type_;
};
class TypeOfMixinPseudoType : public Value {
public:
explicit TypeOfMixinPseudoType(Nonnull<const MixinPseudoType*> class_type)
: Value(Kind::TypeOfMixinPseudoType), mixin_type_(class_type) {}
static auto classof(const Value* value) -> bool {
return value->kind() == Kind::TypeOfMixinPseudoType;
}
auto mixin_type() const -> const MixinPseudoType& { return *mixin_type_; }
private:
Nonnull<const MixinPseudoType*> mixin_type_;
};
class TypeOfInterfaceType : public Value {
public:
explicit TypeOfInterfaceType(Nonnull<const InterfaceType*> iface_type)
+4
View File
@@ -85,6 +85,8 @@ LET "let"
LIBRARY "library"
MATCH "match"
MINUS "-"
MIX "__mix"
MIXIN "__mixin"
NOT "not"
OR "or"
PACKAGE "package"
@@ -187,6 +189,8 @@ operand_start [(A-Za-z0-9_\"]
{LIBRARY} { return CARBON_SIMPLE_TOKEN(LIBRARY); }
{MATCH} { return CARBON_SIMPLE_TOKEN(MATCH); }
{MINUS} { return CARBON_SIMPLE_TOKEN(MINUS); }
{MIXIN} { return CARBON_SIMPLE_TOKEN(MIXIN); }
{MIX} { return CARBON_SIMPLE_TOKEN(MIX); }
{NOT} { return CARBON_SIMPLE_TOKEN(NOT); }
{OR} { return CARBON_SIMPLE_TOKEN(OR); }
{PACKAGE} { return CARBON_SIMPLE_TOKEN(PACKAGE); }
+58 -1
View File
@@ -108,8 +108,11 @@
%type <std::optional<Nonnull<Expression*>>> class_declaration_extends
%type <Nonnull<Declaration*>> declaration
%type <Nonnull<FunctionDeclaration*>> function_declaration
%type <Nonnull<MixDeclaration*>> mix_declaration
%type <Nonnull<AliasDeclaration*>> alias_declaration
%type <std::vector<Nonnull<Declaration*>>> declaration_list
%type <std::vector<Nonnull<Declaration*>>> class_body
%type <std::vector<Nonnull<Declaration*>>> mixin_body
%type <std::vector<Nonnull<Declaration*>>> interface_body
%type <std::vector<Nonnull<Declaration*>>> impl_body
%type <Nonnull<Statement*>> statement
@@ -160,6 +163,7 @@
%type <Nonnull<Expression*>> statement_expression
%type <Nonnull<Expression*>> if_expression
%type <Nonnull<Expression*>> expression
%type <Nonnull<Expression*>> mixin_import
%type <Nonnull<GenericBinding*>> generic_binding
%type <Nonnull<Pattern*>> deduced_param
%type <std::vector<Nonnull<AstNode*>>> deduced_params
@@ -249,6 +253,8 @@
LIBRARY
MATCH
MINUS
MIX
MIXIN
NOT
OR
PACKAGE
@@ -1066,6 +1072,10 @@ variable_declaration: identifier COLON pattern
alias_declaration: ALIAS identifier EQUAL expression SEMICOLON
{ $$ = arena->New<AliasDeclaration>(context.source_loc(), $2, $4); }
;
// EXPERIMENTAL MIXIN FEATURE
mix_declaration: MIX expression SEMICOLON
{ $$ = arena->New<MixDeclaration>(context.source_loc(), $2); }
;
alternative:
identifier tuple
{ $$ = arena->New<AlternativeSignature>(context.source_loc(), $1, $2); }
@@ -1099,6 +1109,17 @@ type_params:
| tuple_pattern
{ $$ = $1; }
;
// EXPERIMENTAL MIXIN FEATURE
mixin_import:
// Empty
{ $$ = arena->New<TypeTypeLiteral>(context.source_loc()); }
| FOR expression
{
context.RecordSyntaxError("'for' not supported currently");
YYERROR;
// $$ = $2;
}
;
class_declaration_extensibility:
// Empty
{ $$ = Carbon::ClassExtensibility::None; }
@@ -1116,12 +1137,19 @@ class_declaration_extends:
declaration:
function_declaration
{ $$ = $1; }
| class_declaration_extensibility CLASS identifier type_params class_declaration_extends LEFT_CURLY_BRACE declaration_list RIGHT_CURLY_BRACE
| class_declaration_extensibility CLASS identifier type_params class_declaration_extends LEFT_CURLY_BRACE class_body RIGHT_CURLY_BRACE
{
$$ = arena->New<ClassDeclaration>(
context.source_loc(), $3,
arena->New<SelfDeclaration>(context.source_loc()), $1, $4, $5, $7);
}
| MIXIN identifier type_params mixin_import LEFT_CURLY_BRACE mixin_body RIGHT_CURLY_BRACE
{
// EXPERIMENTAL MIXN FEATURE
auto self =
arena -> New<GenericBinding>(context.source_loc(), "Self", $4);
$$ = arena->New<MixinDeclaration>(context.source_loc(), $2, $3, self, $6);
}
| CHOICE identifier type_params LEFT_CURLY_BRACE alternative_list RIGHT_CURLY_BRACE
{ $$ = arena->New<ChoiceDeclaration>(context.source_loc(), $2, $3, $5); }
| VAR variable_declaration SEMICOLON
@@ -1184,6 +1212,35 @@ declaration_list:
$$.push_back(Nonnull<Declaration*>($2));
}
;
class_body:
// Empty
{ $$ = {}; }
| class_body declaration
{
$$ = $1;
$$.push_back(Nonnull<Declaration*>($2));
}
| class_body mix_declaration
{
$$ = $1;
$$.push_back(Nonnull<Declaration*>($2));
}
;
// EXPERIMENTAL MIXIN FEATURE
mixin_body:
// Empty
{ $$ = {}; }
| mixin_body function_declaration
{
$$ = $1;
$$.push_back(Nonnull<Declaration*>($2));
}
| mixin_body mix_declaration
{
$$ = $1;
$$.push_back(Nonnull<Declaration*>($2));
}
;
interface_body:
// Empty
{ $$ = {}; }
+38
View File
@@ -0,0 +1,38 @@
// 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;
__mixin M1 {
fn F1[me: Self](x: Self) -> Self{
return x;
}
// CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/mixin/fail-circular-mixing.carbon:[[@LINE+1]]: 'M3' has not been declared yet
__mix M3;
}
__mixin M2 {
fn F2() {
}
__mix M1;
}
__mixin M3 {
__mix M2;
fn F3() {
}
}
class C {
__mix M2;
}
fn Main() -> i32 {
return 0;
}
@@ -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: %{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
// CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/mixin/fail-field-member-name-clash.carbon:23: Member named F (declared at {{.*}}/explorer/testdata/mixin/fail-field-member-name-clash.carbon:17) cannot be mixed into C because it clashes with an existing member with the same name (declared at {{.*}}/explorer/testdata/mixin/fail-field-member-name-clash.carbon:21)
package ExplorerTest api;
__mixin M {
fn F[me: Self](x: Self) -> Self{
return x;
}
}
class C {
let F: i32 = 0;
__mix M;
}
fn Main() -> i32 {
return 0;
}
@@ -0,0 +1,29 @@
// 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
// CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/mixin/fail-method-member-name-clash.carbon:25: Member named F (declared at {{.*}}/explorer/testdata/mixin/fail-method-member-name-clash.carbon:17) cannot be mixed into C because it clashes with an existing member with the same name (declared at {{.*}}/explorer/testdata/mixin/fail-method-member-name-clash.carbon:23)
package ExplorerTest api;
__mixin M {
fn F[me: Self](x: Self) -> Self{
return x;
}
}
class C {
fn F[me: Self](x: Self) -> Self {
return x;
}
__mix M;
}
fn Main() -> i32 {
return 0;
}
+19
View File
@@ -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;
__mixin Operations {}
fn Main() -> i32 {
// CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/mixin/fail-mix-as-type-expr.carbon:[[@LINE+1]]: Expected a type, but got mixin Operations
var a: Operations;
return 0;
}
+38
View File
@@ -0,0 +1,38 @@
// 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
// CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/mixin/fail-mix-diamond-clash.carbon:34: Member named F1 (declared at {{.*}}/explorer/testdata/mixin/fail-mix-diamond-clash.carbon:16) is being mixed multiple times into C
package ExplorerTest api;
__mixin M1 {
fn F1[me: Self](x: Self) -> Self{
return x;
}
}
__mixin M2 {
fn F2() {
}
__mix M1;
}
__mixin M3 {
__mix M1;
fn F3() {
}
}
class C {
__mix M2;
__mix M3;
}
fn Main() -> i32 {
return 0;
}
+49
View File
@@ -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: %{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;
__mixin Operations {
fn Square[me: Self](x:i32) -> i32{
return x * x;
}
}
// CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/mixin/fail-mix-in-global.carbon:[[@LINE+1]]: syntax error, unexpected MIX, expecting END_OF_FILE
__mix Operations;
class Point {
fn Origin() -> Point {
return {.x = 0, .y = 0};
}
var x: i32;
var y: i32;
__mix Operations;
}
class Complex {
fn Zero() -> Complex {
return {.r = 0, .i = 0};
}
var r: i32;
var i: i32;
__mix Operations;
}
fn Main() -> i32 {
var p: Point = Point.Origin();
var c: Complex = Complex.Zero();
p.x = 3;
c.r = 3;
return p.Square(p.y) - c.Square(c.i);
}
+29
View File
@@ -0,0 +1,29 @@
// 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;
__mixin Operations {
fn Square[me: Self](x:i32) -> i32{
return x * x;
}
}
interface A {
fn F();
}
external impl i32 as A {
// CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/mixin/fail-mix-in-impl.carbon:[[@LINE+1]]: syntax error, unexpected MIX, expecting ALIAS or FN or RIGHT_CURLY_BRACE
__mix Operations;
fn F() {}
}
fn Main() -> i32 { return 0; }
+31
View File
@@ -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
// CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/mixin/fail-mix-members-clash.carbon:27: Member named F1 (declared at {{.*}}/explorer/testdata/mixin/fail-mix-members-clash.carbon:21) cannot be mixed into C because it clashes with an existing member with the same name (declared at {{.*}}/explorer/testdata/mixin/fail-mix-members-clash.carbon:16)
package ExplorerTest api;
__mixin M1 {
fn F1[me: Self](x: Self) -> Self{
return x;
}
}
__mixin M2 {
fn F1() {
}
}
class C {
__mix M1;
__mix M2;
}
fn Main() -> i32 {
return 0;
}
+26
View File
@@ -0,0 +1,26 @@
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// RUN: %{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
// CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/mixin/fail-recursive-mixing.carbon:18: Member named F1 (declared at {{.*}}/explorer/testdata/mixin/fail-recursive-mixing.carbon:16) is being mixed multiple times into M1
package ExplorerTest api;
__mixin M1 {
fn F1[me: Self](x: Self) -> Self{
return x;
}
__mix M1;
}
class C {
__mix M1;
}
fn Main() -> i32 {
return 0;
}
+45
View File
@@ -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;
__mixin Operations {
fn F[me: Self](x: Self) -> Self{
return x;
}
}
class Point {
fn Origin() -> Point {
return {.x = 0, .y = 0};
}
var x: i32;
var y: i32;
__mix Operations;
}
class Complex {
fn Zero() -> Complex {
return {.r = 0, .i = 0};
}
var r: i32;
var i: i32;
}
fn Main() -> i32 {
var p: Point = Point.Origin();
var c: Complex = {.r = 42, .i = 1};
// CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/mixin/fail-self-substitution.carbon:[[@LINE+1]]: type error in call: 'class Complex' is not implicitly convertible to 'class Point'
var p1: Point = p.F(c);
return p1.x - 42;
}
+39
View File
@@ -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;
__mixin Operations {
// Here Self which is both the input and output type is a type variable
fn F[me: Self](x: Self) -> Self {
return x;
}
}
class Point {
fn Origin() -> Point {
return {.x = 0, .y = 0};
}
var x: i32;
var y: i32;
__mix Operations;
}
fn Main() -> i32 {
var p1: Point = Point.Origin();
var p2: Point = {.x = 42, .y = 1};
// After accessing the mixin member F through a Point object, the
// input and output type variables of F get substituted with
// the Point class type
var p3: Point = p1.F(p2);
return p3.x - 42;
}
+34
View File
@@ -0,0 +1,34 @@
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// RUN: %{explorer} %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
// RUN: %{explorer} --parser_debug --trace_file=- %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s
// AUTOUPDATE: %{explorer} %s
// CHECK: result: 0
package ExplorerTest api;
__mixin M1 {
fn Scale10[me:Self](x:i32) -> i32{
return x * 10;
}
}
__mixin M2 {
__mix M1;
fn Square[me: Self](x:i32) -> i32{
return x * x;
}
}
class C {
__mix M2;
}
fn Main() -> i32 {
var c: C = {};
return c.Square(11) - c.Scale10(10) - 21;
}
+47
View File
@@ -0,0 +1,47 @@
// 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;
__mixin Operations {
fn Square[me: Self](x:i32) -> i32{
return x * x;
}
}
class Point {
fn Origin() -> Point {
return {.x = 0, .y = 0};
}
var x: i32;
var y: i32;
__mix Operations;
}
class Complex {
fn Zero() -> Complex {
return {.r = 0, .i = 0};
}
var r: i32;
var i: i32;
__mix Operations;
}
fn Main() -> i32 {
var p: Point = Point.Origin();
var c: Complex = Complex.Zero();
p.x = 3;
c.r = 4;
return p.Square(p.x) - c.Square(c.r) + 7;
}
@@ -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: %{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;
__mixin Operations {
fn Square[me: Self](x:i32) -> i32{
return x * x;
}
}
class Point {
var x: i32;
var y: i32;
fn DistanceSquare[me:Self](other:Self) -> i32 {
return me.Square(me.x - other.x) + me.Square(me.y - other.y);
}
__mix Operations;
}
class Complex {
var r: i32;
var i: i32;
__mix Operations;
fn AbsSquare[me:Self]() -> i32 {
return me.Square(me.r) + me.Square(me.i);
}
}
fn Main() -> i32 {
var p1: Point = {.x = 1, .y = 2 };
var p2: Point = {.x = 4, .y = 3 };
var c: Complex = {.r = 5, .i = 6 };
return c.AbsSquare() - p1.DistanceSquare(p2) - 51;
}