|
@@ -194,6 +194,7 @@ static auto IsTypeOfType(Nonnull<const Value*> value) -> bool {
|
|
|
case Value::Kind::PointerType:
|
|
case Value::Kind::PointerType:
|
|
|
case Value::Kind::StructType:
|
|
case Value::Kind::StructType:
|
|
|
case Value::Kind::NominalClassType:
|
|
case Value::Kind::NominalClassType:
|
|
|
|
|
+ case Value::Kind::MixinPseudoType:
|
|
|
case Value::Kind::ChoiceType:
|
|
case Value::Kind::ChoiceType:
|
|
|
case Value::Kind::ContinuationType:
|
|
case Value::Kind::ContinuationType:
|
|
|
case Value::Kind::StringType:
|
|
case Value::Kind::StringType:
|
|
@@ -210,6 +211,7 @@ static auto IsTypeOfType(Nonnull<const Value*> value) -> bool {
|
|
|
case Value::Kind::InterfaceType:
|
|
case Value::Kind::InterfaceType:
|
|
|
case Value::Kind::ConstraintType:
|
|
case Value::Kind::ConstraintType:
|
|
|
case Value::Kind::TypeOfClassType:
|
|
case Value::Kind::TypeOfClassType:
|
|
|
|
|
+ case Value::Kind::TypeOfMixinPseudoType:
|
|
|
case Value::Kind::TypeOfInterfaceType:
|
|
case Value::Kind::TypeOfInterfaceType:
|
|
|
case Value::Kind::TypeOfConstraintType:
|
|
case Value::Kind::TypeOfConstraintType:
|
|
|
case Value::Kind::TypeOfChoiceType:
|
|
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?
|
|
// ... is T.(I(Type).V) considered to be a type?
|
|
|
return IsTypeOfType(&assoc.constant().static_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();
|
|
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::ChoiceType:
|
|
|
case Value::Kind::ContinuationType:
|
|
case Value::Kind::ContinuationType:
|
|
|
case Value::Kind::StringType:
|
|
case Value::Kind::StringType:
|
|
|
|
|
+ case Value::Kind::MixinPseudoType:
|
|
|
return type;
|
|
return type;
|
|
|
case Value::Kind::TypeOfClassType:
|
|
case Value::Kind::TypeOfClassType:
|
|
|
|
|
+ case Value::Kind::TypeOfMixinPseudoType:
|
|
|
case Value::Kind::TypeOfInterfaceType:
|
|
case Value::Kind::TypeOfInterfaceType:
|
|
|
case Value::Kind::TypeOfConstraintType:
|
|
case Value::Kind::TypeOfConstraintType:
|
|
|
case Value::Kind::TypeOfChoiceType:
|
|
case Value::Kind::TypeOfChoiceType:
|
|
@@ -1577,19 +1589,21 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
|
|
|
}
|
|
}
|
|
|
case Value::Kind::NominalClassType: {
|
|
case Value::Kind::NominalClassType: {
|
|
|
const auto& t_class = cast<NominalClassType>(object_type);
|
|
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 =
|
|
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);
|
|
access.set_static_type(field_type);
|
|
|
- switch ((*member)->kind()) {
|
|
|
|
|
|
|
+ switch (member->kind()) {
|
|
|
case DeclarationKind::VariableDeclaration:
|
|
case DeclarationKind::VariableDeclaration:
|
|
|
access.set_value_category(access.object().value_category());
|
|
access.set_value_category(access.object().value_category());
|
|
|
break;
|
|
break;
|
|
|
case DeclarationKind::FunctionDeclaration: {
|
|
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() ==
|
|
if (func_decl->is_method() && func_decl->me_pattern().kind() ==
|
|
|
PatternKind::AddrPattern) {
|
|
PatternKind::AddrPattern) {
|
|
|
access.set_is_field_addr_me_method();
|
|
access.set_is_field_addr_me_method();
|
|
@@ -1764,19 +1778,20 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
|
|
|
case Value::Kind::NominalClassType: {
|
|
case Value::Kind::NominalClassType: {
|
|
|
const NominalClassType& class_type =
|
|
const NominalClassType& class_type =
|
|
|
cast<NominalClassType>(*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: {
|
|
case DeclarationKind::FunctionDeclaration: {
|
|
|
const auto& func = cast<FunctionDeclaration>(*member);
|
|
const auto& func = cast<FunctionDeclaration>(*member);
|
|
|
- if (func->is_method()) {
|
|
|
|
|
|
|
+ if (func.is_method()) {
|
|
|
break;
|
|
break;
|
|
|
}
|
|
}
|
|
|
Nonnull<const Value*> field_type = Substitute(
|
|
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_static_type(field_type);
|
|
|
access.set_value_category(ValueCategory::Let);
|
|
access.set_value_category(ValueCategory::Let);
|
|
|
return Success();
|
|
return Success();
|
|
@@ -1785,7 +1800,7 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
|
|
|
break;
|
|
break;
|
|
|
}
|
|
}
|
|
|
access.set_static_type(
|
|
access.set_static_type(
|
|
|
- arena_->New<TypeOfMemberName>(Member(*member)));
|
|
|
|
|
|
|
+ arena_->New<TypeOfMemberName>(Member(member)));
|
|
|
access.set_value_category(ValueCategory::Let);
|
|
access.set_value_category(ValueCategory::Let);
|
|
|
return Success();
|
|
return Success();
|
|
|
} else {
|
|
} else {
|
|
@@ -3431,8 +3446,13 @@ auto TypeChecker::TypeCheckClassDeclaration(
|
|
|
if (trace_stream_) {
|
|
if (trace_stream_) {
|
|
|
**trace_stream_ << class_scope;
|
|
**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()) {
|
|
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_) {
|
|
if (trace_stream_) {
|
|
|
**trace_stream_ << "** finished checking class " << class_decl->name()
|
|
**trace_stream_ << "** finished checking class " << class_decl->name()
|
|
@@ -3441,6 +3461,132 @@ auto TypeChecker::TypeCheckClassDeclaration(
|
|
|
return Success();
|
|
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(
|
|
auto TypeChecker::DeclareInterfaceDeclaration(
|
|
|
Nonnull<InterfaceDeclaration*> iface_decl, const ScopeInfo& scope_info)
|
|
Nonnull<InterfaceDeclaration*> iface_decl, const ScopeInfo& scope_info)
|
|
|
-> ErrorOr<Success> {
|
|
-> ErrorOr<Success> {
|
|
@@ -3521,7 +3667,7 @@ auto TypeChecker::TypeCheckInterfaceDeclaration(
|
|
|
**trace_stream_ << iface_scope;
|
|
**trace_stream_ << iface_scope;
|
|
|
}
|
|
}
|
|
|
for (Nonnull<Declaration*> m : iface_decl->members()) {
|
|
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_) {
|
|
if (trace_stream_) {
|
|
|
**trace_stream_ << "** finished checking interface " << iface_decl->name()
|
|
**trace_stream_ << "** finished checking interface " << iface_decl->name()
|
|
@@ -3813,7 +3959,7 @@ auto TypeChecker::TypeCheckImplDeclaration(Nonnull<ImplDeclaration*> impl_decl,
|
|
|
BringAssociatedConstantsIntoScope(constraint, self, result.interface,
|
|
BringAssociatedConstantsIntoScope(constraint, self, result.interface,
|
|
|
member_scope);
|
|
member_scope);
|
|
|
|
|
|
|
|
- CARBON_RETURN_IF_ERROR(TypeCheckDeclaration(m, member_scope));
|
|
|
|
|
|
|
+ CARBON_RETURN_IF_ERROR(TypeCheckDeclaration(m, member_scope, impl_decl));
|
|
|
}
|
|
}
|
|
|
if (trace_stream_) {
|
|
if (trace_stream_) {
|
|
|
**trace_stream_ << "finished checking impl\n";
|
|
**trace_stream_ << "finished checking impl\n";
|
|
@@ -3884,6 +4030,8 @@ static bool IsValidTypeForAliasTarget(Nonnull<const Value*> type) {
|
|
|
case Value::Kind::BoolValue:
|
|
case Value::Kind::BoolValue:
|
|
|
case Value::Kind::StructValue:
|
|
case Value::Kind::StructValue:
|
|
|
case Value::Kind::NominalClassValue:
|
|
case Value::Kind::NominalClassValue:
|
|
|
|
|
+ case Value::Kind::MixinPseudoType:
|
|
|
|
|
+ case Value::Kind::TypeOfMixinPseudoType:
|
|
|
case Value::Kind::AlternativeValue:
|
|
case Value::Kind::AlternativeValue:
|
|
|
case Value::Kind::TupleValue:
|
|
case Value::Kind::TupleValue:
|
|
|
case Value::Kind::ImplWitness:
|
|
case Value::Kind::ImplWitness:
|
|
@@ -3955,7 +4103,8 @@ auto TypeChecker::TypeCheck(AST& ast) -> ErrorOr<Success> {
|
|
|
DeclareDeclaration(declaration, top_level_scope_info));
|
|
DeclareDeclaration(declaration, top_level_scope_info));
|
|
|
}
|
|
}
|
|
|
for (Nonnull<Declaration*> decl : ast.declarations) {
|
|
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.
|
|
// Check to see if this declaration is a builtin.
|
|
|
// TODO: Only do this when type-checking the prelude.
|
|
// TODO: Only do this when type-checking the prelude.
|
|
|
builtins_.Register(decl);
|
|
builtins_.Register(decl);
|
|
@@ -3964,8 +4113,9 @@ auto TypeChecker::TypeCheck(AST& ast) -> ErrorOr<Success> {
|
|
|
return 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> {
|
|
-> ErrorOr<Success> {
|
|
|
if (trace_stream_) {
|
|
if (trace_stream_) {
|
|
|
**trace_stream_ << "checking " << DeclarationKindName(d->kind()) << "\n";
|
|
**trace_stream_ << "checking " << DeclarationKindName(d->kind()) << "\n";
|
|
@@ -3989,6 +4139,16 @@ auto TypeChecker::TypeCheckDeclaration(Nonnull<Declaration*> d,
|
|
|
CARBON_RETURN_IF_ERROR(
|
|
CARBON_RETURN_IF_ERROR(
|
|
|
TypeCheckClassDeclaration(&cast<ClassDeclaration>(*d), impl_scope));
|
|
TypeCheckClassDeclaration(&cast<ClassDeclaration>(*d), impl_scope));
|
|
|
return Success();
|
|
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:
|
|
case DeclarationKind::ChoiceDeclaration:
|
|
|
CARBON_RETURN_IF_ERROR(
|
|
CARBON_RETURN_IF_ERROR(
|
|
|
TypeCheckChoiceDeclaration(&cast<ChoiceDeclaration>(*d), impl_scope));
|
|
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));
|
|
CARBON_RETURN_IF_ERROR(DeclareClassDeclaration(&class_decl, scope_info));
|
|
|
break;
|
|
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: {
|
|
case DeclarationKind::ChoiceDeclaration: {
|
|
|
auto& choice = cast<ChoiceDeclaration>(*d);
|
|
auto& choice = cast<ChoiceDeclaration>(*d);
|
|
|
CARBON_RETURN_IF_ERROR(DeclareChoiceDeclaration(&choice, scope_info));
|
|
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
|
|
} // namespace Carbon
|