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:
Doug Gregor
2014-03-01 00:51:21 +00:00
parent e4444e36cb
commit d92f1a3158
11 changed files with 180 additions and 46 deletions

View File

@@ -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; }

View File

@@ -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;
};

View File

@@ -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

View File

@@ -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) {

View File

@@ -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);
}

View File

@@ -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))

View File

@@ -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>() &&

View File

@@ -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>();

View File

@@ -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))

View File

@@ -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")) {

View File

@@ -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.