Skip to content

Commit b062a25

Browse files
committed
[Sema] Requesify inheritsSuperclassInitializers
This commit introduces a request for computing whether a class inherits both designated and convenience initializers from its superclass. The shared logic of finding initializers which the subclass hasn't overriden has been factored out into `collectNonOveriddenSuperclassInits`.
1 parent 660c162 commit b062a25

File tree

7 files changed

+114
-62
lines changed

7 files changed

+114
-62
lines changed

include/swift/AST/Decl.h

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -527,13 +527,10 @@ class alignas(1 << DeclAlignInBits) Decl {
527527
NumRequirementsInSignature : 16
528528
);
529529

530-
SWIFT_INLINE_BITFIELD(ClassDecl, NominalTypeDecl, 2+1+2+1+7+1+1+1+1+1+1,
530+
SWIFT_INLINE_BITFIELD(ClassDecl, NominalTypeDecl, 2+2+1+7+1+1+1+1+1+1,
531531
/// The stage of the inheritance circularity check for this class.
532532
Circularity : 2,
533533

534-
/// Whether this class inherits its superclass's convenience initializers.
535-
InheritsSuperclassInits : 1,
536-
537534
/// \see ClassDecl::ForeignKind
538535
RawForeignKind : 2,
539536

@@ -3970,15 +3967,6 @@ class ClassDecl final : public NominalTypeDecl {
39703967
/// from its superclass.
39713968
bool inheritsSuperclassInitializers();
39723969

3973-
/// Marks that this class inherits convenience initializers from its
3974-
/// superclass.
3975-
///
3976-
/// This is computed as part of adding implicit initializers.
3977-
void setInheritsSuperclassInitializers() {
3978-
assert(addedImplicitInitializers());
3979-
Bits.ClassDecl.InheritsSuperclassInits = true;
3980-
}
3981-
39823970
/// Walks the class hierarchy starting from this class, checking various
39833971
/// conditions.
39843972
AncestryOptions checkAncestry() const;

include/swift/AST/TypeCheckRequests.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1627,6 +1627,25 @@ class SynthesizeDefaultInitRequest
16271627
bool isCached() const { return true; }
16281628
};
16291629

1630+
/// Checks whether this declaration inherits its superclass' designated and
1631+
/// convenience initializers.
1632+
class InheritsSuperclassInitializersRequest
1633+
: public SimpleRequest<InheritsSuperclassInitializersRequest,
1634+
bool(ClassDecl *), CacheKind::Cached> {
1635+
public:
1636+
using SimpleRequest::SimpleRequest;
1637+
1638+
private:
1639+
friend SimpleRequest;
1640+
1641+
// Evaluation.
1642+
llvm::Expected<bool> evaluate(Evaluator &evaluator, ClassDecl *decl) const;
1643+
1644+
public:
1645+
// Caching.
1646+
bool isCached() const { return true; }
1647+
};
1648+
16301649
// Allow AnyValue to compare two Type values, even though Type doesn't
16311650
// support ==.
16321651
template<>

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,3 +177,5 @@ SWIFT_REQUEST(TypeChecker, HasDefaultInitRequest,
177177
bool(NominalTypeDecl *), Cached, NoLocationInfo)
178178
SWIFT_REQUEST(TypeChecker, SynthesizeDefaultInitRequest,
179179
ConstructorDecl *(NominalTypeDecl *), Cached, NoLocationInfo)
180+
SWIFT_REQUEST(TypeChecker, InheritsSuperclassInitializersRequest,
181+
bool(ClassDecl *), Cached, NoLocationInfo)

lib/AST/Decl.cpp

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3885,7 +3885,6 @@ ClassDecl::ClassDecl(SourceLoc ClassLoc, Identifier Name, SourceLoc NameLoc,
38853885
ClassLoc(ClassLoc) {
38863886
Bits.ClassDecl.Circularity
38873887
= static_cast<unsigned>(CircularityCheck::Unchecked);
3888-
Bits.ClassDecl.InheritsSuperclassInits = 0;
38893888
Bits.ClassDecl.RawForeignKind = 0;
38903889
Bits.ClassDecl.HasMissingDesignatedInitializers = 0;
38913890
Bits.ClassDecl.ComputedHasMissingDesignatedInitializers = 0;
@@ -4001,31 +4000,16 @@ bool ClassDecl::isIncompatibleWithWeakReferences() const {
40014000
}
40024001

40034002
bool ClassDecl::inheritsSuperclassInitializers() {
4004-
// Check whether we already have a cached answer.
4005-
if (addedImplicitInitializers())
4006-
return Bits.ClassDecl.InheritsSuperclassInits;
4007-
40084003
// If there's no superclass, there's nothing to inherit.
4009-
ClassDecl *superclassDecl;
4010-
if (!(superclassDecl = getSuperclassDecl())) {
4011-
setAddedImplicitInitializers();
4012-
return false;
4013-
}
4014-
4015-
// If the superclass has known-missing designated initializers, inheriting
4016-
// is unsafe.
4017-
if (superclassDecl->hasMissingDesignatedInitializers())
4004+
if (!getSuperclass())
40184005
return false;
40194006

4020-
// Otherwise, do all the work of resolving constructors, which will also
4021-
// calculate the right answer.
4022-
if (auto *resolver = getASTContext().getLazyResolver())
4023-
resolver->resolveImplicitConstructors(this);
4024-
4025-
return Bits.ClassDecl.InheritsSuperclassInits;
4007+
auto &ctx = getASTContext();
4008+
auto *mutableThis = const_cast<ClassDecl *>(this);
4009+
return evaluateOrDefault(
4010+
ctx.evaluator, InheritsSuperclassInitializersRequest{mutableThis}, false);
40264011
}
40274012

4028-
40294013
AncestryOptions ClassDecl::checkAncestry() const {
40304014
return AncestryOptions(evaluateOrDefault(getASTContext().evaluator,
40314015
ClassAncestryFlagsRequest{const_cast<ClassDecl *>(this)},

lib/Sema/CodeSynthesis.cpp

Lines changed: 75 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -864,6 +864,48 @@ static bool hasUserDefinedDesignatedInit(Evaluator &eval,
864864
false);
865865
}
866866

867+
static bool canInheritDesignatedInits(Evaluator &eval, ClassDecl *decl) {
868+
// We can only inherit designated initializers if the user hasn't defined
869+
// a designated init of their own, and all the stored properties have initial
870+
// values.
871+
return !hasUserDefinedDesignatedInit(eval, decl) &&
872+
areAllStoredPropertiesDefaultInitializable(eval, decl);
873+
}
874+
875+
static void collectNonOveriddenSuperclassInits(
876+
ClassDecl *subclass, SmallVectorImpl<ConstructorDecl *> &results) {
877+
auto superclassTy = subclass->getSuperclass();
878+
assert(superclassTy);
879+
880+
// Record all of the initializers the subclass has overriden, excluding stub
881+
// overrides, which we don't want to consider as viable delegates for
882+
// convenience inits.
883+
llvm::SmallPtrSet<ConstructorDecl *, 4> overriddenInits;
884+
for (auto member : subclass->getMembers())
885+
if (auto ctor = dyn_cast<ConstructorDecl>(member))
886+
if (!ctor->hasStubImplementation())
887+
if (auto overridden = ctor->getOverriddenDecl())
888+
overriddenInits.insert(overridden);
889+
890+
auto superclassCtors = TypeChecker::lookupConstructors(
891+
subclass, superclassTy, NameLookupFlags::IgnoreAccessControl);
892+
893+
for (auto memberResult : superclassCtors) {
894+
auto superclassCtor = cast<ConstructorDecl>(memberResult.getValueDecl());
895+
896+
// Skip invalid superclass initializers.
897+
if (superclassCtor->isInvalid())
898+
continue;
899+
900+
// Skip unavailable superclass initializers.
901+
if (AvailableAttr::isUnavailable(superclassCtor))
902+
continue;
903+
904+
if (!overriddenInits.count(superclassCtor))
905+
results.push_back(superclassCtor);
906+
}
907+
}
908+
867909
/// For a class with a superclass, automatically define overrides
868910
/// for all of the superclass's designated initializers.
869911
static void addImplicitInheritedConstructorsToClass(ClassDecl *decl) {
@@ -944,25 +986,11 @@ static void addImplicitInheritedConstructorsToClass(ClassDecl *decl) {
944986

945987
bool canInheritInitializers = defaultInitable && !foundDesignatedInit;
946988

947-
bool canInheritConvenienceInitalizers =
948-
!superclassDecl->hasMissingDesignatedInitializers();
949989
SmallVector<ConstructorDecl *, 4> requiredConvenienceInitializers;
950-
for (auto memberResult : ctors) {
951-
auto member = memberResult.getValueDecl();
952-
953-
// Skip unavailable superclass initializers.
954-
if (AvailableAttr::isUnavailable(member))
955-
continue;
956-
957-
// Skip invalid superclass initializers.
958-
auto superclassCtor = dyn_cast<ConstructorDecl>(member);
959-
if (superclassCtor->isInvalid())
960-
continue;
961-
962-
// If we have an override for this constructor, it's okay.
963-
if (overriddenInits.count(superclassCtor) > 0)
964-
continue;
990+
SmallVector<ConstructorDecl *, 4> nonOverridenSuperclassCtors;
991+
collectNonOveriddenSuperclassInits(decl, nonOverridenSuperclassCtors);
965992

993+
for (auto *superclassCtor : nonOverridenSuperclassCtors) {
966994
// We only care about required or designated initializers.
967995
if (!superclassCtor->isDesignatedInit()) {
968996
if (superclassCtor->isRequired()) {
@@ -973,10 +1001,6 @@ static void addImplicitInheritedConstructorsToClass(ClassDecl *decl) {
9731001
continue;
9741002
}
9751003

976-
// Otherwise, it may no longer be safe to inherit convenience
977-
// initializers.
978-
canInheritConvenienceInitalizers &= canInheritInitializers;
979-
9801004
// Everything after this is only relevant for Swift classes being defined.
9811005
if (decl->hasClangNode())
9821006
continue;
@@ -1028,12 +1052,38 @@ static void addImplicitInheritedConstructorsToClass(ClassDecl *decl) {
10281052
}
10291053
}
10301054

1031-
if (canInheritConvenienceInitalizers) {
1032-
decl->setInheritsSuperclassInitializers();
1033-
} else {
1055+
if (!decl->inheritsSuperclassInitializers())
10341056
for (ConstructorDecl *requiredCtor : requiredConvenienceInitializers)
10351057
diagnoseMissingRequiredInitializer(decl, requiredCtor, ctx);
1036-
}
1058+
}
1059+
1060+
llvm::Expected<bool>
1061+
InheritsSuperclassInitializersRequest::evaluate(Evaluator &eval,
1062+
ClassDecl *decl) const {
1063+
auto superclass = decl->getSuperclass();
1064+
assert(superclass);
1065+
1066+
// If the superclass has known-missing designated initializers, inheriting
1067+
// is unsafe.
1068+
auto *superclassDecl = superclass->getClassOrBoundGenericClass();
1069+
if (superclassDecl->hasMissingDesignatedInitializers())
1070+
return false;
1071+
1072+
// If we're allowed to inherit designated initializers, then we can inherit
1073+
// convenience inits too.
1074+
if (canInheritDesignatedInits(eval, decl))
1075+
return true;
1076+
1077+
// Otherwise we need to check whether the user has overriden all of the
1078+
// superclass' designed inits.
1079+
SmallVector<ConstructorDecl *, 4> nonOverridenSuperclassCtors;
1080+
collectNonOveriddenSuperclassInits(decl, nonOverridenSuperclassCtors);
1081+
1082+
auto allDesignatedInitsOverriden =
1083+
llvm::none_of(nonOverridenSuperclassCtors, [](ConstructorDecl *ctor) {
1084+
return ctor->isDesignatedInit();
1085+
});
1086+
return allDesignatedInitsOverriden;
10371087
}
10381088

10391089
static bool shouldAttemptInitializerSynthesis(const NominalTypeDecl *decl) {

lib/Serialization/Deserialization.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3409,8 +3409,8 @@ class swift::DeclDeserializer {
34093409
theClass->setImplicit();
34103410
theClass->setIsObjC(isObjC);
34113411
theClass->setSuperclass(MF.getType(superclassID));
3412-
if (inheritsSuperclassInitializers)
3413-
theClass->setInheritsSuperclassInitializers();
3412+
ctx.evaluator.cacheOutput(InheritsSuperclassInitializersRequest{theClass},
3413+
std::move(inheritsSuperclassInitializers));
34143414

34153415
handleInherited(theClass,
34163416
rawInheritedAndDependencyIDs.slice(0, numInheritedTypes));

test/decl/class/circular_inheritance.swift

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
// RUN: %FileCheck -check-prefix CHECK-DOT %s < %t.dot
77

88
// Check that we produced superclass type requests.
9-
// RUN: %{python} %utils/process-stats-dir.py --evaluate 'SuperclassTypeRequest == 17' %t/stats-dir
9+
// RUN: %{python} %utils/process-stats-dir.py --evaluate 'SuperclassTypeRequest == 18' %t/stats-dir
1010

1111
class Left // expected-error {{circular reference}}
1212
: Right.Hand { // expected-note {{through reference here}}
@@ -72,3 +72,12 @@ func crash() {
7272
Circle()
7373
// expected-error@-1 {{'Circle' cannot be constructed because it has no accessible initializers}}
7474
}
75+
76+
// FIXME: We shouldn't emit the redundant "circular reference" diagnostics here.
77+
class WithDesignatedInit : WithDesignatedInit {
78+
// expected-error@-1 {{'WithDesignatedInit' inherits from itself}}
79+
// expected-error@-2 {{circular reference}}
80+
// expected-note@-3 {{through reference here}}
81+
82+
init(x: Int) {} // expected-error {{circular reference}}
83+
}

0 commit comments

Comments
 (0)