Skip to content

Commit 6b60db3

Browse files
manavgabhawalaManav Gabhawala
authored andcommitted
[AST][Sema] Fixes the IterativeTypeChecker and better manages circular protocol inheritance
The IterativeTypeChecker now use loops instead of recursion to help keep the stack size low We diagnose circular dependencies for protocols in a more efficient manner and also prevent the possibility of infinite loops
1 parent 7fcf1db commit 6b60db3

File tree

4 files changed

+50
-64
lines changed

4 files changed

+50
-64
lines changed

lib/AST/ConformanceLookupTable.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,7 @@ bool ConformanceLookupTable::addProtocol(NominalTypeDecl *nominal,
445445

446446
// If this entry is synthesized or implied, scan to determine
447447
// whether there are any explicit better conformances that make this
448-
// conformance trivially superseded (and, therefore, no worth
448+
// conformance trivially superseded (and, therefore, not worth
449449
// recording).
450450
auto &conformanceEntries = Conformances[protocol];
451451
if (kind == ConformanceEntryKind::Implied ||
@@ -458,7 +458,9 @@ bool ConformanceLookupTable::addProtocol(NominalTypeDecl *nominal,
458458

459459
case ConformanceEntryKind::Implied:
460460
// An implied conformance is better than a synthesized one.
461-
if (kind == ConformanceEntryKind::Synthesized)
461+
// Ignore implied circular protocol inheritance
462+
if (kind == ConformanceEntryKind::Synthesized ||
463+
existingEntry->getProtocol() == protocol)
462464
return false;
463465
break;
464466

lib/Sema/ITCDecl.cpp

Lines changed: 15 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ void IterativeTypeChecker::processInheritedProtocols(
220220
// FIXME: Technically, we only need very basic name binding.
221221
auto inheritedClause = protocol->getInherited();
222222
bool anyDependencies = false;
223+
bool diagnosedCircularity = false;
223224
llvm::SmallSetVector<ProtocolDecl *, 4> allProtocols;
224225
for (unsigned i = 0, n = inheritedClause.size(); i != n; ++i) {
225226
TypeLoc &inherited = inheritedClause[i];
@@ -235,43 +236,32 @@ void IterativeTypeChecker::processInheritedProtocols(
235236
// FIXME: We'd prefer to keep what the user wrote here.
236237
SmallVector<ProtocolDecl *, 4> protocols;
237238
if (inherited.getType()->isExistentialType(protocols)) {
238-
allProtocols.insert(protocols.begin(), protocols.end());
239-
continue;
239+
for (auto inheritedProtocol: protocols) {
240+
if (inheritedProtocol == protocol ||
241+
inheritedProtocol->inheritsFrom(protocol)) {
242+
if (!diagnosedCircularity &&
243+
!protocol->isInheritedProtocolsValid()) {
244+
diagnose(protocol,
245+
diag::circular_protocol_def, protocol->getName().str())
246+
.fixItRemove(inherited.getSourceRange());
247+
diagnosedCircularity = true;
248+
}
249+
continue;
250+
}
251+
allProtocols.insert(inheritedProtocol);
252+
}
240253
}
241254
}
242255

243256
// If we enumerated any dependencies, we can't complete this request.
244257
if (anyDependencies)
245258
return;
246259

247-
// Check for circular inheritance.
248-
// FIXME: The diagnostics here should be improved... and this should probably
249-
// be handled by the normal cycle detection.
250-
bool diagnosedCircularity = false;
251-
for (unsigned i = 0, n = allProtocols.size(); i != n; /*in loop*/) {
252-
if (allProtocols[i] == protocol ||
253-
allProtocols[i]->inheritsFrom(protocol)) {
254-
if (!diagnosedCircularity &&
255-
!protocol->isInheritedProtocolsValid()) {
256-
diagnose(protocol,
257-
diag::circular_protocol_def, protocol->getName().str());
258-
diagnosedCircularity = true;
259-
}
260-
261-
allProtocols.remove(allProtocols[i]);
262-
--n;
263-
continue;
264-
}
265-
266-
++i;
267-
}
268-
269260
// FIXME: Hack to deal with recursion elsewhere.
270261
// We recurse through DeclContext::getLocalProtocols() -- this should be
271262
// redone to use the IterativeDeclChecker also.
272263
if (protocol->isInheritedProtocolsValid())
273264
return;
274-
275265
protocol->setInheritedProtocols(getASTContext().AllocateCopy(allProtocols));
276266
}
277267

lib/Sema/IterativeTypeChecker.cpp

Lines changed: 30 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -66,49 +66,43 @@ bool IterativeTypeChecker::breakCycle(TypeCheckRequest request) {
6666
}
6767

6868
void IterativeTypeChecker::satisfy(TypeCheckRequest request) {
69-
// If the request has already been satisfied, we're done.
70-
if (isSatisfied(request)) return;
71-
72-
// Check for circular dependencies in our requests.
73-
// FIXME: This stack operation is painfully inefficient.
74-
auto existingRequest = std::find(ActiveRequests.rbegin(),
75-
ActiveRequests.rend(),
76-
request);
77-
if (existingRequest != ActiveRequests.rend()) {
78-
auto first = existingRequest.base();
79-
--first;
80-
diagnoseCircularReference(llvm::makeArrayRef(&*first,
81-
&*ActiveRequests.end()));
82-
return;
83-
}
84-
85-
// Add this request to the stack of active requests.
86-
ActiveRequests.push_back(request);
87-
defer { ActiveRequests.pop_back(); };
88-
89-
while (true) {
69+
70+
auto startingSize = ActiveRequests.size();
71+
72+
auto addToActiveRequests = [&](TypeCheckRequest request) {
73+
// Check for circular dependencies in our requests.
74+
auto existingRequest = std::find(ActiveRequests.rbegin(),
75+
ActiveRequests.rend(),
76+
request);
77+
if (existingRequest != ActiveRequests.rend()) {
78+
auto first = existingRequest.base();
79+
--first;
80+
diagnoseCircularReference(llvm::makeArrayRef(&*first,
81+
&*ActiveRequests.end()));
82+
return;
83+
}
84+
// Add this request to the stack of active requests.
85+
ActiveRequests.push_back(request);
86+
};
87+
88+
addToActiveRequests(request);
89+
90+
while (ActiveRequests.size() != startingSize) {
91+
request = ActiveRequests.back();
92+
// If the request has already been satisfied, we're done.
93+
if (isSatisfied(request)) {
94+
ActiveRequests.pop_back();
95+
continue;
96+
}
97+
9098
// Process this requirement, enumerating dependencies if anything else needs
9199
// to be handled first.
92-
SmallVector<TypeCheckRequest, 4> unsatisfied;
93100
process(request, [&](TypeCheckRequest dependency) -> bool {
94101
if (isSatisfied(dependency)) return false;
95-
96102
// Record the unsatisfied dependency.
97-
unsatisfied.push_back(dependency);
103+
addToActiveRequests(dependency);
98104
return true;
99105
});
100-
101-
// If there were no unsatisfied dependencies, we're done.
102-
if (unsatisfied.empty()) {
103-
assert(isSatisfied(request));
104-
break;
105-
}
106-
107-
// Recurse to satisfy any unsatisfied dependencies.
108-
// FIXME: Don't recurse in the iterative type checker, silly!
109-
for (auto dependency : unsatisfied) {
110-
satisfy(dependency);
111-
}
112106
}
113107
}
114108

validation-test/compiler_crashers/10659-swift-printingdiagnosticconsumer-handlediagnostic.timeout.swift renamed to validation-test/compiler_crashers_fixed/10659-swift-printingdiagnosticconsumer-handlediagnostic.timeout.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
// See http://swift.org/LICENSE.txt for license information
66
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
77

8-
// RUN: not --crash %target-swift-frontend %s -parse
8+
// RUN: not %target-swift-frontend %s -parse
99
// REQUIRES: asserts
1010
protocol c:A
1111
protocol A:d

0 commit comments

Comments
 (0)