Skip to content

Commit f828882

Browse files
authored
Merge pull request #78738 from calda/cal--fix-nested-weak-self-issue
Fix issue where implicit self was unexpectedly not allowed in nested weak self closure in Swift 6 mode
2 parents 7931a3f + 14632b7 commit f828882

File tree

3 files changed

+268
-34
lines changed

3 files changed

+268
-34
lines changed

Diff for: lib/Sema/MiscDiagnostics.cpp

+29-24
Original file line numberDiff line numberDiff line change
@@ -1840,16 +1840,14 @@ class ImplicitSelfUsageChecker : public BaseDiagnosticWalker {
18401840
return selfDeclAllowsImplicitSelf510(DRE, ty, inClosure);
18411841

18421842
return selfDeclAllowsImplicitSelf(DRE->getDecl(), ty, inClosure,
1843-
/*validateParentClosures:*/ true,
1844-
/*validateSelfRebindings:*/ true);
1843+
/*isValidatingParentClosures:*/ false);
18451844
}
18461845

18471846
/// Whether or not implicit self is allowed for this implicit self decl
18481847
static bool selfDeclAllowsImplicitSelf(const ValueDecl *selfDecl,
18491848
const Type captureType,
18501849
const AbstractClosureExpr *inClosure,
1851-
bool validateParentClosures,
1852-
bool validateSelfRebindings) {
1850+
bool isValidatingParentClosures) {
18531851
ASTContext &ctx = inClosure->getASTContext();
18541852

18551853
auto requiresSelfQualification =
@@ -1883,28 +1881,32 @@ class ImplicitSelfUsageChecker : public BaseDiagnosticWalker {
18831881
}
18841882
}
18851883

1886-
// If the self decl comes from a conditional statement, validate
1887-
// that it is an allowed `guard let self` or `if let self` condition.
1884+
// If the self decl refers to a weak capture, then implicit self is not
1885+
// allowed. Self must be unwrapped in a `guard let self` / `if let self`
1886+
// condition first. This is usually enforced by the type checker, but
1887+
// isn't in some cases (e.g. when calling a method on `Optional<Self>`).
1888+
// - When validating implicit self usage in a nested closure, it's not
1889+
// necessary for self to be unwrapped in this parent closure. If self
1890+
// isn't unwrapped correctly in the nested closure, we would have
1891+
// already noticed when validating that closure.
1892+
if (selfDecl->getInterfaceType()->is<WeakStorageType>() &&
1893+
!isValidatingParentClosures) {
1894+
return false;
1895+
}
1896+
1897+
// If the self decl refers to an invalid unwrapping conditon like
1898+
// `guard let self = somethingOtherThanSelf`, then implicit self is always
1899+
// disallowed.
18881900
// - Even if this closure doesn't have a `weak self` capture, it could
18891901
// be a closure nested in some parent closure with a `weak self`
18901902
// capture, so we should always validate the conditional statement
18911903
// that defines self if present.
1892-
if (validateSelfRebindings) {
1893-
if (auto conditionalStmt = parentConditionalStmt(selfDecl)) {
1894-
if (!hasValidSelfRebinding(conditionalStmt, ctx)) {
1895-
return false;
1896-
}
1904+
if (auto condStmt = parentConditionalStmt(selfDecl)) {
1905+
if (!hasValidSelfRebinding(condStmt, ctx)) {
1906+
return false;
18971907
}
18981908
}
18991909

1900-
// If this closure has a `weak self` capture, require that the
1901-
// closure unwraps self. If not, implicit self is not allowed
1902-
// in this closure or in any nested closure.
1903-
if (closureHasWeakSelfCapture(inClosure) &&
1904-
!hasValidSelfRebinding(parentConditionalStmt(selfDecl), ctx)) {
1905-
return false;
1906-
}
1907-
19081910
if (auto autoclosure = dyn_cast<AutoClosureExpr>(inClosure)) {
19091911
// Implicit self is always allowed in autoclosure thunks generated
19101912
// during type checking. An example of this is when storing an instance
@@ -1927,7 +1929,7 @@ class ImplicitSelfUsageChecker : public BaseDiagnosticWalker {
19271929
// - We have to do this for all closures, even closures that typically
19281930
// don't require self qualificationm since an invalid self capture in
19291931
// a parent closure still disallows implicit self in a nested closure.
1930-
if (validateParentClosures) {
1932+
if (!isValidatingParentClosures) {
19311933
return !implicitSelfDisallowedDueToInvalidParent(selfDecl, captureType,
19321934
inClosure);
19331935
} else {
@@ -2048,10 +2050,14 @@ class ImplicitSelfUsageChecker : public BaseDiagnosticWalker {
20482050
// Check whether implicit self is disallowed due to this specific
20492051
// closure, or if its disallowed due to some parent of this closure,
20502052
// so we can return the specific closure that is invalid.
2053+
//
2054+
// If this is a `weak self` capture, we don't need to validate that
2055+
// that capture has been unwrapped in a `let self = self` binding
2056+
// within the parent closure. A self rebinding in this inner closure
2057+
// is sufficient to enable implicit self.
20512058
if (!selfDeclAllowsImplicitSelf(outerSelfDecl, captureType,
20522059
outerClosure,
2053-
/*validateParentClosures:*/ false,
2054-
/*validateSelfRebindings:*/ true)) {
2060+
/*isValidatingParentClosures:*/ true)) {
20552061
return outerClosure;
20562062
}
20572063

@@ -2064,8 +2070,7 @@ class ImplicitSelfUsageChecker : public BaseDiagnosticWalker {
20642070
// parent closures, we don't need to do that separate for this closure.
20652071
if (validateIntermediateParents) {
20662072
if (!selfDeclAllowsImplicitSelf(selfDecl, captureType, outerClosure,
2067-
/*validateParentClosures*/ false,
2068-
/*validateSelfRebindings*/ false)) {
2073+
/*isValidatingParentClosures*/ true)) {
20692074
return outerClosure;
20702075
}
20712076
}

Diff for: test/expr/closure/closures.swift

+35
Original file line numberDiff line numberDiff line change
@@ -1407,6 +1407,13 @@ class TestGithubIssue69911 {
14071407
}
14081408
}
14091409

1410+
doVoidStuff { [weak self] in
1411+
guard let self = self ?? TestGithubIssue69911.staticOptional else { return }
1412+
doVoidStuff { [self] in // expected-warning {{capture 'self' was never used}} expeced-note {{variable other than 'self' captured here under the name 'self' does not enable implicit 'self'}} expected-note {{variable other than 'self' captured here under the name 'self' does not enable implicit 'self'}}
1413+
x += 1 // expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}}
1414+
}
1415+
}
1416+
14101417
doVoidStuff { [weak self] in
14111418
// Since this unwrapping is invalid, implicit self is disallowed in all nested closures:
14121419
guard let self = self ?? TestGithubIssue69911.staticOptional else { return }
@@ -1804,3 +1811,31 @@ class TestLazyLocal {
18041811
}
18051812
}
18061813
}
1814+
1815+
class TestExtensionOnOptionalSelf {
1816+
init() {}
1817+
}
1818+
1819+
extension TestExtensionOnOptionalSelf? {
1820+
func foo() {
1821+
_ = { [weak self] in // expected-warning {{variable 'self' was written to, but never read}}
1822+
foo() // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}}
1823+
}
1824+
1825+
_ = {
1826+
foo()
1827+
}
1828+
1829+
_ = { [weak self] in // expected-warning {{variable 'self' was written to, but never read}}
1830+
_ = {
1831+
foo()
1832+
}
1833+
}
1834+
1835+
_ = { [weak self] in
1836+
_ = { [self] in // expected-warning {{capture 'self' was never used}}
1837+
foo()
1838+
}
1839+
}
1840+
}
1841+
}

Diff for: test/expr/closure/closures_swift6.swift

+204-10
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,92 @@ class TestGithubIssue70089 {
454454
}
455455
}
456456
}
457+
458+
func testClosuresInsideWeakSelfNotUnwrapped() {
459+
// https://forums.swift.org/t/nested-weak-capture-and-implicit-self-in-swift-6/77230/1
460+
doVoidStuff { [weak self] in
461+
doVoidStuff { [weak self] in
462+
guard let self else { return }
463+
x += 1
464+
}
465+
}
466+
467+
doVoidStuff { [weak self] in
468+
doVoidStuff { [weak self] in
469+
doVoidStuff { [weak self] in
470+
guard let self else { return }
471+
doVoidStuff { [weak self] in
472+
doVoidStuff { [weak self] in
473+
guard let self else { return }
474+
x += 1
475+
}
476+
}
477+
}
478+
}
479+
}
480+
481+
doVoidStuff { [weak self] in
482+
doVoidStuff { [weak self] in
483+
guard let self else { return }
484+
doVoidStuff { [self] in
485+
doVoidStuff { [self] in
486+
doVoidStuff { [weak self] in
487+
guard let self else { return }
488+
x += 1
489+
}
490+
}
491+
}
492+
}
493+
}
494+
495+
doVoidStuff { [weak self] in
496+
guard let self = self ?? TestGithubIssue70089.staticOptional else { return }
497+
doVoidStuff { [weak self] in
498+
guard let self else { return }
499+
x += 1 // expected-error{{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}}
500+
}
501+
}
502+
503+
doVoidStuff { [weak self] in
504+
doVoidStuff { [self] in
505+
x += 1 // expected-error {{explicit use of 'self' is required when 'self' is optional, to make control flow explicit}} expected-note{{reference 'self?.' explicitly}}
506+
}
507+
}
508+
509+
doVoidStuff { [weak self] in
510+
doVoidStuff { [self] in
511+
self.x += 1 // expected-error {{value of optional type 'TestGithubIssue70089?' must be unwrapped to refer to member 'x' of wrapped base type 'TestGithubIssue70089'}} expected-note {{chain the optional using '?' to access member 'x' only for non-'nil' base values}} expected-note{{force-unwrap using '!' to abort execution if the optional value contains 'nil'}}
512+
}
513+
}
514+
515+
doVoidStuff { [weak self] in
516+
doVoidStuff { [self] in
517+
self?.x += 1
518+
}
519+
}
520+
521+
doVoidStuff { [weak self] in
522+
doVoidStuff { [self] in
523+
guard let self else { return }
524+
self.x += 1
525+
}
526+
}
527+
528+
doVoidStuff { [weak self] in
529+
doVoidStuff { [self] in
530+
guard let self else { return }
531+
x += 1
532+
}
533+
}
534+
535+
doVoidStuff { [weak self] in
536+
guard let self = self ?? TestGithubIssue70089.staticOptional else { return }
537+
doVoidStuff { [self] in
538+
guard let self else { return } // expected-error{{initializer for conditional binding must have Optional type, not 'TestGithubIssue70089'}}
539+
x += 1 // expected-error{{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}}
540+
}
541+
}
542+
}
457543
}
458544

459545
class TestGithubIssue69911 {
@@ -522,9 +608,9 @@ class TestGithubIssue69911 {
522608
self.x += 1
523609
}
524610

525-
doVoidStuffNonEscaping {
611+
doVoidStuffNonEscaping { // expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}}
526612
doVoidStuffNonEscaping {
527-
x += 1 // expected-error{{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}}
613+
x += 1 // expected-error{{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}}
528614
self.x += 1
529615
}
530616
}
@@ -670,7 +756,7 @@ final class AutoclosureTests {
670756
doVoidStuff { [weak self] in
671757
doVoidStuff { [self] in
672758
guard let self else { return }
673-
method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}}
759+
method()
674760
}
675761
}
676762

@@ -702,13 +788,6 @@ final class AutoclosureTests {
702788
}
703789
}
704790

705-
doVoidStuff { [weak self] in
706-
doVoidStuff { [self] in
707-
guard let self else { return }
708-
method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}}
709-
}
710-
}
711-
712791
doVoidStuff { [weak self] in
713792
guard let self = self else { return }
714793
doVoidStuff { // expected-note {{capture 'self' explicitly to enable implicit 'self' in this closure}}
@@ -844,3 +923,118 @@ class rdar129475277 {
844923
}
845924
}
846925
}
926+
927+
class TestExtensionOnOptionalSelf {
928+
init() {}
929+
func bar() {}
930+
}
931+
932+
extension TestExtensionOnOptionalSelf? {
933+
func foo() {
934+
_ = { [weak self] in
935+
foo() // expected-error {{implicit use of 'self' in closure; use 'self.' to make capture semantics explicit}}
936+
}
937+
938+
_ = {
939+
foo()
940+
self.foo()
941+
self?.bar()
942+
}
943+
944+
_ = { [weak self] in
945+
_ = {
946+
foo() // expected-error {{implicit use of 'self' in closure; use 'self.' to make capture semantics explicit}}
947+
self.foo()
948+
self?.bar()
949+
}
950+
}
951+
952+
_ = { [weak self] in
953+
_ = { [self] in
954+
foo()
955+
self.foo()
956+
self?.bar()
957+
}
958+
}
959+
}
960+
}
961+
962+
// non-optional self in this extension, but on a type with members defined on optional self
963+
extension TestExtensionOnOptionalSelf {
964+
func foo() {
965+
_ = { [weak self] in
966+
foo() // expected-error {{implicit use of 'self' in closure; use 'self.' to make capture semantics explicit}}
967+
self.foo()
968+
self?.bar()
969+
}
970+
971+
_ = { // expected-note {{capture 'self' explicitly to enable implicit 'self' in this closure}}
972+
foo() // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note {{reference 'self.' explicitly}}
973+
self.foo()
974+
}
975+
976+
_ = { [weak self] in
977+
_ = {
978+
foo() // expected-error {{implicit use of 'self' in closure; use 'self.' to make capture semantics explicit}}
979+
self.foo()
980+
}
981+
}
982+
983+
_ = { [weak self] in
984+
_ = { [self] in
985+
foo()
986+
self.foo()
987+
}
988+
}
989+
990+
_ = { [weak self] in
991+
_ = { [self] in
992+
_ = { [self] in
993+
foo()
994+
self.foo()
995+
}
996+
}
997+
}
998+
999+
_ = { [weak self] in
1000+
doVoidStuffNonEscaping {
1001+
_ = { [self] in
1002+
foo()
1003+
self.foo()
1004+
}
1005+
}
1006+
}
1007+
1008+
_ = { [weak self] in
1009+
guard case let self = self else { return }
1010+
_ = { [self] in
1011+
foo()
1012+
}
1013+
}
1014+
}
1015+
}
1016+
1017+
actor TestActor {
1018+
func setUp() {
1019+
doVoidStuff { [weak self] in
1020+
Task { [weak self] in
1021+
guard let self else { return }
1022+
await test()
1023+
}
1024+
}
1025+
}
1026+
1027+
@MainActor
1028+
func test() { }
1029+
}
1030+
1031+
class C {
1032+
func foo() {
1033+
_ = { [self] in // expected-note {{variable other than 'self' captured here under the name 'self' does not enable implicit 'self'}} expected-warning {{capture 'self' was never used}}
1034+
guard case let self = C() else { return }
1035+
_ = { [self] in
1036+
foo() // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}}
1037+
}
1038+
}
1039+
}
1040+
}

0 commit comments

Comments
 (0)