mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-06 09:14:42 +01:00
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:
co-authored by
josh11b
parent
661a3c3907
commit
037c99e2fb
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 ";
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_;
|
||||
};
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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); }
|
||||
|
||||
@@ -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
|
||||
{ $$ = {}; }
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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; }
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user