Skip to content

Commit 9e55fb0

Browse files
committed
Allow implicit self in inner functions in [weak self] closures, like with [self] closures
1 parent 3be14e0 commit 9e55fb0

File tree

3 files changed

+181
-5
lines changed

3 files changed

+181
-5
lines changed

lib/AST/UnqualifiedLookup.cpp

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -401,19 +401,68 @@ bool implicitSelfReferenceIsUnwrapped(const ValueDecl *selfDecl,
401401
// and check that both its LHS and RHS are 'self'
402402
for (auto cond : conditionalStmt->getCond()) {
403403
if (auto pattern = cond.getPattern()) {
404-
if (pattern->getBoundName() != Ctx.Id_self) {
404+
bool isSelfRebinding = false;
405+
406+
if (pattern->getBoundName() == Ctx.Id_self) {
407+
isSelfRebinding = true;
408+
}
409+
410+
else if (auto OSP = dyn_cast<OptionalSomePattern>(pattern)) {
411+
if (auto subPattern = OSP->getSubPattern()) {
412+
if (subPattern->getBoundName() == Ctx.Id_self) {
413+
isSelfRebinding = true;
414+
}
415+
}
416+
}
417+
418+
if (!isSelfRebinding) {
405419
continue;
406420
}
407421
}
408422

409-
if (auto selfDRE = dyn_cast<DeclRefExpr>(cond.getInitializer())) {
410-
return (selfDRE->getDecl()->getName().isSimpleName(Ctx.Id_self));
423+
DeclRefExpr *condDRE = nullptr;
424+
if (auto DRE = dyn_cast<DeclRefExpr>(cond.getInitializer())) {
425+
condDRE = DRE;
426+
}
427+
428+
if (auto LE = dyn_cast<LoadExpr>(cond.getInitializer())) {
429+
if (auto DRE = dyn_cast_or_null<DeclRefExpr>(LE->getSubExpr())) {
430+
condDRE = DRE;
431+
}
432+
}
433+
434+
if (!condDRE) {
435+
return false;
411436
}
437+
438+
return condDRE->getDecl()->getName().isSimpleName(Ctx.Id_self);
412439
}
413440

414441
return false;
415442
}
416443

444+
// Finds the nearest parent closure, which would define the
445+
// permitted usage of implicit self. In closures this is most
446+
// often just `dc` itself, but in functions defined in the
447+
// closure body this would be some parent context.
448+
AbstractClosureExpr *closestParentClosure(DeclContext *dc) {
449+
if (!dc) {
450+
return nullptr;
451+
}
452+
453+
if (auto closure = dyn_cast<AbstractClosureExpr>(dc)) {
454+
return closure;
455+
}
456+
457+
// Stop searching if we find a type decl, since types always
458+
// redefine what 'self' means, even when nested inside a closure.
459+
if (dc->getContextKind() == DeclContextKind::GenericTypeDecl) {
460+
return nullptr;
461+
}
462+
463+
return closestParentClosure(dc->getParent());
464+
}
465+
417466
ValueDecl *UnqualifiedLookupFactory::ResultFinderForTypeContext::lookupBaseDecl(
418467
const DeclContext *baseDC) const {
419468
// Perform an unqualified lookup for the base decl of this result. This
@@ -425,7 +474,8 @@ ValueDecl *UnqualifiedLookupFactory::ResultFinderForTypeContext::lookupBaseDecl(
425474
// self _always_ refers to the context's self `ParamDecl`, even if there
426475
// is another local decl with the name `self` that would be found by
427476
// `lookupSingleLocalDecl`.
428-
auto closureExpr = dyn_cast<ClosureExpr>(factory->DC);
477+
auto parentACE = closestParentClosure(factory->DC);
478+
auto closureExpr = dyn_cast_or_null<ClosureExpr>(parentACE);
429479
if (!closureExpr) {
430480
return nullptr;
431481
}

test/expr/closure/closures.swift

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,22 @@ class C_56501 {
616616
}
617617
}
618618
}
619+
620+
func test7() {
621+
doVoidStuff { [self] in
622+
func innerFunction() {
623+
operation()
624+
}
625+
}
626+
}
627+
628+
func test8() {
629+
doVoidStuffNonEscaping { [self] in
630+
func innerFunction() {
631+
operation()
632+
}
633+
}
634+
}
619635
}
620636

621637
// https://github.com/apple/swift/issues/57029
@@ -812,6 +828,61 @@ public class TestImplicitSelfForWeakSelfCapture {
812828
guard let self = self ?? TestImplicitSelfForWeakSelfCapture.staticOptional else { return } // expected-warning {{value 'self' was defined but never used; consider replacing with boolean test}}
813829
method() // expected-warning {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}}
814830
}
831+
832+
doVoidStuff { [weak self] in
833+
func innerFunction1() {
834+
method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}}
835+
self?.method()
836+
}
837+
838+
guard let self else { return }
839+
840+
func innerFunction2() {
841+
method()
842+
self.method()
843+
}
844+
}
845+
846+
doVoidStuffNonEscaping { [weak self] in
847+
func innerFunction1() {
848+
method()
849+
self?.method()
850+
}
851+
852+
guard let self else { return }
853+
854+
func innerFunction2() {
855+
method()
856+
self.method()
857+
}
858+
}
859+
}
860+
}
861+
862+
class NoImplicitSelfInInnerClass {
863+
func method() { }
864+
865+
private init() { // expected-note {{'self' declared here}} expected-note {{'self' declared here}}
866+
doVoidStuff {
867+
class InnerType { // expected-note {{type declared here}}
868+
func functionInsideInnerType() {
869+
method() // expected-error {{class declaration cannot close over value 'self' defined in outer scope}}
870+
self.method() // expected-error {{value of type 'InnerType' has no member 'method'}}
871+
}
872+
}
873+
}
874+
875+
doVoidStuff { [weak self] in
876+
guard let self else { return }
877+
method()
878+
879+
class InnerType { // expected-note {{type declared here}}
880+
func functionInsideInnerType() {
881+
method() // expected-error {{class declaration cannot close over value 'self' defined in outer scope}}
882+
self.method() // expected-error {{value of type 'InnerType' has no member 'method'}}
883+
}
884+
}
885+
}
815886
}
816887
}
817888

test/expr/closure/closures_swift6.swift

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,61 @@ public class TestImplicitSelfForWeakSelfCapture {
154154
guard let self = self ?? TestImplicitSelfForWeakSelfCapture.staticOptional else { return }
155155
method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}}
156156
}
157+
158+
doVoidStuff { [weak self] in
159+
func innerFunction1() {
160+
method() // expected-error {{explicit use of 'self' is required when 'self' is optional, to make control flow explicit}} expected-note {{reference 'self?.' explicitly}}
161+
self?.method()
162+
}
163+
164+
guard let self else { return }
165+
166+
func innerFunction2() {
167+
method()
168+
self.method()
169+
}
170+
}
171+
172+
doVoidStuffNonEscaping { [weak self] in
173+
func innerFunction1() {
174+
method() // expected-error {{explicit use of 'self' is required when 'self' is optional, to make control flow explicit}} expected-note {{reference 'self?.' explicitly}}
175+
self?.method()
176+
}
177+
178+
guard let self else { return }
179+
180+
func innerFunction2() {
181+
method()
182+
self.method()
183+
}
184+
}
185+
}
186+
}
187+
188+
class NoImplicitSelfInInnerClass {
189+
func method() { }
190+
191+
private init() { // expected-note {{'self' declared here}} expected-note {{'self' declared here}}
192+
doVoidStuff {
193+
class InnerType { // expected-note {{type declared here}}
194+
func functionInsideInnerType() {
195+
method() // expected-error {{class declaration cannot close over value 'self' defined in outer scope}}
196+
self.method() // expected-error {{value of type 'InnerType' has no member 'method'}}
197+
}
198+
}
199+
}
200+
201+
doVoidStuff { [weak self] in
202+
guard let self else { return }
203+
method()
204+
205+
class InnerType { // expected-note {{type declared here}}
206+
func functionInsideInnerType() {
207+
method() // expected-error {{class declaration cannot close over value 'self' defined in outer scope}}
208+
self.method() // expected-error {{value of type 'InnerType' has no member 'method'}}
209+
}
210+
}
211+
}
157212
}
158213
}
159214

@@ -186,4 +241,4 @@ public class TestRebindingSelfIsDisallowed {
186241
let `self` = "self shouldn't become a string"
187242
let _: Int = count // expected-error{{cannot convert value of type 'Void' to specified type 'Int'}}
188243
}
189-
}
244+
}

0 commit comments

Comments
 (0)