mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Inherit complete object initializers when a class supports it.
Teach name lookup to find complete object initializers in its superclass when the current class overrides all of the subobject initializers of its direct superclass. Clean up the implicit declaration of constructors, so we don't rely on callers in the type checker doing the right thing. When we refer to a constructor within the type checker, always use the type through which the constructor was found as the result of construction, so that we can type-check uses of inherited complete object initializers. Fixed a problem with the creation of OpenExistentialExprs when the base object is a metatype. The changes to the code completion tests are an improvement: we're generating ExprSpecific completion results when referring to the superclass initializer with the same signature as the initializer we're in after "super.". Swift SVN r14551
This commit is contained in:
@@ -112,6 +112,17 @@ enum class CircularityCheck {
|
||||
Checked
|
||||
};
|
||||
|
||||
/// Keeps track of whrther a given class inherits initializers from its
|
||||
/// superclass.
|
||||
enum class StoredInheritsSuperclassInits {
|
||||
/// We have not yet checked.
|
||||
Unchecked,
|
||||
/// Superclass initializers are not inherited.
|
||||
NotInherited,
|
||||
/// Complete object initializers in the superclass are inherited.
|
||||
Inherited
|
||||
};
|
||||
|
||||
/// Describes which spelling was used in the source for the 'static' or 'class'
|
||||
/// keyword.
|
||||
enum class StaticSpellingKind : uint8_t {
|
||||
@@ -326,8 +337,14 @@ class alignas(8) Decl {
|
||||
/// Whether this class requires all of its instance variables to
|
||||
/// have in-class initializers.
|
||||
unsigned RequiresStoredPropertyInits : 1;
|
||||
|
||||
/// Whether this class inherits its superclass's complete object
|
||||
/// initializers.
|
||||
///
|
||||
/// This is a value of \c StoredInheritsSuperclassInits.
|
||||
unsigned InheritsSuperclassInits : 2;
|
||||
};
|
||||
enum { NumClassDeclBits = NumNominalTypeDeclBits + 3 };
|
||||
enum { NumClassDeclBits = NumNominalTypeDeclBits + 5 };
|
||||
static_assert(NumClassDeclBits <= 32, "fits in an unsigned");
|
||||
|
||||
class EnumDeclBitfields {
|
||||
@@ -2551,6 +2568,13 @@ public:
|
||||
/// Retrieve the destructor for this class.
|
||||
DestructorDecl *getDestructor();
|
||||
|
||||
/// Determine whether this class inherits the complete object initializers
|
||||
/// from its superclass.
|
||||
///
|
||||
/// \param resolver Used to resolve the signatures of initializers, which is
|
||||
/// required for name lookup.
|
||||
bool inheritsSuperclassInitializers(LazyResolver *resolver);
|
||||
|
||||
// Implement isa/cast/dyncast/etc.
|
||||
static bool classof(const Decl *D) {
|
||||
return D->getKind() == DeclKind::Class;
|
||||
@@ -3749,6 +3773,11 @@ public:
|
||||
ConstructorDeclBits.CompleteObjectInit = completeObjectInit;
|
||||
}
|
||||
|
||||
/// Whether this is a subobject initializer.
|
||||
bool isSubobjectInit() const {
|
||||
return !isCompleteObjectInit();
|
||||
}
|
||||
|
||||
ConstructorDecl *getOverriddenDecl() const { return OverriddenDecl; }
|
||||
void setOverriddenDecl(ConstructorDecl *over) { OverriddenDecl = over; }
|
||||
|
||||
|
||||
@@ -85,6 +85,9 @@ public:
|
||||
/// It does not perform full type-checking, only checks for basic
|
||||
/// consistency and provides the value a type.
|
||||
virtual void resolveDeclSignature(ValueDecl *VD) = 0;
|
||||
|
||||
/// Resolve any implicitly-declared constructors within the given nominal.
|
||||
virtual void resolveImplicitConstructors(NominalTypeDecl *nominal) = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -100,7 +100,7 @@ enum NameLookupOptions {
|
||||
NL_RemoveNonVisible | NL_RemoveOverridden,
|
||||
|
||||
/// The default set of options used for constructor lookup.
|
||||
NL_Constructor = NL_RemoveNonVisible
|
||||
NL_Constructor = NL_QualifiedDefault
|
||||
};
|
||||
|
||||
/// Describes the result of looking for the conformance of a given type
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "swift/AST/DiagnosticEngine.h"
|
||||
#include "swift/AST/DiagnosticsSema.h"
|
||||
#include "swift/AST/Expr.h"
|
||||
#include "swift/AST/LazyResolver.h"
|
||||
#include "swift/AST/TypeLoc.h"
|
||||
#include "llvm/ADT/SmallPtrSet.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
@@ -1021,6 +1022,8 @@ ClassDecl::ClassDecl(SourceLoc ClassLoc, Identifier Name, SourceLoc NameLoc,
|
||||
ClassDeclBits.Circularity
|
||||
= static_cast<unsigned>(CircularityCheck::Unchecked);
|
||||
ClassDeclBits.RequiresStoredPropertyInits = 0;
|
||||
ClassDeclBits.InheritsSuperclassInits
|
||||
= static_cast<unsigned>(StoredInheritsSuperclassInits::Unchecked);
|
||||
}
|
||||
|
||||
DestructorDecl *ClassDecl::getDestructor() {
|
||||
@@ -1031,6 +1034,76 @@ DestructorDecl *ClassDecl::getDestructor() {
|
||||
return cast<DestructorDecl>(results.front());
|
||||
}
|
||||
|
||||
bool ClassDecl::inheritsSuperclassInitializers(LazyResolver *resolver) {
|
||||
// Check whether we already have a cached answer.
|
||||
switch (static_cast<StoredInheritsSuperclassInits>(
|
||||
ClassDeclBits.InheritsSuperclassInits)) {
|
||||
case StoredInheritsSuperclassInits::Unchecked:
|
||||
// Compute below.
|
||||
break;
|
||||
|
||||
case StoredInheritsSuperclassInits::Inherited:
|
||||
return true;
|
||||
|
||||
case StoredInheritsSuperclassInits::NotInherited:
|
||||
return false;
|
||||
}
|
||||
|
||||
// If there's no superclass, there's nothing to inherit.
|
||||
ClassDecl *superclassDecl;
|
||||
if (!getSuperclass() ||
|
||||
!(superclassDecl = getSuperclass()->getClassOrBoundGenericClass())) {
|
||||
ClassDeclBits.InheritsSuperclassInits
|
||||
= static_cast<unsigned>(StoredInheritsSuperclassInits::NotInherited);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Look at all of the initializers of the subclass to gather the initializers
|
||||
// they override from the superclass.
|
||||
auto &ctx = getASTContext();
|
||||
llvm::SmallPtrSet<ConstructorDecl *, 4> overriddenInits;
|
||||
if (resolver)
|
||||
resolver->resolveImplicitConstructors(this);
|
||||
for (auto member : lookupDirect(ctx.Id_init)) {
|
||||
auto ctor = dyn_cast<ConstructorDecl>(member);
|
||||
if (!ctor)
|
||||
continue;
|
||||
|
||||
// Resolve this initializer, if needed.
|
||||
if (!ctor->hasType())
|
||||
resolver->resolveDeclSignature(ctor);
|
||||
|
||||
if (auto overridden = ctor->getOverriddenDecl()) {
|
||||
if (overridden->isSubobjectInit())
|
||||
overriddenInits.insert(overridden);
|
||||
}
|
||||
}
|
||||
|
||||
// Check all of the subobject initializers in the direct superclass.
|
||||
if (resolver)
|
||||
resolver->resolveImplicitConstructors(superclassDecl);
|
||||
for (auto member : superclassDecl->lookupDirect(ctx.Id_init)) {
|
||||
// We only care about subobject initializers.
|
||||
auto ctor = dyn_cast<ConstructorDecl>(member);
|
||||
if (!ctor || ctor->isCompleteObjectInit())
|
||||
continue;
|
||||
|
||||
// If this subobject initializer wasn't overridden, we don't have any
|
||||
//
|
||||
if (overriddenInits.count(ctor) == 0) {
|
||||
ClassDeclBits.InheritsSuperclassInits
|
||||
= static_cast<unsigned>(StoredInheritsSuperclassInits::NotInherited);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// All of the direct superclass's subobject initializers have been overridden
|
||||
// by the sublcass. Initializers can be inherited.
|
||||
ClassDeclBits.InheritsSuperclassInits
|
||||
= static_cast<unsigned>(StoredInheritsSuperclassInits::Inherited);
|
||||
return true;
|
||||
}
|
||||
|
||||
EnumCaseDecl *EnumCaseDecl::create(SourceLoc CaseLoc,
|
||||
ArrayRef<EnumElementDecl *> Elements,
|
||||
DeclContext *DC) {
|
||||
|
||||
@@ -425,8 +425,12 @@ SourceRange MetatypeExpr::getSourceRange() const {
|
||||
if (auto tyR = getBaseTypeRepr())
|
||||
return tyR->getSourceRange();
|
||||
|
||||
if (auto base = getBase())
|
||||
return SourceRange(base->getStartLoc(), MetatypeLoc);
|
||||
if (auto base = getBase()) {
|
||||
if (MetatypeLoc.isValid())
|
||||
return SourceRange(base->getStartLoc(), MetatypeLoc);
|
||||
|
||||
return base->getSourceRange();
|
||||
}
|
||||
|
||||
return SourceRange(MetatypeLoc);
|
||||
}
|
||||
|
||||
@@ -876,13 +876,36 @@ bool DeclContext::lookupQualified(Type type,
|
||||
}
|
||||
}
|
||||
|
||||
// Allow filtering of the visible declarations based on
|
||||
bool onlyCompleteObjectInits = false;
|
||||
auto isAcceptableDecl = [&](Decl *decl) -> bool {
|
||||
// Filter out subobject initializers, if requestred.
|
||||
if (onlyCompleteObjectInits) {
|
||||
if (auto ctor = dyn_cast<ConstructorDecl>(decl)) {
|
||||
if (!ctor->isCompleteObjectInit())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
// Visit all of the nominal types we know about, discovering any others
|
||||
// we need along the way.
|
||||
auto &ctx = getASTContext();
|
||||
llvm::DenseMap<ProtocolDecl *, ProtocolConformance *> knownConformances;
|
||||
while (!stack.empty()) {
|
||||
auto current = stack.back();
|
||||
stack.pop_back();
|
||||
|
||||
// Make sure we've resolved implicit constructors, if we need them.
|
||||
if (name == ctx.Id_init && typeResolver)
|
||||
typeResolver->resolveImplicitConstructors(current);
|
||||
|
||||
// Look for results within the current nominal type and its extensions.
|
||||
bool currentIsProtocol = isa<ProtocolDecl>(current);
|
||||
for (auto decl : current->lookupDirect(name)) {
|
||||
@@ -895,7 +918,8 @@ bool DeclContext::lookupQualified(Type type,
|
||||
continue;
|
||||
}
|
||||
|
||||
decls.push_back(decl);
|
||||
if (isAcceptableDecl(decl))
|
||||
decls.push_back(decl);
|
||||
}
|
||||
|
||||
// If we're not supposed to visit our supertypes, we're done.
|
||||
@@ -904,6 +928,16 @@ bool DeclContext::lookupQualified(Type type,
|
||||
|
||||
// Visit superclass.
|
||||
if (auto classDecl = dyn_cast<ClassDecl>(current)) {
|
||||
// If we're looking for initializers, only look at the superclass if the
|
||||
// current class permits inheritance. Even then, only find complete
|
||||
// object initializers.
|
||||
if (name == ctx.Id_init) {
|
||||
if (classDecl->inheritsSuperclassInitializers(typeResolver))
|
||||
onlyCompleteObjectInits = true;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
|
||||
if (auto superclassType = classDecl->getSuperclass())
|
||||
if (auto superclassDecl = superclassType->getClassOrBoundGenericClass())
|
||||
if (visited.insert(superclassDecl))
|
||||
|
||||
@@ -457,16 +457,21 @@ namespace {
|
||||
/// \param base An expression of existential type whose value will
|
||||
/// be opened.
|
||||
///
|
||||
/// \returns A pair (opaque-value, archetype) that provides a
|
||||
/// reference to the value stored within the expression
|
||||
/// (opaque-value) and a new archetype that describes the dynamic
|
||||
/// type stored within the existential.
|
||||
std::tuple<OpaqueValueExpr *, ArchetypeType *>
|
||||
/// \returns A pair (expr, type) that provides a reference to the value
|
||||
/// stored within the expression or its metatype (if the base was a
|
||||
/// metatype) and the new archetype that describes the dynamic type stored
|
||||
/// within the existential.
|
||||
std::tuple<Expr *, ArchetypeType *>
|
||||
openExistentialReference(Expr *base) {
|
||||
auto &tc = cs.getTypeChecker();
|
||||
base = tc.coerceToRValue(base);
|
||||
|
||||
auto baseTy = base->getType()->getRValueInstanceType();
|
||||
auto baseTy = base->getType()->getRValueType();
|
||||
bool isMetatype = false;
|
||||
if (auto metaTy = baseTy->getAs<MetatypeType>()) {
|
||||
isMetatype = true;
|
||||
baseTy = metaTy->getInstanceType();
|
||||
}
|
||||
assert(baseTy->isExistentialType() && "Type must be existential");
|
||||
|
||||
// Create the archetype.
|
||||
@@ -483,7 +488,14 @@ namespace {
|
||||
// Record this opened existential.
|
||||
OpenedExistentials[archetype] = { base, archetypeVal };
|
||||
|
||||
return std::make_tuple(archetypeVal, archetype);
|
||||
// If we started with a metatype, produce a metatype.
|
||||
Expr *newBase = archetypeVal;
|
||||
if (isMetatype) {
|
||||
auto metaTy = MetatypeType::get(archetype, ctx);
|
||||
newBase = new (ctx) MetatypeExpr(archetypeVal, SourceLoc(), metaTy);
|
||||
}
|
||||
|
||||
return std::make_tuple(newBase, archetype);
|
||||
}
|
||||
|
||||
/// \brief Build a new member reference with the given base and member.
|
||||
@@ -551,12 +563,11 @@ namespace {
|
||||
/*wantInterfaceType=*/true);
|
||||
}
|
||||
|
||||
// If this is a method whose result type is DynamicSelf, or a construction
|
||||
// of a DynamicSelf value, replace DynamicSelf with the actual object
|
||||
// type.
|
||||
// If this is a method whose result type is dynamic Self, or a
|
||||
// construction, replace the result type with the actual object type.
|
||||
if (auto func = dyn_cast<AbstractFunctionDecl>(member)) {
|
||||
if ((isa<FuncDecl>(func) && cast<FuncDecl>(func)->hasDynamicSelf()) ||
|
||||
(baseTy->is<DynamicSelfType>() && isa<ConstructorDecl>(func))) {
|
||||
isa<ConstructorDecl>(func)) {
|
||||
// For a DynamicSelf method on an existential, open up the
|
||||
// existential.
|
||||
if (func->getExtensionType()->is<ProtocolType>() &&
|
||||
|
||||
@@ -962,12 +962,8 @@ ConstraintSystem::getTypeOfMemberReference(Type baseTy, ValueDecl *value,
|
||||
});
|
||||
}
|
||||
}
|
||||
// Alternatively, if this is a constructor referenced from a DynamicSelf base
|
||||
// object, or a constructor within a protocol, replace the result type with
|
||||
// the base object type.
|
||||
else if (isa<ConstructorDecl>(value) &&
|
||||
(baseObjTy->is<DynamicSelfType>() ||
|
||||
isa<ProtocolDecl>(value->getDeclContext()))) {
|
||||
// If this is a construct, replace the result type with the base object type.
|
||||
else if (isa<ConstructorDecl>(value)) {
|
||||
auto outerFnType = openedType->castTo<FunctionType>();
|
||||
auto innerFnType = outerFnType->getResult()->castTo<FunctionType>();
|
||||
|
||||
|
||||
@@ -2641,11 +2641,6 @@ public:
|
||||
auto parentFunc = cast<FuncDecl>(parentDecl);
|
||||
if (func->isStatic() != parentFunc->isStatic())
|
||||
return false;
|
||||
} else if (isa<ConstructorDecl>(decl)) {
|
||||
// Specific checking for constructors.
|
||||
auto parentCtor = cast<ConstructorDecl>(parentDecl);
|
||||
if (!parentCtor->isAbstract())
|
||||
return false;
|
||||
} else if (auto var = dyn_cast<VarDecl>(decl)) {
|
||||
auto parentVar = cast<VarDecl>(parentDecl);
|
||||
if (var->isStatic() != parentVar->isStatic())
|
||||
@@ -3616,7 +3611,9 @@ static ConstructorDecl *createImplicitConstructor(TypeChecker &tc,
|
||||
}
|
||||
|
||||
void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) {
|
||||
assert(isa<StructDecl>(decl) || isa<ClassDecl>(decl));
|
||||
// We can only synthesize implicit constructors for classes and structs.
|
||||
if (!isa<ClassDecl>(decl) && !isa<StructDecl>(decl))
|
||||
return;
|
||||
|
||||
// Don't add constructors to imported Objective-C classes.
|
||||
if (decl->hasClangNode() && isa<ClassDecl>(decl))
|
||||
|
||||
@@ -42,23 +42,6 @@ LookupResult TypeChecker::lookupMember(Type type, Identifier name,
|
||||
|
||||
// Constructor lookup is special.
|
||||
if (name == Context.Id_init) {
|
||||
// For lookup into DynamicSelf, find the constructors of the underlying
|
||||
// self type.
|
||||
if (auto dynSelf = type->getAs<DynamicSelfType>()) {
|
||||
type = dynSelf->getSelfType();
|
||||
}
|
||||
|
||||
// Look through the metatype.
|
||||
if (auto metaTy = type->getAs<MetatypeType>())
|
||||
type = metaTy->getInstanceType();
|
||||
|
||||
// For nominal types, make sure we have the right constructors available.
|
||||
if (auto nominalDecl = type->getAnyNominal()) {
|
||||
// Define implicit default constructor for a struct/class.
|
||||
if (isa<StructDecl>(nominalDecl) || isa<ClassDecl>(nominalDecl))
|
||||
addImplicitConstructors(nominalDecl);
|
||||
}
|
||||
|
||||
// Fall through to look for constructors via the normal means.
|
||||
options = NL_Constructor;
|
||||
} else if (name.str().equals("__conversion")) {
|
||||
|
||||
@@ -443,6 +443,10 @@ public:
|
||||
validateDecl(VD, true);
|
||||
}
|
||||
|
||||
virtual void resolveImplicitConstructors(NominalTypeDecl *nominal) {
|
||||
addImplicitConstructors(nominal);
|
||||
}
|
||||
|
||||
/// Validate the signature of a generic function.
|
||||
///
|
||||
/// \param func The generic function.
|
||||
|
||||
Reference in New Issue
Block a user