Skip to content

Commit 3918fb0

Browse files
authored
Merge pull request #35898 from rjmccall/diagnose-implicit-nested-self
Diagnose the implicit use of self in nested closures
2 parents 8578584 + f29074d commit 3918fb0

File tree

2 files changed

+84
-6
lines changed

2 files changed

+84
-6
lines changed

lib/Sema/MiscDiagnostics.cpp

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1461,14 +1461,31 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E,
14611461
Closures.push_back(ACE);
14621462
}
14631463

1464+
static bool isEnclosingSelfReference(VarDecl *var,
1465+
const AbstractClosureExpr *inClosure) {
1466+
if (var->isSelfParameter())
1467+
return true;
1468+
1469+
// Capture variables have a DC of the parent function.
1470+
if (inClosure && var->isSelfParamCapture() &&
1471+
var->getDeclContext() != inClosure->getParent())
1472+
return true;
1473+
1474+
return false;
1475+
}
1476+
14641477
/// Return true if this is an implicit reference to self which is required
14651478
/// to be explicit in an escaping closure. Metatype references and value
14661479
/// type references are excluded.
1467-
static bool isImplicitSelfParamUseLikelyToCauseCycle(Expr *E) {
1480+
static bool isImplicitSelfParamUseLikelyToCauseCycle(Expr *E,
1481+
const AbstractClosureExpr *inClosure) {
14681482
auto *DRE = dyn_cast<DeclRefExpr>(E);
14691483

1470-
if (!DRE || !DRE->isImplicit() || !isa<VarDecl>(DRE->getDecl()) ||
1471-
!cast<VarDecl>(DRE->getDecl())->isSelfParameter())
1484+
if (!DRE || !DRE->isImplicit())
1485+
return false;
1486+
1487+
auto var = dyn_cast<VarDecl>(DRE->getDecl());
1488+
if (!var || !isEnclosingSelfReference(var, inClosure))
14721489
return false;
14731490

14741491
// Defensive check for type. If the expression doesn't have type here, it
@@ -1544,7 +1561,7 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E,
15441561

15451562
SourceLoc memberLoc = SourceLoc();
15461563
if (auto *MRE = dyn_cast<MemberRefExpr>(E))
1547-
if (isImplicitSelfParamUseLikelyToCauseCycle(MRE->getBase())) {
1564+
if (isImplicitSelfParamUseLikelyToCauseCycle(MRE->getBase(), ACE)) {
15481565
auto baseName = MRE->getMember().getDecl()->getBaseName();
15491566
memberLoc = MRE->getLoc();
15501567
Diags.diagnose(memberLoc,
@@ -1554,7 +1571,7 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E,
15541571

15551572
// Handle method calls with a specific diagnostic + fixit.
15561573
if (auto *DSCE = dyn_cast<DotSyntaxCallExpr>(E))
1557-
if (isImplicitSelfParamUseLikelyToCauseCycle(DSCE->getBase()) &&
1574+
if (isImplicitSelfParamUseLikelyToCauseCycle(DSCE->getBase(), ACE) &&
15581575
isa<DeclRefExpr>(DSCE->getFn())) {
15591576
auto MethodExpr = cast<DeclRefExpr>(DSCE->getFn());
15601577
memberLoc = DSCE->getLoc();
@@ -1569,7 +1586,7 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E,
15691586
}
15701587

15711588
// Catch any other implicit uses of self with a generic diagnostic.
1572-
if (isImplicitSelfParamUseLikelyToCauseCycle(E))
1589+
if (isImplicitSelfParamUseLikelyToCauseCycle(E, ACE))
15731590
Diags.diagnose(E->getLoc(), diag::implicit_use_of_self_in_closure);
15741591

15751592
return { true, E };

test/expr/closure/closures.swift

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ func anonymousClosureArgsInClosureWithArgs() {
151151

152152
func doStuff(_ fn : @escaping () -> Int) {}
153153
func doVoidStuff(_ fn : @escaping () -> ()) {}
154+
func doVoidStuffNonEscaping(_ fn: () -> ()) {}
154155

155156
// <rdar://problem/16193162> Require specifying self for locations in code where strong reference cycles are likely
156157
class ExplicitSelfRequiredTest {
@@ -528,3 +529,63 @@ class SR3186 {
528529
print("\(v)")
529530
}
530531
}
532+
533+
// Apply the explicit 'self' rule even if it referrs to a capture, if
534+
// we're inside a nested closure
535+
class SR14120 {
536+
func operation() {}
537+
538+
func test1() {
539+
doVoidStuff { [self] in
540+
operation()
541+
}
542+
}
543+
544+
func test2() {
545+
doVoidStuff { [self] in
546+
doVoidStuff {
547+
// expected-error@+3 {{call to method 'operation' in closure requires explicit use of 'self'}}
548+
// expected-note@-2 {{capture 'self' explicitly to enable implicit 'self' in this closure}}
549+
// expected-note@+1 {{reference 'self.' explicitly}}
550+
operation()
551+
}
552+
}
553+
}
554+
555+
func test3() {
556+
doVoidStuff { [self] in
557+
doVoidStuff { [self] in
558+
operation()
559+
}
560+
}
561+
}
562+
563+
func test4() {
564+
doVoidStuff { [self] in
565+
doVoidStuff {
566+
self.operation()
567+
}
568+
}
569+
}
570+
571+
func test5() {
572+
doVoidStuff { [self] in
573+
doVoidStuffNonEscaping {
574+
operation()
575+
}
576+
}
577+
}
578+
579+
func test6() {
580+
doVoidStuff { [self] in
581+
doVoidStuff { [self] in
582+
doVoidStuff {
583+
// expected-error@+3 {{call to method 'operation' in closure requires explicit use of 'self'}}
584+
// expected-note@-2 {{capture 'self' explicitly to enable implicit 'self' in this closure}}
585+
// expected-note@+1 {{reference 'self.' explicitly}}
586+
operation()
587+
}
588+
}
589+
}
590+
}
591+
}

0 commit comments

Comments
 (0)