Skip to content

Commit ff62be1

Browse files
authored
Merge pull request #65211 from calda/cal--weak-self-capture-inner-func
[SE-0365] Allow implicit self in inner functions in [weak self] closures, like with [self] closures
2 parents 37d2d69 + b41769b commit ff62be1

File tree

3 files changed

+276
-5
lines changed

3 files changed

+276
-5
lines changed

lib/AST/UnqualifiedLookup.cpp

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -401,19 +401,65 @@ 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+
isSelfRebinding = subPattern->getBoundName() == Ctx.Id_self;
413+
}
414+
}
415+
416+
if (!isSelfRebinding) {
405417
continue;
406418
}
407419
}
408420

409-
if (auto selfDRE = dyn_cast<DeclRefExpr>(cond.getInitializer())) {
410-
return (selfDRE->getDecl()->getName().isSimpleName(Ctx.Id_self));
421+
Expr *exprToCheckForDRE = cond.getInitializer();
422+
if (auto LE = dyn_cast<LoadExpr>(exprToCheckForDRE)) {
423+
if (auto subexpr = LE->getSubExpr()) {
424+
exprToCheckForDRE = subexpr;
425+
}
411426
}
427+
428+
exprToCheckForDRE = exprToCheckForDRE->getSemanticsProvidingExpr();
429+
430+
DeclRefExpr *condDRE = dyn_cast<DeclRefExpr>(exprToCheckForDRE);
431+
if (!condDRE || !condDRE->getDecl()->hasName()) {
432+
return false;
433+
}
434+
435+
return condDRE->getDecl()->getName().isSimpleName(Ctx.Id_self);
412436
}
413437

414438
return false;
415439
}
416440

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

test/expr/closure/closures.swift

Lines changed: 170 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,160 @@ 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+
subscript(index: Int) -> Int { // expected-error {{subscript' functions may only be declared within a type}}
846+
method()
847+
return index
848+
}
849+
}
850+
851+
doVoidStuffNonEscaping { [weak self] in
852+
func innerFunction1() {
853+
method()
854+
self?.method()
855+
}
856+
857+
guard let self else { return }
858+
859+
func innerFunction2() {
860+
method()
861+
self.method()
862+
}
863+
864+
subscript(index: Int) -> Int { // expected-error {{subscript' functions may only be declared within a type}}
865+
method()
866+
return index
867+
}
868+
}
869+
870+
doVoidStuff { [weak self] in
871+
guard let self else { return }
872+
873+
func innerFunction1() {
874+
doVoidStuff { // expected-note {{capture 'self' explicitly to enable implicit 'self' in this closure}}
875+
method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note {{reference 'self.' explicitly}}
876+
}
877+
878+
// This example should probably compile without an error -- seems like a bug in the impl of SE-0269
879+
doVoidStuff { [self] in // expected-note {{variable other than 'self' captured here under the name 'self' does not enable implicit 'self'}}
880+
method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}}
881+
self.method()
882+
}
883+
884+
doVoidStuff { [weak self] in
885+
method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}}
886+
self?.method()
887+
}
888+
889+
doVoidStuff { [weak self] in
890+
guard let self else { return }
891+
method()
892+
893+
func innerMethod3() {
894+
method()
895+
self.method()
896+
}
897+
}
898+
}
899+
}
900+
}
901+
}
902+
903+
class NoImplicitSelfInInnerClass {
904+
func method() { }
905+
906+
private init() { // expected-note {{'self' declared here}} expected-note {{'self' declared here}} expected-note {{'self' declared here}} expected-note {{'self' declared here}} expected-note {{'self' declared here}} expected-note {{'self' declared here}} expected-note {{'self' declared here}}
907+
doVoidStuff {
908+
class InnerType { // expected-note {{type declared here}} expected-note {{type declared here}} expected-note {{type declared here}}
909+
init() {
910+
method() // expected-error {{class declaration cannot close over value 'self' defined in outer scope}}
911+
self.method() // expected-error {{value of type 'InnerType' has no member 'method'}}
912+
}
913+
914+
func functionInsideInnerType() {
915+
method() // expected-error {{class declaration cannot close over value 'self' defined in outer scope}}
916+
self.method() // expected-error {{value of type 'InnerType' has no member 'method'}}
917+
}
918+
919+
subscript(index: Int) -> Int {
920+
method() // expected-error {{class declaration cannot close over value 'self' defined in outer scope}}
921+
self.method() // expected-error {{value of type 'InnerType' has no member 'method'}}
922+
return index
923+
}
924+
}
925+
}
926+
927+
doVoidStuff { [weak self] in
928+
guard let self else { return }
929+
method()
930+
931+
class InnerType { // expected-note {{type declared here}} expected-note {{type declared here}} expected-note {{type declared here}}
932+
func methodOnInnerType() { }
933+
934+
init() {
935+
methodOnInnerType()
936+
method() // expected-error {{class declaration cannot close over value 'self' defined in outer scope}}
937+
self.method() // expected-error {{value of type 'InnerType' has no member 'method'}}
938+
}
939+
940+
func functionInsideInnerType() {
941+
methodOnInnerType()
942+
method() // expected-error {{class declaration cannot close over value 'self' defined in outer scope}}
943+
self.method() // expected-error {{value of type 'InnerType' has no member 'method'}}
944+
}
945+
946+
subscript(index: Int) -> Int {
947+
methodOnInnerType()
948+
method() // expected-error {{class declaration cannot close over value 'self' defined in outer scope}}
949+
self.method() // expected-error {{value of type 'InnerType' has no member 'method'}}
950+
return index
951+
}
952+
}
953+
}
954+
955+
doVoidStuff { [weak self] in
956+
guard let self else { return }
957+
958+
func innerMethod() {
959+
method()
960+
961+
class InnerType { // expected-note {{type declared here}}
962+
func methodOnInnerType() { }
963+
964+
init() {
965+
methodOnInnerType()
966+
method() // expected-error {{class declaration cannot close over value 'self' defined in outer scope}}
967+
self.method() // expected-error {{value of type 'InnerType' has no member 'method'}}
968+
969+
doVoidStuff { [weak self] in
970+
guard let self else { return }
971+
self.method() // expected-error {{value of type 'InnerType' has no member 'method'}}
972+
methodOnInnerType()
973+
}
974+
975+
doVoidStuff { [weak self] in
976+
guard let self else { return }
977+
method() // expected-error {{value of type 'InnerType' has no member 'method'}}
978+
methodOnInnerType()
979+
}
980+
}
981+
}
982+
}
983+
}
984+
815985
}
816986
}
817987

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)