Skip to content

Commit 44c8052

Browse files
authored
Merge pull request #34123 from DougGregor/circular-inheritance-decl
Teach SuperclassDeclRequest to always diagnose circularity.
2 parents 4e633b6 + 2dc8a13 commit 44c8052

15 files changed

+71
-117
lines changed

include/swift/AST/Decl.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3920,9 +3920,6 @@ class ClassDecl final : public NominalTypeDecl {
39203920
/// Set the superclass of this class.
39213921
void setSuperclass(Type superclass);
39223922

3923-
/// Whether this class has a circular reference in its inheritance hierarchy.
3924-
bool hasCircularInheritance() const;
3925-
39263923
/// Walk this class and all of the superclasses of this class, transitively,
39273924
/// invoking the callback function for each class.
39283925
///

include/swift/AST/NameLookupRequests.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,10 @@ class SuperclassDeclRequest :
160160
evaluate(Evaluator &evaluator, NominalTypeDecl *subject) const;
161161

162162
public:
163+
// Cycle handling
164+
void diagnoseCycle(DiagnosticEngine &diags) const;
165+
void noteCycleStep(DiagnosticEngine &diags) const;
166+
163167
// Caching
164168
bool isCached() const { return true; }
165169
Optional<ClassDecl *> getCachedResult() const;

include/swift/AST/TypeCheckRequests.h

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1986,29 +1986,6 @@ class PreCheckFunctionBuilderRequest
19861986
bool isCached() const { return true; }
19871987
};
19881988

1989-
/// Computes whether a class has a circular reference in its inheritance
1990-
/// hierarchy.
1991-
class HasCircularInheritanceRequest
1992-
: public SimpleRequest<HasCircularInheritanceRequest, bool(ClassDecl *),
1993-
RequestFlags::Cached> {
1994-
public:
1995-
using SimpleRequest::SimpleRequest;
1996-
1997-
private:
1998-
friend SimpleRequest;
1999-
2000-
// Evaluation.
2001-
bool evaluate(Evaluator &evaluator, ClassDecl *decl) const;
2002-
2003-
public:
2004-
// Cycle handling.
2005-
void diagnoseCycle(DiagnosticEngine &diags) const;
2006-
void noteCycleStep(DiagnosticEngine &diags) const;
2007-
2008-
// Cached.
2009-
bool isCached() const { return true; }
2010-
};
2011-
20121989
/// Computes whether a protocol has a circular reference in its list of
20131990
/// inherited protocols.
20141991
class HasCircularInheritedProtocolsRequest

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,6 @@ SWIFT_REQUEST(TypeChecker, FunctionOperatorRequest, OperatorDecl *(FuncDecl *),
9595
SWIFT_REQUEST(NameLookup, GenericSignatureRequest,
9696
GenericSignature (GenericContext *),
9797
SeparatelyCached, NoLocationInfo)
98-
SWIFT_REQUEST(TypeChecker, HasCircularInheritanceRequest,
99-
bool(ClassDecl *), Cached, NoLocationInfo)
10098
SWIFT_REQUEST(TypeChecker, HasCircularInheritedProtocolsRequest,
10199
bool(ProtocolDecl *), Cached, NoLocationInfo)
102100
SWIFT_REQUEST(TypeChecker, HasCircularRawValueRequest,

lib/AST/Decl.cpp

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4148,13 +4148,6 @@ void NominalTypeDecl::synthesizeSemanticMembersIfNeeded(DeclName member) {
41484148
}
41494149
}
41504150

4151-
bool ClassDecl::hasCircularInheritance() const {
4152-
auto &ctx = getASTContext();
4153-
auto *mutableThis = const_cast<ClassDecl *>(this);
4154-
return evaluateOrDefault(ctx.evaluator,
4155-
HasCircularInheritanceRequest{mutableThis}, true);
4156-
}
4157-
41584151
ClassDecl::ClassDecl(SourceLoc ClassLoc, Identifier Name, SourceLoc NameLoc,
41594152
MutableArrayRef<TypeLoc> Inherited,
41604153
GenericParamList *GenericParams, DeclContext *Parent)
@@ -4276,7 +4269,7 @@ bool ClassDecl::isIncompatibleWithWeakReferences() const {
42764269

42774270
bool ClassDecl::inheritsSuperclassInitializers() const {
42784271
// If there's no superclass, there's nothing to inherit.
4279-
if (!getSuperclass())
4272+
if (!getSuperclassDecl())
42804273
return false;
42814274

42824275
auto &ctx = getASTContext();
@@ -4294,19 +4287,11 @@ AncestryOptions ClassDecl::checkAncestry() const {
42944287
AncestryFlags
42954288
ClassAncestryFlagsRequest::evaluate(Evaluator &evaluator,
42964289
ClassDecl *value) const {
4297-
llvm::SmallPtrSet<const ClassDecl *, 8> visited;
4298-
42994290
AncestryOptions result;
43004291
const ClassDecl *CD = value;
43014292
auto *M = value->getParentModule();
43024293

43034294
do {
4304-
// If we hit circularity, we will diagnose at some point in typeCheckDecl().
4305-
// However we have to explicitly guard against that here because we get
4306-
// called as part of the interface type request.
4307-
if (!visited.insert(CD).second)
4308-
break;
4309-
43104295
if (CD->isGenericContext())
43114296
result |= AncestryFlags::Generic;
43124297

@@ -4496,10 +4481,9 @@ ClassDecl::findImplementingMethod(const AbstractFunctionDecl *Method) const {
44964481
bool ClassDecl::walkSuperclasses(
44974482
llvm::function_ref<TypeWalker::Action(ClassDecl *)> fn) const {
44984483

4499-
SmallPtrSet<ClassDecl *, 8> seen;
45004484
auto *cls = const_cast<ClassDecl *>(this);
45014485

4502-
while (cls && seen.insert(cls).second) {
4486+
while (cls) {
45034487
switch (fn(cls)) {
45044488
case TypeWalker::Action::Stop:
45054489
return true;

lib/AST/NameLookup.cpp

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2290,12 +2290,33 @@ SuperclassDeclRequest::evaluate(Evaluator &evaluator,
22902290
inheritedTypes, modulesFound, anyObject);
22912291

22922292
// Look for a class declaration.
2293+
ClassDecl *superclass = nullptr;
22932294
for (auto inheritedNominal : inheritedNominalTypes) {
2294-
if (auto classDecl = dyn_cast<ClassDecl>(inheritedNominal))
2295-
return classDecl;
2295+
if (auto classDecl = dyn_cast<ClassDecl>(inheritedNominal)) {
2296+
superclass = classDecl;
2297+
break;
2298+
}
22962299
}
2297-
}
22982300

2301+
// If we found a superclass, ensure that we don't have a circular
2302+
// inheritance hierarchy by evaluating its superclass. This forces the
2303+
// diagnostic at this point and then suppresses the superclass failure.
2304+
if (superclass) {
2305+
auto result = Ctx.evaluator(SuperclassDeclRequest{superclass});
2306+
bool hadCycle = false;
2307+
if (auto err = result.takeError()) {
2308+
llvm::handleAllErrors(std::move(err),
2309+
[&hadCycle](const CyclicalRequestError<SuperclassDeclRequest> &E) {
2310+
hadCycle = true;
2311+
});
2312+
2313+
if (hadCycle)
2314+
return nullptr;
2315+
}
2316+
2317+
return superclass;
2318+
}
2319+
}
22992320

23002321
return nullptr;
23012322
}

lib/AST/NameLookupRequests.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,19 @@ SourceLoc InheritedDeclsReferencedRequest::getNearestLoc() const {
4545
//----------------------------------------------------------------------------//
4646
// Superclass declaration computation.
4747
//----------------------------------------------------------------------------//
48+
void SuperclassDeclRequest::diagnoseCycle(DiagnosticEngine &diags) const {
49+
// FIXME: Improve this diagnostic.
50+
auto nominalDecl = std::get<0>(getStorage());
51+
diags.diagnose(nominalDecl, diag::circular_class_inheritance,
52+
nominalDecl->getName());
53+
}
54+
55+
void SuperclassDeclRequest::noteCycleStep(DiagnosticEngine &diags) const {
56+
auto decl = std::get<0>(getStorage());
57+
diags.diagnose(decl, diag::kind_declname_declared_here,
58+
decl->getDescriptiveKind(), decl->getName());
59+
}
60+
4861
Optional<ClassDecl *> SuperclassDeclRequest::getCachedResult() const {
4962
auto nominalDecl = std::get<0>(getStorage());
5063

lib/AST/TypeCheckRequests.cpp

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1136,23 +1136,6 @@ void swift::simple_display(llvm::raw_ostream &out,
11361136
}
11371137
}
11381138

1139-
//----------------------------------------------------------------------------//
1140-
// HasCircularInheritanceRequest computation.
1141-
//----------------------------------------------------------------------------//
1142-
1143-
void HasCircularInheritanceRequest::diagnoseCycle(
1144-
DiagnosticEngine &diags) const {
1145-
auto *decl = std::get<0>(getStorage());
1146-
diags.diagnose(decl, diag::circular_class_inheritance, decl->getName());
1147-
}
1148-
1149-
void HasCircularInheritanceRequest::noteCycleStep(
1150-
DiagnosticEngine &diags) const {
1151-
auto *decl = std::get<0>(getStorage());
1152-
diags.diagnose(decl, diag::kind_declname_declared_here,
1153-
decl->getDescriptiveKind(), decl->getName());
1154-
}
1155-
11561139
//----------------------------------------------------------------------------//
11571140
// HasCircularInheritedProtocolsRequest computation.
11581141
//----------------------------------------------------------------------------//

lib/SILGen/SILGen.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1288,8 +1288,8 @@ bool SILGenModule::requiresIVarDestroyer(ClassDecl *cd) {
12881288
// Only needed if we have non-trivial ivars, we're not a root class, and
12891289
// the superclass is not @objc.
12901290
return (hasNonTrivialIVars(cd) &&
1291-
cd->getSuperclass() &&
1292-
!cd->getSuperclass()->getClassOrBoundGenericClass()->hasClangNode());
1291+
cd->getSuperclassDecl() &&
1292+
!cd->getSuperclassDecl()->hasClangNode());
12931293
}
12941294

12951295
/// TODO: This needs a better name.

lib/Sema/CodeSynthesis.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1076,12 +1076,11 @@ InheritsSuperclassInitializersRequest::evaluate(Evaluator &eval,
10761076
if (decl->getAttrs().hasAttribute<InheritsConvenienceInitializersAttr>())
10771077
return true;
10781078

1079-
auto superclass = decl->getSuperclass();
1080-
assert(superclass);
1079+
auto superclassDecl = decl->getSuperclassDecl();
1080+
assert(superclassDecl);
10811081

10821082
// If the superclass has known-missing designated initializers, inheriting
10831083
// is unsafe.
1084-
auto *superclassDecl = superclass->getClassOrBoundGenericClass();
10851084
if (superclassDecl->getModuleContext() != decl->getParentModule() &&
10861085
superclassDecl->hasMissingDesignatedInitializers())
10871086
return false;
@@ -1357,7 +1356,7 @@ HasDefaultInitRequest::evaluate(Evaluator &evaluator,
13571356
// Don't synthesize a default for a subclass, it will attempt to inherit its
13581357
// initializers from its superclass.
13591358
if (auto *cd = dyn_cast<ClassDecl>(decl))
1360-
if (cd->getSuperclass())
1359+
if (cd->getSuperclassDecl())
13611360
return false;
13621361

13631362
// If the user has already defined a designated initializer, then don't

lib/Sema/DerivedConformanceCodable.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,15 @@ using namespace swift;
3030
/// Returns whether the type represented by the given ClassDecl inherits from a
3131
/// type which conforms to the given protocol.
3232
static bool superclassConformsTo(ClassDecl *target, KnownProtocolKind kpk) {
33-
if (!target || !target->getSuperclass() || target->hasCircularInheritance()) {
33+
if (!target) {
3434
return false;
3535
}
36-
return !target->getSuperclassDecl()
36+
37+
auto superclass = target->getSuperclassDecl();
38+
if (!superclass)
39+
return false;
40+
41+
return !superclass
3742
->getModuleContext()
3843
->lookupConformance(target->getSuperclass(),
3944
target->getASTContext().getProtocol(kpk))

lib/Sema/TypeCheckDecl.cpp

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -233,24 +233,6 @@ static bool canSkipCircularityCheck(NominalTypeDecl *decl) {
233233
return decl->hasClangNode() || decl->wasDeserialized();
234234
}
235235

236-
bool
237-
HasCircularInheritanceRequest::evaluate(Evaluator &evaluator,
238-
ClassDecl *decl) const {
239-
if (canSkipCircularityCheck(decl) || !decl->hasSuperclass())
240-
return false;
241-
242-
auto *superclass = decl->getSuperclassDecl();
243-
auto result = evaluator(HasCircularInheritanceRequest{superclass});
244-
245-
// If we have a cycle, handle it and return true.
246-
if (!result) {
247-
using Error = CyclicalRequestError<HasCircularInheritanceRequest>;
248-
llvm::handleAllErrors(result.takeError(), [](const Error &E) {});
249-
return true;
250-
}
251-
return result.get();
252-
}
253-
254236
bool
255237
HasCircularInheritedProtocolsRequest::evaluate(Evaluator &evaluator,
256238
ProtocolDecl *decl) const {

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2011,7 +2011,7 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
20112011
checkGenericParams(CD);
20122012

20132013
// Check for circular inheritance.
2014-
(void)CD->hasCircularInheritance();
2014+
(void)CD->getSuperclassDecl();
20152015

20162016
// Force lowering of stored properties.
20172017
(void) CD->getStoredProperties();

lib/Sema/TypeCheckRequestFunctions.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,12 @@ SuperclassTypeRequest::evaluate(Evaluator &evaluator,
9393
return proto->getGenericSignature()
9494
->getSuperclassBound(proto->getSelfInterfaceType());
9595
}
96+
97+
if (!proto->getSuperclassDecl())
98+
return Type();
99+
} else if (auto classDecl = dyn_cast<ClassDecl>(nominalDecl)) {
100+
if (!classDecl->getSuperclassDecl())
101+
return Type();
96102
}
97103

98104
for (unsigned int idx : indices(nominalDecl->getInherited())) {

test/decl/class/circular_inheritance.swift

Lines changed: 9 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@
22
// RUN: mkdir -p %t/stats-dir
33
// RUN: %target-typecheck-verify-swift
44
// RUN: not %target-swift-frontend -typecheck -debug-cycles %s -build-request-dependency-graph -output-request-graphviz %t.dot -stats-output-dir %t/stats-dir 2> %t.cycles
5+
// RUN: %FileCheck -check-prefix CHECK-DOT %s < %t.dot
56

6-
class Left // expected-error {{circular reference}} expected-note {{through reference here}}
7+
class Left // expected-error {{'Left' inherits from itself}} expected-note {{through reference here}}
78
: Right.Hand { // expected-note {{through reference here}}
89
class Hand {}
910
}
1011

11-
class Right // expected-note 2 {{through reference here}}
12+
class Right // expected-note {{through reference here}} expected-note{{class 'Right' declared here}}
1213
: Left.Hand { // expected-note {{through reference here}}
1314
class Hand {}
1415
}
@@ -23,58 +24,42 @@ protocol P : P {} // expected-error {{protocol 'P' refines itself}}
2324
class Isomorphism : Automorphism { }
2425
class Automorphism : Automorphism { } // expected-error{{'Automorphism' inherits from itself}}
2526

26-
// FIXME: Useless error
27-
let _ = A() // expected-error{{'A' cannot be constructed because it has no accessible initializers}}
27+
let _ = A()
2828

2929
class Outer {
3030
class Inner : Outer {}
3131
}
3232

33-
class Outer2 // expected-error {{circular reference}} expected-note {{through reference here}}
33+
class Outer2 // expected-error {{'Outer2' inherits from itself}} expected-note {{through reference here}}
3434
: Outer2.Inner { // expected-note {{through reference here}}
3535

3636
class Inner {}
3737
}
3838

39-
class Outer3 // expected-error {{circular reference}} expected-note {{through reference here}}
39+
class Outer3 // expected-error {{'Outer3' inherits from itself}} expected-note {{through reference here}}
4040
: Outer3.Inner<Int> { // expected-note {{through reference here}}
4141
class Inner<T> {}
4242
}
4343

44-
// CHECK: ===CYCLE DETECTED===
45-
// CHECK-LABEL: `--{{.*}}HasCircularInheritanceRequest(circular_inheritance.(file).Left@
46-
// CHECK-NEXT: `--{{.*}}SuperclassDeclRequest({{.*Left}}
47-
// CHECK: `--{{.*}}InheritedDeclsReferencedRequest(circular_inheritance.(file).Left@
48-
// CHECK: `--{{.*}}SuperclassDeclRequest
49-
// CHECK: `--{{.*}}InheritedDeclsReferencedRequest(circular_inheritance.(file).Right@
50-
// CHECK: `--{{.*}}SuperclassDeclRequest{{.*(cyclic dependency)}}
51-
5244
// CHECK-DOT: digraph Dependencies
5345
// CHECK-DOT: label="InheritedTypeRequest
5446

5547
protocol Initable {
5648
init()
57-
// expected-note@-1 {{protocol requires initializer 'init()' with type '()'; do you want to add a stub?}}
58-
// expected-note@-2 {{did you mean 'init'?}}
5949
}
6050

6151
protocol Shape : Circle {}
6252

6353
class Circle : Initable & Circle {}
6454
// expected-error@-1 {{'Circle' inherits from itself}}
65-
// expected-error@-2 {{type 'Circle' does not conform to protocol 'Initable'}}
55+
// expected-error@-2 {{initializer requirement 'init()' can only be satisfied by a 'required' initializer in non-final class 'Circle'}}
6656

6757
func crash() {
68-
Circle()
69-
// expected-error@-1 {{'Circle' cannot be constructed because it has no accessible initializers}}
58+
_ = Circle()
7059
}
7160

72-
// FIXME: We shouldn't emit the redundant "circular reference" diagnostics here.
7361
class WithDesignatedInit : WithDesignatedInit {
7462
// expected-error@-1 {{'WithDesignatedInit' inherits from itself}}
75-
// expected-error@-2 {{circular reference}}
76-
// expected-note@-3 {{through reference here}}
77-
// expected-note@-4 2 {{through reference here}}
7863

79-
init(x: Int) {} // expected-error {{circular reference}}
64+
init(x: Int) {}
8065
}

0 commit comments

Comments
 (0)