Skip to content

Commit 4ddf6e5

Browse files
committed
Teach SuperclassDeclRequest to always diagnose circularity.
Rather than relying on clients to cope with the potential for circular inheritance of superclass declarations, teach SuperclassDeclRequest to establish whether circular inheritance has occurred and produce "null" in such cases. This allows other clients to avoid having to think about To benefit from this, have SuperclassTypeRequest evaluate SuperclassDeclRequest first and, if null, produce a Type(). This ensures that we don't get into an inconsistent situation where there is a superclass type but no superclass declaration.
1 parent 8857deb commit 4ddf6e5

File tree

8 files changed

+61
-25
lines changed

8 files changed

+61
-25
lines changed

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;

lib/AST/Decl.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4276,7 +4276,7 @@ bool ClassDecl::isIncompatibleWithWeakReferences() const {
42764276

42774277
bool ClassDecl::inheritsSuperclassInitializers() const {
42784278
// If there's no superclass, there's nothing to inherit.
4279-
if (!getSuperclass())
4279+
if (!getSuperclassDecl())
42804280
return false;
42814281

42824282
auto &ctx = getASTContext();

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/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/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: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
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
55

6-
class Left // expected-error {{circular reference}} expected-note {{through reference here}}
6+
class Left // expected-error {{'Left' inherits from itself}} expected-note {{through reference here}}
77
: Right.Hand { // expected-note {{through reference here}}
88
class Hand {}
99
}
1010

11-
class Right // expected-note 2 {{through reference here}}
11+
class Right // expected-note {{through reference here}} expected-note{{class 'Right' declared here}}
1212
: Left.Hand { // expected-note {{through reference here}}
1313
class Hand {}
1414
}
@@ -23,20 +23,19 @@ protocol P : P {} // expected-error {{protocol 'P' refines itself}}
2323
class Isomorphism : Automorphism { }
2424
class Automorphism : Automorphism { } // expected-error{{'Automorphism' inherits from itself}}
2525

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

2928
class Outer {
3029
class Inner : Outer {}
3130
}
3231

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

3635
class Inner {}
3736
}
3837

39-
class Outer3 // expected-error {{circular reference}} expected-note {{through reference here}}
38+
class Outer3 // expected-error {{'Outer3' inherits from itself}} expected-note {{through reference here}}
4039
: Outer3.Inner<Int> { // expected-note {{through reference here}}
4140
class Inner<T> {}
4241
}
@@ -54,27 +53,21 @@ class Outer3 // expected-error {{circular reference}} expected-note {{through re
5453

5554
protocol Initable {
5655
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'?}}
5956
}
6057

6158
protocol Shape : Circle {}
6259

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

6764
func crash() {
68-
Circle()
69-
// expected-error@-1 {{'Circle' cannot be constructed because it has no accessible initializers}}
65+
_ = Circle()
7066
}
7167

7268
// FIXME: We shouldn't emit the redundant "circular reference" diagnostics here.
7369
class WithDesignatedInit : WithDesignatedInit {
7470
// 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}}
7871

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

0 commit comments

Comments
 (0)