Skip to content

Commit 5bfdfd8

Browse files
committed
Only enable SE-0365 in Swift 6
1 parent cc92f76 commit 5bfdfd8

File tree

4 files changed

+162
-46
lines changed

4 files changed

+162
-46
lines changed

lib/AST/UnqualifiedLookup.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,13 @@ ValueDecl *UnqualifiedLookupFactory::ResultFinderForTypeContext::lookupBaseDecl(
408408
}
409409
}
410410

411+
// We can only change the behavior of lookup in Swift 6 and later,
412+
// due to a bug in Swift 5 where implicit self is always allowed
413+
// for weak self captures in non-escaping closures.
414+
if (!factory->Ctx.LangOpts.isSwiftVersionAtLeast(6)) {
415+
return nullptr;
416+
}
417+
411418
if (isInWeakSelfClosure) {
412419
return ASTScope::lookupSingleLocalDecl(factory->DC->getParentSourceFile(),
413420
DeclName(factory->Ctx.Id_self),

lib/Sema/MiscDiagnostics.cpp

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1580,7 +1580,13 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E,
15801580
// let self = .someOptionalVariable else { return }` or `let self =
15811581
// someUnrelatedVariable`. If self hasn't been unwrapped yet and is still
15821582
// an optional, we would have already hit an error elsewhere.
1583-
if (closureHasWeakSelfCapture(inClosure)) {
1583+
//
1584+
// We can only enable this behavior in Swift 6 and later, due to a
1585+
// bug in Swift 5 where implicit self was always allowed for
1586+
// weak self captures (even before self was unwrapped) in
1587+
// non-escaping closures.
1588+
if (Ctx.LangOpts.isSwiftVersionAtLeast(6) &&
1589+
closureHasWeakSelfCapture(inClosure)) {
15841590
return !implicitWeakSelfReferenceIsValid(DRE);
15851591
}
15861592

@@ -1666,11 +1672,16 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E,
16661672

16671673
/// Return true if this is a closure expression that will require explicit
16681674
/// use or capture of "self." for qualification of member references.
1669-
static bool isClosureRequiringSelfQualification(
1670-
const AbstractClosureExpr *CE) {
1675+
static bool
1676+
isClosureRequiringSelfQualification(const AbstractClosureExpr *CE,
1677+
ASTContext &Ctx) {
16711678
// If this closure capture self weakly, then we have to validate each
1672-
// usage of implicit self individually, even in a nonescaping closure
1673-
if (closureHasWeakSelfCapture(CE)) {
1679+
// usage of implicit self individually, even in a nonescaping closure.
1680+
//
1681+
// We can only do this in Swift 6 mode, since we didn't do this in Swift 5
1682+
// (and changing this behavior causes new errors to be emitted).
1683+
if (Ctx.LangOpts.isSwiftVersionAtLeast(6) &&
1684+
closureHasWeakSelfCapture(CE)) {
16741685
return true;
16751686
}
16761687

@@ -1703,7 +1714,7 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E,
17031714
if (auto *CE = dyn_cast<AbstractClosureExpr>(E)) {
17041715
// If this is a potentially-escaping closure expression, start looking
17051716
// for references to self if we aren't already.
1706-
if (isClosureRequiringSelfQualification(CE))
1717+
if (isClosureRequiringSelfQualification(CE, Ctx))
17071718
Closures.push_back(CE);
17081719
}
17091720

@@ -1720,13 +1731,6 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E,
17201731
// Until Swift 6, only emit a warning when we get this with an
17211732
// explicit capture, since we used to not diagnose this at all.
17221733
auto shouldOnlyWarn = [&](Expr *selfRef) {
1723-
// If this implicit self decl is from a closure that captured self
1724-
// weakly, then we should always emit an error, since implicit self was
1725-
// only allowed starting in Swift 5.8 and later.
1726-
if (closureHasWeakSelfCapture(ACE)) {
1727-
return false;
1728-
}
1729-
17301734
// We know that isImplicitSelfParamUseLikelyToCauseCycle is true,
17311735
// which means all these casts are valid.
17321736
return !cast<VarDecl>(cast<DeclRefExpr>(selfRef)->getDecl())
@@ -1770,7 +1774,7 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E,
17701774

17711775
Expr *walkToExprPost(Expr *E) override {
17721776
if (auto *CE = dyn_cast<AbstractClosureExpr>(E)) {
1773-
if (isClosureRequiringSelfQualification(CE)) {
1777+
if (isClosureRequiringSelfQualification(CE, Ctx)) {
17741778
assert(Closures.size() > 0);
17751779
Closures.pop_back();
17761780
}
@@ -1893,16 +1897,16 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E,
18931897
}
18941898
};
18951899

1900+
auto &ctx = DC->getASTContext();
18961901
AbstractClosureExpr *ACE = nullptr;
18971902
if (DC->isLocalContext()) {
18981903
while (DC->getParent()->isLocalContext() && !ACE) {
18991904
if (auto *closure = dyn_cast<AbstractClosureExpr>(DC))
1900-
if (DiagnoseWalker::isClosureRequiringSelfQualification(closure))
1905+
if (DiagnoseWalker::isClosureRequiringSelfQualification(closure, ctx))
19011906
ACE = const_cast<AbstractClosureExpr *>(closure);
19021907
DC = DC->getParent();
19031908
}
19041909
}
1905-
auto &ctx = DC->getASTContext();
19061910
const_cast<Expr *>(E)->walk(DiagnoseWalker(ctx, ACE));
19071911
}
19081912

@@ -2594,7 +2598,7 @@ class VarDeclUsageChecker : public ASTWalker {
25942598

25952599
VarDecls[vd] |= Flag;
25962600
}
2597-
2601+
25982602
void markBaseOfStorageUse(Expr *E, ConcreteDeclRef decl, unsigned flags);
25992603
void markBaseOfStorageUse(Expr *E, bool isMutating);
26002604

@@ -3275,7 +3279,7 @@ VarDeclUsageChecker::~VarDeclUsageChecker() {
32753279
auto initExpr = SC->getCond()[0].getInitializer();
32763280
if (initExpr->getStartLoc().isValid()) {
32773281
unsigned noParens = initExpr->canAppendPostfixExpression();
3278-
3282+
32793283
// If the subexpr is an "as?" cast, we can rewrite it to
32803284
// be an "is" test.
32813285
ConditionalCheckedCastExpr *CCE = nullptr;
@@ -3656,7 +3660,7 @@ void VarDeclUsageChecker::handleIfConfig(IfConfigDecl *ICD) {
36563660
// conservatively mark it read and written. This will silence "variable
36573661
// unused" and "could be marked let" warnings for it.
36583662
if (auto *DRE = dyn_cast<DeclRefExpr>(E))
3659-
VDUC.addMark(DRE->getDecl(), RK_Read|RK_Written);
3663+
VDUC.addMark(DRE->getDecl(), RK_Read | RK_Written);
36603664
else if (auto *declRef = dyn_cast<UnresolvedDeclRefExpr>(E)) {
36613665
auto name = declRef->getName();
36623666
auto loc = declRef->getLoc();

test/expr/closure/closures.swift

Lines changed: 23 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -257,16 +257,13 @@ class ExplicitSelfRequiredTest {
257257
return 42
258258
}
259259

260-
// The error emitted by these cases cause `VarDeclUsageChecker` to not run analysis on this method,
261-
// because its `sawError` flag is set to true. To preserve the "capture 'y' was never used" warnings
262-
// above, we put these cases in their own method.
263260
func weakSelfError() {
264-
doVoidStuff({ [weak self] in x += 1 }) // expected-error {{explicit use of 'self' is required when 'self' is optional, to make control flow explicit}} expected-note {{reference 'self?.' explicitly}}
265-
doVoidStuffNonEscaping({ [weak self] in x += 1 }) // expected-error {{explicit use of 'self' is required when 'self' is optional, to make control flow explicit}} expected-note {{reference 'self?.' explicitly}}
266-
doStuff({ [weak self] in x+1 }) // expected-error {{explicit use of 'self' is required when 'self' is optional, to make control flow explicit}} expected-note {{reference 'self?.' explicitly}}
267-
doVoidStuff({ [weak self] in _ = method() }) // expected-error {{explicit use of 'self' is required when 'self' is optional, to make control flow explicit}} expected-note{{reference 'self?.' explicitly}}
268-
doVoidStuffNonEscaping({ [weak self] in _ = method() }) // expected-error {{explicit use of 'self' is required when 'self' is optional, to make control flow explicit}} expected-note{{reference 'self?.' explicitly}}
269-
doStuff({ [weak self] in method() }) // expected-error {{explicit use of 'self' is required when 'self' is optional, to make control flow explicit}} expected-note{{reference 'self?.' explicitly}}
261+
doVoidStuff({ [weak self] in x += 1 }) // expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-warning {{variable 'self' was written to, but never read}}
262+
doVoidStuffNonEscaping({ [weak self] in x += 1 }) // expected-warning {{variable 'self' was written to, but never read}}
263+
doStuff({ [weak self] in x+1 }) // expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-warning {{variable 'self' was written to, but never read}}
264+
doVoidStuff({ [weak self] in _ = method() }) // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-warning {{variable 'self' was written to, but never read}}
265+
doVoidStuffNonEscaping({ [weak self] in _ = method() }) // expected-warning {{variable 'self' was written to, but never read}}
266+
doStuff({ [weak self] in method() }) // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-warning {{variable 'self' was written to, but never read}}
270267
}
271268
}
272269

@@ -747,19 +744,21 @@ public class TestImplicitSelfForWeakSelfCapture {
747744

748745
private init() {
749746
doVoidStuff { [weak self] in
750-
method() // expected-error {{explicit use of 'self' is required when 'self' is optional, to make control flow explicit}} expected-note {{reference 'self?.' explicitly}}
747+
method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}}
751748
guard let self = self else { return }
752-
method()
749+
method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}}
750+
self.method()
753751
}
754752

755753
doVoidStuff { [weak self] in
756754
if let self = self {
757-
method()
755+
method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}}
756+
self.method()
758757
}
759758
}
760759

761760
doVoidStuff { [weak self] in
762-
guard let self = self else { return }
761+
guard let self = self else { return } // expected-warning {{value 'self' was defined but never used; consider replacing with boolean test}}
763762
doVoidStuff { // expected-note {{capture 'self' explicitly to enable implicit 'self' in this closure}}
764763
method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note {{reference 'self.' explicitly}}
765764
}
@@ -768,42 +767,40 @@ public class TestImplicitSelfForWeakSelfCapture {
768767
doVoidStuff { [weak self] in
769768
guard let self = self ?? TestImplicitSelfForWeakSelfCapture.staticOptional else { return }
770769
method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}}
770+
self.method()
771771
}
772772

773773
doVoidStuffNonEscaping { [weak self] in
774-
method() // expected-error {{explicit use of 'self' is required when 'self' is optional, to make control flow explicit}} expected-note {{reference 'self?.' explicitly}}
774+
method()
775775
guard let self = self else { return }
776776
method()
777+
self.method()
777778
}
778779

779780
doVoidStuffNonEscaping { [weak self] in
780781
if let self = self {
781782
method()
783+
self.method()
782784
}
783785
}
784786

785787
doVoidStuff { [weak self] in
786788
let `self`: TestImplicitSelfForWeakSelfCapture? = self ?? TestImplicitSelfForWeakSelfCapture.staticOptional
787-
guard let self = self else { return }
789+
guard let self = self else { return } // expected-warning {{value 'self' was defined but never used; consider replacing with boolean test}}
788790
method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}}
789791
}
790792

791793
doVoidStuffNonEscaping { [weak self] in
792794
let `self`: TestImplicitSelfForWeakSelfCapture? = self ?? TestImplicitSelfForWeakSelfCapture.staticOptional
793795
guard let self = self else { return }
794-
method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}}
795-
}
796-
797-
doVoidStuffNonEscaping { [weak self] in
798-
guard let self = self else { return }
799-
doVoidStuff { // expected-note {{capture 'self' explicitly to enable implicit 'self' in this closure}}
800-
method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note {{reference 'self.' explicitly}}
801-
}
796+
method()
797+
self.method()
802798
}
803799

804800
doVoidStuffNonEscaping { [weak self] in
805801
guard let self = self ?? TestImplicitSelfForWeakSelfCapture.staticOptional else { return }
806-
method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}}
802+
method()
803+
self.method()
807804
}
808805
}
809806
}
@@ -824,12 +821,12 @@ public class TestRebindingSelfIsDisallowed {
824821

825822
doVoidStuff { [weak self] in
826823
let `self` = "self shouldn't become a string"
827-
let _: Int = count // expected-error{{reference to property 'count' in closure requires explicit use of 'self' to make capture semantics explicit}}
824+
let _: Int = count // expected-error{{cannot convert value of type 'Void' to specified type 'Int'}}
828825
}
829826

830827
doVoidStuffNonEscaping { [weak self] in
831828
let `self` = "self shouldn't become a string"
832-
let _: Int = count // expected-error{{reference to property 'count' in closure requires explicit use of 'self' to make capture semantics explicit}}
829+
let _: Int = count // expected-error{{cannot convert value of type 'Void' to specified type 'Int'}}
833830
}
834831
}
835832

test/expr/closure/closures_swift6.swift

Lines changed: 109 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,18 @@ func doVoidStuffNonEscaping(_ fn: () -> ()) {}
77

88
class ExplicitSelfRequiredTest {
99
var x = 42
10-
func method() {
10+
func method() -> Int {
1111
doVoidStuff({ doStuff({ x+1 })}) // expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{28-28= [self] in}} expected-note{{reference 'self.' explicitly}} {{29-29=self.}}
12+
return 42
13+
}
14+
15+
func weakSelfError() {
16+
doVoidStuff({ [weak self] in x += 1 }) // expected-error {{explicit use of 'self' is required when 'self' is optional, to make control flow explicit}} expected-note {{reference 'self?.' explicitly}}
17+
doVoidStuffNonEscaping({ [weak self] in x += 1 }) // expected-error {{explicit use of 'self' is required when 'self' is optional, to make control flow explicit}} expected-note {{reference 'self?.' explicitly}}
18+
doStuff({ [weak self] in x+1 }) // expected-error {{explicit use of 'self' is required when 'self' is optional, to make control flow explicit}} expected-note {{reference 'self?.' explicitly}}
19+
doVoidStuff({ [weak self] in _ = method() }) // expected-error {{explicit use of 'self' is required when 'self' is optional, to make control flow explicit}} expected-note{{reference 'self?.' explicitly}}
20+
doVoidStuffNonEscaping({ [weak self] in _ = method() }) // expected-error {{explicit use of 'self' is required when 'self' is optional, to make control flow explicit}} expected-note{{reference 'self?.' explicitly}}
21+
doStuff({ [weak self] in method() }) // expected-error {{explicit use of 'self' is required when 'self' is optional, to make control flow explicit}} expected-note{{reference 'self?.' explicitly}}
1222
}
1323
}
1424

@@ -70,3 +80,101 @@ class C_56501 {
7080
}
7181
}
7282
}
83+
84+
public class TestImplicitSelfForWeakSelfCapture {
85+
static let staticOptional: TestImplicitSelfForWeakSelfCapture? = .init()
86+
func method() { }
87+
88+
private init() {
89+
doVoidStuff { [weak self] in
90+
method() // expected-error {{explicit use of 'self' is required when 'self' is optional, to make control flow explicit}} expected-note {{reference 'self?.' explicitly}}
91+
guard let self = self else { return }
92+
method()
93+
}
94+
95+
doVoidStuff { [weak self] in
96+
if let self = self {
97+
method()
98+
}
99+
}
100+
101+
doVoidStuff { [weak self] in
102+
guard let self = self else { return }
103+
doVoidStuff { // expected-note {{capture 'self' explicitly to enable implicit 'self' in this closure}}
104+
method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note {{reference 'self.' explicitly}}
105+
}
106+
}
107+
108+
doVoidStuff { [weak self] in
109+
guard let self = self ?? TestImplicitSelfForWeakSelfCapture.staticOptional else { return }
110+
method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}}
111+
}
112+
113+
doVoidStuffNonEscaping { [weak self] in
114+
method() // expected-error {{explicit use of 'self' is required when 'self' is optional, to make control flow explicit}} expected-note {{reference 'self?.' explicitly}}
115+
guard let self = self else { return }
116+
method()
117+
}
118+
119+
doVoidStuffNonEscaping { [weak self] in
120+
if let self = self {
121+
method()
122+
}
123+
}
124+
125+
doVoidStuff { [weak self] in
126+
let `self`: TestImplicitSelfForWeakSelfCapture? = self ?? TestImplicitSelfForWeakSelfCapture.staticOptional
127+
guard let self = self else { return }
128+
method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}}
129+
}
130+
131+
doVoidStuffNonEscaping { [weak self] in
132+
let `self`: TestImplicitSelfForWeakSelfCapture? = self ?? TestImplicitSelfForWeakSelfCapture.staticOptional
133+
guard let self = self else { return }
134+
method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}}
135+
}
136+
137+
doVoidStuffNonEscaping { [weak self] in
138+
guard let self = self else { return }
139+
doVoidStuff { // expected-note {{capture 'self' explicitly to enable implicit 'self' in this closure}}
140+
method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note {{reference 'self.' explicitly}}
141+
}
142+
}
143+
144+
doVoidStuffNonEscaping { [weak self] in
145+
guard let self = self ?? TestImplicitSelfForWeakSelfCapture.staticOptional else { return }
146+
method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}}
147+
}
148+
}
149+
}
150+
151+
public class TestRebindingSelfIsDisallowed {
152+
let count: Void = ()
153+
154+
private init() {
155+
doVoidStuff {
156+
let `self` = "self shouldn't become a string"
157+
let _: Int = count // expected-error{{cannot convert value of type 'Void' to specified type 'Int'}}
158+
}
159+
160+
doVoidStuffNonEscaping {
161+
let `self` = "self shouldn't become a string"
162+
let _: Int = count // expected-error{{cannot convert value of type 'Void' to specified type 'Int'}}
163+
}
164+
165+
doVoidStuff { [weak self] in
166+
let `self` = "self shouldn't become a string"
167+
let _: Int = count // expected-error{{reference to property 'count' in closure requires explicit use of 'self' to make capture semantics explicit}}
168+
}
169+
170+
doVoidStuffNonEscaping { [weak self] in
171+
let `self` = "self shouldn't become a string"
172+
let _: Int = count // expected-error{{reference to property 'count' in closure requires explicit use of 'self' to make capture semantics explicit}}
173+
}
174+
}
175+
176+
func method() {
177+
let `self` = "self shouldn't become a string"
178+
let _: Int = count // expected-error{{cannot convert value of type 'Void' to specified type 'Int'}}
179+
}
180+
}

0 commit comments

Comments
 (0)