Skip to content

Commit 8528021

Browse files
committed
Change lookup behavior in ASTScope::lookupUnqualified when self has been unwrapped
1 parent ebcc9c2 commit 8528021

File tree

3 files changed

+95
-138
lines changed

3 files changed

+95
-138
lines changed

lib/AST/UnqualifiedLookup.cpp

Lines changed: 82 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,43 @@ UnqualifiedLookupFactory::ResultFinderForTypeContext::getBaseDeclForResult(
388388
return nominalDecl;
389389
}
390390

391+
/// Whether or not the given self decl is defined in an optional
392+
/// unwrapping condition (e.g. `guard let self else { return }`).
393+
/// If this is true, then we know any implicit self reference in the
394+
/// following scope is guaranteed to be non-optional.
395+
bool implicitSelfReferenceIsUnwrapped(const ValueDecl *selfDecl,
396+
const AbstractClosureExpr *inClosure) {
397+
ASTContext &Ctx = selfDecl->getASTContext();
398+
399+
// Check if the implicit self decl refers to a var in a conditional stmt
400+
LabeledConditionalStmt *conditionalStmt = nullptr;
401+
if (auto var = dyn_cast<VarDecl>(selfDecl)) {
402+
if (auto parentStmt = var->getParentPatternStmt()) {
403+
conditionalStmt = dyn_cast<LabeledConditionalStmt>(parentStmt);
404+
}
405+
}
406+
407+
if (!conditionalStmt) {
408+
return false;
409+
}
410+
411+
// Find the condition that defined the self decl,
412+
// and check that both its LHS and RHS are 'self'
413+
for (auto cond : conditionalStmt->getCond()) {
414+
if (auto pattern = cond.getPattern()) {
415+
if (pattern->getBoundName() != Ctx.Id_self) {
416+
continue;
417+
}
418+
}
419+
420+
if (auto selfDRE = dyn_cast<DeclRefExpr>(cond.getInitializer())) {
421+
return (selfDRE->getDecl()->getName().isSimpleName(Ctx.Id_self));
422+
}
423+
}
424+
425+
return false;
426+
}
427+
391428
ValueDecl *UnqualifiedLookupFactory::ResultFinderForTypeContext::lookupBaseDecl(
392429
const DeclContext *baseDC) const {
393430
// Perform an unqualified lookup for the base decl of this result. This
@@ -399,29 +436,59 @@ ValueDecl *UnqualifiedLookupFactory::ResultFinderForTypeContext::lookupBaseDecl(
399436
// self _always_ refers to the context's self `ParamDecl`, even if there
400437
// is another local decl with the name `self` that would be found by
401438
// `lookupSingleLocalDecl`.
402-
bool isInWeakSelfClosure = false;
403-
if (auto closureExpr = dyn_cast<ClosureExpr>(factory->DC)) {
404-
if (auto decl = closureExpr->getCapturedSelfDecl()) {
405-
if (auto a = decl->getAttrs().getAttribute<ReferenceOwnershipAttr>()) {
406-
isInWeakSelfClosure = a->get() == ReferenceOwnership::Weak;
407-
}
439+
auto closureExpr = dyn_cast<ClosureExpr>(factory->DC);
440+
if (!closureExpr) {
441+
return nullptr;
442+
}
443+
444+
bool capturesSelfWeakly = false;
445+
if (auto decl = closureExpr->getCapturedSelfDecl()) {
446+
if (auto a = decl->getAttrs().getAttribute<ReferenceOwnershipAttr>()) {
447+
capturesSelfWeakly = a->get() == ReferenceOwnership::Weak;
408448
}
409449
}
410450

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)) {
451+
if (!capturesSelfWeakly) {
415452
return nullptr;
416453
}
417454

418-
if (isInWeakSelfClosure) {
419-
return ASTScope::lookupSingleLocalDecl(factory->DC->getParentSourceFile(),
420-
DeclName(factory->Ctx.Id_self),
421-
factory->Loc);
455+
auto selfDecl = ASTScope::lookupSingleLocalDecl(
456+
factory->DC->getParentSourceFile(), DeclName(factory->Ctx.Id_self),
457+
factory->Loc);
458+
459+
if (!selfDecl) {
460+
return nullptr;
461+
}
462+
463+
// In Swift 5 mode, implicit self is allowed within non-escaping
464+
// closures even before self is unwrapped. For example, this is allowed:
465+
//
466+
// doVoidStuffNonEscaping { [weak self] in
467+
// method() // implicitly `self.method()`
468+
// }
469+
//
470+
// To support this, we have to preserve the lookup behavior from
471+
// Swift 5.7 and earlier where implicit self defaults to the closure's
472+
// `ParamDecl`. This causes the closure to capture self strongly, however,
473+
// which is not acceptable for escaping closures.
474+
//
475+
// Escaping closures, however, only need to permit implicit self once
476+
// it has been unwrapped to be non-optional:
477+
//
478+
// doVoidStuffEscaping { [weak self] in
479+
// guard let self else { return }
480+
// method()
481+
// }
482+
//
483+
// In these cases, using the Swift 6 lookup behavior doesn't affect
484+
// how the body is type-checked, so it can be used in Swift 5 mode
485+
// without breaking source compatibility for non-escaping closures.
486+
if (!factory->Ctx.LangOpts.isSwiftVersionAtLeast(6) &&
487+
!implicitSelfReferenceIsUnwrapped(selfDecl, closureExpr)) {
488+
return nullptr;
422489
}
423490

424-
return nullptr;
491+
return selfDecl;
425492
}
426493

427494
// TODO (someday): Instead of adding unavailable entries to Results,

lib/Sema/MiscDiagnostics.cpp

Lines changed: 5 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -1538,20 +1538,6 @@ static bool closureHasWeakSelfCapture(const AbstractClosureExpr *ACE) {
15381538
return false;
15391539
}
15401540

1541-
/// Whether or not implicit self decls in this closure require manual
1542-
/// lookup in order to perform diagnostics with the semantics described
1543-
/// in SE-0365. This is necessary for closures that capture self weakly
1544-
/// in Swift 5 language modes, since implicit self decls in weak self
1545-
/// closures have different semantics in Swift 5 than in Swift 6.
1546-
static bool
1547-
closureRequiresManualLookupForImplicitSelf(const AbstractClosureExpr *ACE) {
1548-
if (ACE->getASTContext().LangOpts.isSwiftVersionAtLeast(6)) {
1549-
return false;
1550-
}
1551-
1552-
return closureHasWeakSelfCapture(ACE);
1553-
}
1554-
15551541
// Returns true if this is an implicit self expr
15561542
static bool isImplicitSelf(const Expr *E) {
15571543
auto *DRE = dyn_cast<DeclRefExpr>(E);
@@ -1563,27 +1549,6 @@ static bool isImplicitSelf(const Expr *E) {
15631549
return DRE->getDecl()->getName().isSimpleName(Ctx.Id_self);
15641550
}
15651551

1566-
// When inside a closure that requires manual lookup for implicit self,
1567-
// (any closure with a weak self capture, in Swift 5 mode),
1568-
// we have to manually lookup what self refers to at the location
1569-
// according to the semantics of Swift 6 in order to avoid confusing
1570-
// 'variable self is unused' warnings in Swift 5 mode.
1571-
static ValueDecl *lookupSelfDeclIfNecessary(const DeclRefExpr *DRE,
1572-
const AbstractClosureExpr *ACE) {
1573-
ASTContext &Ctx = DRE->getDecl()->getASTContext();
1574-
1575-
if (closureRequiresManualLookupForImplicitSelf(ACE) && isImplicitSelf(DRE)) {
1576-
auto lookupResult = ASTScope::lookupSingleLocalDecl(
1577-
ACE->getParentSourceFile(), DeclName(Ctx.Id_self), DRE->getLoc());
1578-
1579-
if (lookupResult) {
1580-
return lookupResult;
1581-
}
1582-
}
1583-
1584-
return DRE->getDecl();
1585-
}
1586-
15871552
/// Look for any property references in closures that lack a 'self.' qualifier.
15881553
/// Within a closure, we require that the source code contain 'self.' explicitly
15891554
/// (or that the closure explicitly capture 'self' in the capture list) because
@@ -1683,29 +1648,11 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E,
16831648
static bool
16841649
implicitWeakSelfReferenceIsValid(const DeclRefExpr *DRE,
16851650
const AbstractClosureExpr *inClosure) {
1686-
// Implicit self DREs have different semantics in Swift 5 language modes
1687-
// than they have in Swift 6 mode. Take this example:
1688-
//
1689-
// doVoidStuff { [weak self] in
1690-
// guard let self else { return }
1691-
// method()
1692-
// }
1693-
//
1694-
// In Swift 6, the implicit self DRE refers to the self decl
1695-
// defined in the guard statement. In Swift 5 mode, the implicit
1696-
// self DRE instead refers to closure's self `ParamDecl`.
1697-
//
1698-
// In order to perform these diagnostics according to the semantics
1699-
// defined in SE-0365, we need to know the decl that the DRE refers
1700-
// to in Swift 6. In Swift 5 mode, that requires performing a manual
1701-
// lookup of what implicit self refers to at this location (according
1702-
// to the semantics of Swift 6).
1703-
auto selfDecl = lookupSelfDeclIfNecessary(DRE, inClosure);
1704-
ASTContext &Ctx = selfDecl->getASTContext();
1651+
ASTContext &Ctx = DRE->getDecl()->getASTContext();
17051652

17061653
// Check if the implicit self decl refers to a var in a conditional stmt
17071654
LabeledConditionalStmt *conditionalStmt = nullptr;
1708-
if (auto var = dyn_cast<VarDecl>(selfDecl)) {
1655+
if (auto var = dyn_cast<VarDecl>(DRE->getDecl())) {
17091656
if (auto parentStmt = var->getParentPatternStmt()) {
17101657
conditionalStmt = dyn_cast<LabeledConditionalStmt>(parentStmt);
17111658
}
@@ -2656,10 +2603,6 @@ class VarDeclUsageChecker : public ASTWalker {
26562603
/// occur in, when they are in a pattern in a StmtCondition.
26572604
llvm::SmallDenseMap<VarDecl*, LabeledConditionalStmt*> StmtConditionForVD;
26582605

2659-
/// The stack of closure exprs for the current position in the AST.
2660-
/// The top item on the stack, if present, is the current closure scope.
2661-
SmallVector<AbstractClosureExpr *, 4> ClosureStack;
2662-
26632606
#ifndef NDEBUG
26642607
llvm::SmallPtrSet<Expr*, 32> AllExprsSeen;
26652608
#endif
@@ -2726,44 +2669,6 @@ class VarDeclUsageChecker : public ASTWalker {
27262669
VarDecls[vd] |= Flag;
27272670
}
27282671

2729-
/// The `ValueDecl` to use when performing usage analysis of the given DRE.
2730-
/// When inside a closure that requires manual lookup for implcit self decls
2731-
/// (a closure that captures self weakly in Swift 5 language modes), we
2732-
/// look up the `ValueDecl` that implicit self refers to according to
2733-
/// Swift 6 semantics.
2734-
ValueDecl *getDeclForUsageAnalysis(DeclRefExpr *DRE) {
2735-
if (ClosureStack.size() == 0) {
2736-
return DRE->getDecl();
2737-
}
2738-
2739-
auto closure = ClosureStack[ClosureStack.size() - 1];
2740-
assert(closure);
2741-
2742-
if (isImplicitSelf(DRE)) {
2743-
// Implicit self DREs have different semantics in Swift 5 language modes
2744-
// than they have in Swift 6 mode. Take this example:
2745-
//
2746-
// doVoidStuff { [weak self] in
2747-
// guard let self else { return }
2748-
// method()
2749-
// }
2750-
//
2751-
// In Swift 6, the implicit self DRE refers to the self decl
2752-
// defined in the guard statement. In Swift 5 mode, the implicit
2753-
// self DRE instead refers to closure's self `ParamDecl`.
2754-
//
2755-
// If we perform usage analysis using Swift 5 semantics, we'd
2756-
// get a confusing "value 'self' was defined but never used;
2757-
// consider replacing with boolean test" warning. To suppress this
2758-
// warning, we instead perform usage analysis using Swift 6 semantics
2759-
// (by performing a manual lookup of what implicit self refers to
2760-
// at this location according to Swift 6 semantics).
2761-
return lookupSelfDeclIfNecessary(DRE, closure);
2762-
} else {
2763-
return DRE->getDecl();
2764-
}
2765-
}
2766-
27672672
void markBaseOfStorageUse(Expr *E, ConcreteDeclRef decl, unsigned flags);
27682673
void markBaseOfStorageUse(Expr *E, bool isMutating);
27692674

@@ -2862,7 +2767,6 @@ class VarDeclUsageChecker : public ASTWalker {
28622767

28632768
/// The heavy lifting happens when visiting expressions.
28642769
PreWalkResult<Expr *> walkToExprPre(Expr *E) override;
2865-
PostWalkResult<Expr *> walkToExprPost(Expr *E) override;
28662770

28672771
/// handle #if directives.
28682772
void handleIfConfig(IfConfigDecl *ICD);
@@ -3675,7 +3579,7 @@ void VarDeclUsageChecker::markStoredOrInOutExpr(Expr *E, unsigned Flags) {
36753579

36763580
// If we found a decl that is being assigned to, then mark it.
36773581
if (auto *DRE = dyn_cast<DeclRefExpr>(E)) {
3678-
addMark(getDeclForUsageAnalysis(DRE), Flags);
3582+
addMark(DRE->getDecl(), Flags);
36793583
return;
36803584
}
36813585

@@ -3760,7 +3664,7 @@ ASTWalker::PreWalkResult<Expr *> VarDeclUsageChecker::walkToExprPre(Expr *E) {
37603664
// If this is a DeclRefExpr found in a random place, it is a load of the
37613665
// vardecl.
37623666
if (auto *DRE = dyn_cast<DeclRefExpr>(E)) {
3763-
addMark(getDeclForUsageAnalysis(DRE), RK_Read);
3667+
addMark(DRE->getDecl(), RK_Read);
37643668

37653669
// If the Expression is a read of a getter, track for diagnostics
37663670
if (auto VD = dyn_cast<VarDecl>(DRE->getDecl())) {
@@ -3818,20 +3722,6 @@ ASTWalker::PreWalkResult<Expr *> VarDeclUsageChecker::walkToExprPre(Expr *E) {
38183722
if (isa<ErrorExpr>(E))
38193723
sawError = true;
38203724

3821-
// Track the current closure in `ClosureStack`
3822-
if (auto closure = dyn_cast<AbstractClosureExpr>(E)) {
3823-
ClosureStack.push_back(closure);
3824-
}
3825-
3826-
return Action::Continue(E);
3827-
}
3828-
3829-
ASTWalker::PostWalkResult<Expr *> VarDeclUsageChecker::walkToExprPost(Expr *E) {
3830-
if (auto closure = dyn_cast<AbstractClosureExpr>(E)) {
3831-
assert(ClosureStack.size() > 0);
3832-
ClosureStack.pop_back();
3833-
}
3834-
38353725
return Action::Continue(E);
38363726
}
38373727

@@ -3851,7 +3741,7 @@ void VarDeclUsageChecker::handleIfConfig(IfConfigDecl *ICD) {
38513741
// conservatively mark it read and written. This will silence "variable
38523742
// unused" and "could be marked let" warnings for it.
38533743
if (auto *DRE = dyn_cast<DeclRefExpr>(E))
3854-
VDUC.addMark(VDUC.getDeclForUsageAnalysis(DRE), RK_Read | RK_Written);
3744+
VDUC.addMark(DRE->getDecl(), RK_Read | RK_Written);
38553745
else if (auto *declRef = dyn_cast<UnresolvedDeclRefExpr>(E)) {
38563746
auto name = declRef->getName();
38573747
auto loc = declRef->getLoc();

test/expr/closure/closures.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -261,12 +261,12 @@ class ExplicitSelfRequiredTest {
261261
// because its `sawError` flag is set to true. To preserve the "capture 'y' was never used" warnings
262262
// above, we put these cases in their own method.
263263
func weakSelfError() {
264-
doVoidStuff({ [weak self] in x += 1 }) // expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}}
265-
doVoidStuffNonEscaping({ [weak self] in x += 1 }) // expected-warning {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}}
266-
doStuff({ [weak self] in x+1 }) // expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}}
267-
doVoidStuff({ [weak self] in _ = method() }) // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}}
268-
doVoidStuffNonEscaping({ [weak self] in _ = method() }) // expected-warning {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}}
269-
doStuff({ [weak self] in method() }) // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}}
264+
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}}
265+
doVoidStuffNonEscaping({ [weak self] in x += 1 }) // expected-warning {{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}}
266+
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}}
267+
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}}
268+
doVoidStuffNonEscaping({ [weak self] in _ = method() }) // expected-warning {{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}}
269+
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}}
270270
}
271271
}
272272

@@ -775,7 +775,7 @@ public class TestImplicitSelfForWeakSelfCapture {
775775
}
776776

777777
doVoidStuff { [weak self] in
778-
guard let self = self ?? TestImplicitSelfForWeakSelfCapture.staticOptional else { return }
778+
guard let self = self ?? TestImplicitSelfForWeakSelfCapture.staticOptional else { return } // expected-warning {{value 'self' was defined but never used; consider replacing with boolean test}}
779779
method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}}
780780
}
781781

@@ -811,7 +811,7 @@ public class TestImplicitSelfForWeakSelfCapture {
811811
}
812812

813813
doVoidStuffNonEscaping { [weak self] in
814-
guard let self = self ?? TestImplicitSelfForWeakSelfCapture.staticOptional else { return }
814+
guard let self = self ?? TestImplicitSelfForWeakSelfCapture.staticOptional else { return } // expected-warning {{value 'self' was defined but never used; consider replacing with boolean test}}
815815
method() // expected-warning {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}}
816816
}
817817
}

0 commit comments

Comments
 (0)