|
@@ -7,14 +7,21 @@
|
|
|
#include <algorithm>
|
|
#include <algorithm>
|
|
|
#include <iterator>
|
|
#include <iterator>
|
|
|
#include <map>
|
|
#include <map>
|
|
|
|
|
+#include <optional>
|
|
|
#include <set>
|
|
#include <set>
|
|
|
|
|
+#include <string>
|
|
|
|
|
+#include <string_view>
|
|
|
|
|
+#include <unordered_set>
|
|
|
#include <vector>
|
|
#include <vector>
|
|
|
|
|
|
|
|
#include "common/error.h"
|
|
#include "common/error.h"
|
|
|
#include "common/ostream.h"
|
|
#include "common/ostream.h"
|
|
|
#include "explorer/ast/declaration.h"
|
|
#include "explorer/ast/declaration.h"
|
|
|
|
|
+#include "explorer/ast/expression.h"
|
|
|
#include "explorer/common/arena.h"
|
|
#include "explorer/common/arena.h"
|
|
|
#include "explorer/common/error_builders.h"
|
|
#include "explorer/common/error_builders.h"
|
|
|
|
|
+#include "explorer/common/nonnull.h"
|
|
|
|
|
+#include "explorer/common/source_location.h"
|
|
|
#include "explorer/interpreter/impl_scope.h"
|
|
#include "explorer/interpreter/impl_scope.h"
|
|
|
#include "explorer/interpreter/interpreter.h"
|
|
#include "explorer/interpreter/interpreter.h"
|
|
|
#include "explorer/interpreter/pattern_analysis.h"
|
|
#include "explorer/interpreter/pattern_analysis.h"
|
|
@@ -366,21 +373,35 @@ auto TypeChecker::FieldTypesImplicitlyConvertible(
|
|
|
return true;
|
|
return true;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// Returns all class members from class and its parent classes.
|
|
|
|
|
+static auto GetClassHierarchy(const NominalClassType& class_type)
|
|
|
|
|
+ -> std::vector<Nonnull<const NominalClassType*>> {
|
|
|
|
|
+ Nonnull<const NominalClassType*> curr_class_type = &class_type;
|
|
|
|
|
+ std::vector<Nonnull<const NominalClassType*>> all_classes{curr_class_type};
|
|
|
|
|
+ while (curr_class_type->base().has_value()) {
|
|
|
|
|
+ curr_class_type = curr_class_type->base().value();
|
|
|
|
|
+ all_classes.push_back(curr_class_type);
|
|
|
|
|
+ }
|
|
|
|
|
+ return all_classes;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
auto TypeChecker::FieldTypes(const NominalClassType& class_type) const
|
|
auto TypeChecker::FieldTypes(const NominalClassType& class_type) const
|
|
|
-> std::vector<NamedValue> {
|
|
-> std::vector<NamedValue> {
|
|
|
std::vector<NamedValue> field_types;
|
|
std::vector<NamedValue> field_types;
|
|
|
- for (Nonnull<Declaration*> m : class_type.declaration().members()) {
|
|
|
|
|
- switch (m->kind()) {
|
|
|
|
|
- case DeclarationKind::VariableDeclaration: {
|
|
|
|
|
- const auto& var = cast<VariableDeclaration>(*m);
|
|
|
|
|
- Nonnull<const Value*> field_type =
|
|
|
|
|
- Substitute(class_type.bindings(), &var.binding().static_type());
|
|
|
|
|
- field_types.push_back(
|
|
|
|
|
- {.name = var.binding().name(), .value = field_type});
|
|
|
|
|
- break;
|
|
|
|
|
|
|
+ for (const auto class_type : GetClassHierarchy(class_type)) {
|
|
|
|
|
+ for (Nonnull<Declaration*> m : class_type->declaration().members()) {
|
|
|
|
|
+ switch (m->kind()) {
|
|
|
|
|
+ case DeclarationKind::VariableDeclaration: {
|
|
|
|
|
+ const auto& var = cast<VariableDeclaration>(*m);
|
|
|
|
|
+ Nonnull<const Value*> field_type =
|
|
|
|
|
+ Substitute(class_type->bindings(), &var.binding().static_type());
|
|
|
|
|
+ field_types.push_back(
|
|
|
|
|
+ {.name = var.binding().name(), .value = field_type});
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ default:
|
|
|
|
|
+ break;
|
|
|
}
|
|
}
|
|
|
- default:
|
|
|
|
|
- break;
|
|
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
return field_types;
|
|
return field_types;
|
|
@@ -2194,11 +2215,10 @@ 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);
|
|
|
CARBON_ASSIGN_OR_RETURN(
|
|
CARBON_ASSIGN_OR_RETURN(
|
|
|
- auto type_member, FindMixedMemberAndType(
|
|
|
|
|
- access.source_loc(), access.member_name(),
|
|
|
|
|
- t_class.declaration().members(), &t_class));
|
|
|
|
|
- if (type_member.has_value()) {
|
|
|
|
|
- auto [member_type, member] = type_member.value();
|
|
|
|
|
|
|
+ const auto res,
|
|
|
|
|
+ FindMemberWithParents(access.member_name(), &t_class));
|
|
|
|
|
+ if (res.has_value()) {
|
|
|
|
|
+ auto [member_type, member] = res.value();
|
|
|
Nonnull<const Value*> field_type =
|
|
Nonnull<const Value*> field_type =
|
|
|
Substitute(t_class.bindings(), member_type);
|
|
Substitute(t_class.bindings(), member_type);
|
|
|
access.set_member(Member(member));
|
|
access.set_member(Member(member));
|
|
@@ -2413,9 +2433,9 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
|
|
|
const auto& class_type = cast<NominalClassType>(*type);
|
|
const auto& class_type = cast<NominalClassType>(*type);
|
|
|
CARBON_ASSIGN_OR_RETURN(
|
|
CARBON_ASSIGN_OR_RETURN(
|
|
|
auto type_member,
|
|
auto type_member,
|
|
|
- FindMixedMemberAndType(
|
|
|
|
|
- access.source_loc(), access.member_name(),
|
|
|
|
|
- class_type.declaration().members(), &class_type));
|
|
|
|
|
|
|
+ FindMixedMemberAndType(access.member_name(),
|
|
|
|
|
+ class_type.declaration().members(),
|
|
|
|
|
+ &class_type));
|
|
|
if (type_member.has_value()) {
|
|
if (type_member.has_value()) {
|
|
|
auto [member_type, member] = type_member.value();
|
|
auto [member_type, member] = type_member.value();
|
|
|
access.set_member(Member(member));
|
|
access.set_member(Member(member));
|
|
@@ -4067,13 +4087,37 @@ auto TypeChecker::DeclareClassDeclaration(Nonnull<ClassDeclaration*> class_decl,
|
|
|
ImplScope class_scope;
|
|
ImplScope class_scope;
|
|
|
class_scope.AddParent(scope_info.innermost_scope);
|
|
class_scope.AddParent(scope_info.innermost_scope);
|
|
|
|
|
|
|
|
- if (class_decl->extensibility() != ClassExtensibility::None) {
|
|
|
|
|
- return ProgramError(class_decl->source_loc())
|
|
|
|
|
- << "Class prefixes `base` and `abstract` are not supported yet";
|
|
|
|
|
- }
|
|
|
|
|
- if (class_decl->extends()) {
|
|
|
|
|
|
|
+ if (class_decl->extensibility() == ClassExtensibility::Abstract) {
|
|
|
return ProgramError(class_decl->source_loc())
|
|
return ProgramError(class_decl->source_loc())
|
|
|
- << "Class extension with `extends` is not supported yet";
|
|
|
|
|
|
|
+ << "Class prefix `abstract` is not supported yet";
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ std::optional<Nonnull<const NominalClassType*>> base_class;
|
|
|
|
|
+ if (class_decl->base_expr().has_value()) {
|
|
|
|
|
+ Nonnull<Expression*> base_class_expr = *class_decl->base_expr();
|
|
|
|
|
+ CARBON_ASSIGN_OR_RETURN(const auto base_type,
|
|
|
|
|
+ TypeCheckTypeExp(base_class_expr, class_scope));
|
|
|
|
|
+ switch (base_type->kind()) {
|
|
|
|
|
+ case Value::Kind::NominalClassType:
|
|
|
|
|
+ base_class = cast<NominalClassType>(base_type);
|
|
|
|
|
+ if (base_class.value()->declaration().extensibility() ==
|
|
|
|
|
+ ClassExtensibility::None) {
|
|
|
|
|
+ return ProgramError(class_decl->source_loc())
|
|
|
|
|
+ << "Base class `" << base_class.value()->declaration().name()
|
|
|
|
|
+ << "` is `final` and cannot inherited. Add the `base` or "
|
|
|
|
|
+ "`abstract` class prefix to `"
|
|
|
|
|
+ << base_class.value()->declaration().name()
|
|
|
|
|
+ << "` to allow it to be inherited";
|
|
|
|
|
+ }
|
|
|
|
|
+ class_decl->set_base(&base_class.value()->declaration());
|
|
|
|
|
+ break;
|
|
|
|
|
+ default:
|
|
|
|
|
+ return ProgramError(class_decl->source_loc())
|
|
|
|
|
+ << "Unsupported base class type for class `"
|
|
|
|
|
+ << class_decl->name()
|
|
|
|
|
+ << "`. Only simple classes are currently supported as base "
|
|
|
|
|
+ "class.";
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
std::vector<Nonnull<const GenericBinding*>> bindings = scope_info.bindings;
|
|
std::vector<Nonnull<const GenericBinding*>> bindings = scope_info.bindings;
|
|
@@ -4090,7 +4134,7 @@ auto TypeChecker::DeclareClassDeclaration(Nonnull<ClassDeclaration*> class_decl,
|
|
|
// For class declaration `class MyType(T:! Type, U:! AnInterface)`, `Self`
|
|
// For class declaration `class MyType(T:! Type, U:! AnInterface)`, `Self`
|
|
|
// should have the value `MyType(T, U)`.
|
|
// should have the value `MyType(T, U)`.
|
|
|
Nonnull<NominalClassType*> self_type = arena_->New<NominalClassType>(
|
|
Nonnull<NominalClassType*> self_type = arena_->New<NominalClassType>(
|
|
|
- class_decl, Bindings::SymbolicIdentity(arena_, bindings));
|
|
|
|
|
|
|
+ class_decl, Bindings::SymbolicIdentity(arena_, bindings), base_class);
|
|
|
self->set_static_type(arena_->New<TypeType>());
|
|
self->set_static_type(arena_->New<TypeType>());
|
|
|
self->set_constant_value(self_type);
|
|
self->set_constant_value(self_type);
|
|
|
|
|
|
|
@@ -5079,9 +5123,25 @@ auto TypeChecker::DeclareDeclaration(Nonnull<Declaration*> d,
|
|
|
return Success();
|
|
return Success();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+auto TypeChecker::FindMemberWithParents(
|
|
|
|
|
+ std::string_view name, Nonnull<const NominalClassType*> class_type)
|
|
|
|
|
+ -> ErrorOr<std::optional<
|
|
|
|
|
+ std::pair<Nonnull<const Value*>, Nonnull<const Declaration*>>>> {
|
|
|
|
|
+ CARBON_ASSIGN_OR_RETURN(
|
|
|
|
|
+ const auto res,
|
|
|
|
|
+ FindMixedMemberAndType(name, class_type->declaration().members(),
|
|
|
|
|
+ class_type));
|
|
|
|
|
+ if (res.has_value()) {
|
|
|
|
|
+ return res;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (const auto base = class_type->base(); base.has_value()) {
|
|
|
|
|
+ return FindMemberWithParents(name, base.value());
|
|
|
|
|
+ }
|
|
|
|
|
+ return {std::nullopt};
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
auto TypeChecker::FindMixedMemberAndType(
|
|
auto TypeChecker::FindMixedMemberAndType(
|
|
|
- SourceLocation source_loc, const std::string_view& name,
|
|
|
|
|
- llvm::ArrayRef<Nonnull<Declaration*>> members,
|
|
|
|
|
|
|
+ const std::string_view& name, llvm::ArrayRef<Nonnull<Declaration*>> members,
|
|
|
const Nonnull<const Value*> enclosing_type)
|
|
const Nonnull<const Value*> enclosing_type)
|
|
|
-> ErrorOr<std::optional<
|
|
-> ErrorOr<std::optional<
|
|
|
std::pair<Nonnull<const Value*>, Nonnull<const Declaration*>>>> {
|
|
std::pair<Nonnull<const Value*>, Nonnull<const Declaration*>>>> {
|
|
@@ -5091,8 +5151,7 @@ auto TypeChecker::FindMixedMemberAndType(
|
|
|
Nonnull<const MixinPseudoType*> mixin = &mix_decl.mixin_value();
|
|
Nonnull<const MixinPseudoType*> mixin = &mix_decl.mixin_value();
|
|
|
CARBON_ASSIGN_OR_RETURN(
|
|
CARBON_ASSIGN_OR_RETURN(
|
|
|
const auto res,
|
|
const auto res,
|
|
|
- FindMixedMemberAndType(source_loc, name,
|
|
|
|
|
- mixin->declaration().members(), mixin));
|
|
|
|
|
|
|
+ FindMixedMemberAndType(name, mixin->declaration().members(), mixin));
|
|
|
if (res.has_value()) {
|
|
if (res.has_value()) {
|
|
|
if (isa<NominalClassType>(enclosing_type)) {
|
|
if (isa<NominalClassType>(enclosing_type)) {
|
|
|
Bindings temp_map;
|
|
Bindings temp_map;
|