Skip to content

Commit 6707f33

Browse files
authored
Merge pull request #59339 from eeckstein/improve-diagnose-inf-recursion
DiagnoseInfiniteRecursion: improve detection in (potentially) overridden class methods
2 parents a66f937 + 3e1cc7e commit 6707f33

File tree

2 files changed

+39
-14
lines changed

2 files changed

+39
-14
lines changed

lib/SILOptimizer/Mandatory/DiagnoseInfiniteRecursion.cpp

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -71,31 +71,50 @@ static bool isRecursiveCall(FullApplySite applySite) {
7171

7272
if (auto *CMI = dyn_cast<ClassMethodInst>(callee)) {
7373

74+
SILModule &module = parentFunc->getModule();
75+
CanType classType = CMI->getOperand()->getType().getASTType();
76+
if (auto mt = dyn_cast<MetatypeType>(classType)) {
77+
classType = mt.getInstanceType();
78+
}
79+
ClassDecl *classDecl = classType.getClassOrBoundGenericClass();
80+
7481
// FIXME: If we're not inside the module context of the method,
7582
// we may have to deserialize vtables. If the serialized tables
7683
// are damaged, the pass will crash.
7784
//
7885
// Though, this has the added bonus of not looking into vtables
7986
// outside the current module. Because we're not doing IPA, let
8087
// alone cross-module IPA, this is all well and good.
81-
SILModule &module = parentFunc->getModule();
82-
CanType classType = CMI->getOperand()->getType().getASTType();
83-
ClassDecl *classDecl = classType.getClassOrBoundGenericClass();
8488
if (classDecl && classDecl->getModuleContext() != module.getSwiftModule())
8589
return false;
8690

87-
if (!calleesAreStaticallyKnowable(module, CMI->getMember()))
91+
SILFunction *method = getTargetClassMethod(module, classDecl, CMI);
92+
if (method != parentFunc)
8893
return false;
8994

90-
// The "statically knowable" check just means that we have all the
91-
// callee candidates available for analysis. We still need to check
92-
// if the current function has a known override point.
93-
auto *methodDecl = CMI->getMember().getAbstractFunctionDecl();
94-
if (methodDecl->isOverridden())
95-
return false;
95+
SILDeclRef member = CMI->getMember();
96+
if (calleesAreStaticallyKnowable(module, member) &&
97+
// The "statically knowable" check just means that we have all the
98+
// callee candidates available for analysis. We still need to check
99+
// if the current function has a known override point.
100+
!member.getAbstractFunctionDecl()->isOverridden()) {
101+
return true;
102+
}
96103

97-
SILFunction *method = getTargetClassMethod(module, classDecl, CMI);
98-
return method == parentFunc;
104+
// Even if the method is (or could be) overridden, it's a recursive call if
105+
// it's called on the self argument:
106+
// ```
107+
// class X {
108+
// // Even if foo() is overridden in a derived class, it'll end up in an
109+
// // infinite recursion if initially called on an instance of `X`.
110+
// func foo() { foo() }
111+
// }
112+
// ```
113+
if (parentFunc->hasSelfParam() &&
114+
CMI->getOperand() == SILValue(parentFunc->getSelfArgument())) {
115+
return true;
116+
}
117+
return false;
99118
}
100119

101120
if (auto *WMI = dyn_cast<WitnessMethodInst>(callee)) {

test/SILOptimizer/infinite_recursion.swift

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -242,11 +242,11 @@ class S {
242242
return a() // expected-warning {{function call causes an infinite recursion}}
243243
}
244244

245-
func b() { // No warning - has a known override.
245+
func b() {
246246
var i = 0
247247
repeat {
248248
i += 1
249-
b()
249+
b() // expected-warning {{function call causes an infinite recursion}}
250250
} while (i > 5)
251251
}
252252

@@ -273,6 +273,12 @@ class T: S {
273273
}
274274
}
275275

276+
public class U {
277+
required convenience init(x: Int) {
278+
self.init(x: x) // expected-warning {{function call causes an infinite recursion}}
279+
}
280+
}
281+
276282
func == (l: S?, r: S?) -> Bool {
277283
if l == nil && r == nil { return true } // expected-warning {{function call causes an infinite recursion}}
278284
guard let l = l, let r = r else { return false }

0 commit comments

Comments
 (0)