Skip to content

Commit 3aa133f

Browse files
authored
Merge pull request #70575 from calda/cal--fix-70089
[SE-0365] Fix issue where implicit self was unexpectedly disallowed in nested closures
2 parents cae8298 + da144f7 commit 3aa133f

File tree

12 files changed

+1764
-235
lines changed

12 files changed

+1764
-235
lines changed

include/swift/AST/DeclContext.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ namespace swift {
8282
class SerializedTopLevelCodeDecl;
8383
class StructDecl;
8484
class AccessorDecl;
85+
class ClosureExpr;
8586

8687
template <typename T>
8788
struct AvailableDuringLoweringDeclFilter;
@@ -504,6 +505,14 @@ class alignas(1 << DeclContextAlignInBits) DeclContext
504505
const_cast<DeclContext *>(this)->getInnermostSkippedFunctionContext();
505506
}
506507

508+
/// Returns the innermost context that is a ClosureExpr, which defines how
509+
/// self behaves, unless within a type context that redefines self.
510+
LLVM_READONLY
511+
ClosureExpr *getInnermostClosureForSelfCapture();
512+
const ClosureExpr *getInnermostClosureForSelfCapture() const {
513+
return const_cast<DeclContext *>(this)->getInnermostClosureForSelfCapture();
514+
}
515+
507516
/// Returns the semantic parent of this context. A context has a
508517
/// parent if and only if it is not a module context.
509518
DeclContext *getParent() const {

include/swift/AST/Expr.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4393,7 +4393,7 @@ struct CaptureListEntry {
43934393
explicit CaptureListEntry(PatternBindingDecl *PBD);
43944394

43954395
VarDecl *getVar() const;
4396-
bool isSimpleSelfCapture() const;
4396+
bool isSimpleSelfCapture(bool excludeWeakCaptures = true) const;
43974397
};
43984398

43994399
/// CaptureListExpr - This expression represents the capture list on an explicit

include/swift/AST/Stmt.h

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -709,9 +709,13 @@ class alignas(1 << PatternAlignInBits) StmtConditionElement {
709709
}
710710

711711
/// Whether or not this conditional stmt rebinds self with a `let self`
712-
/// or `let self = self` condition. If `requireLoadExpr` is `true`,
713-
/// additionally requires that the RHS of the self condition is a `LoadExpr`.
714-
bool rebindsSelf(ASTContext &Ctx, bool requireLoadExpr = false) const;
712+
/// or `let self = self` condition.
713+
/// - If `requiresCaptureListRef` is `true`, additionally requires that the
714+
/// RHS of the self condition references a var defined in a capture list.
715+
/// - If `requireLoadExpr` is `true`, additionally requires that the RHS of
716+
/// the self condition is a `LoadExpr`.
717+
bool rebindsSelf(ASTContext &Ctx, bool requiresCaptureListRef = false,
718+
bool requireLoadExpr = false) const;
715719

716720
SourceLoc getStartLoc() const;
717721
SourceLoc getEndLoc() const;
@@ -817,9 +821,13 @@ class LabeledConditionalStmt : public LabeledStmt {
817821
StmtCondition *getCondPointer() { return &Cond; }
818822

819823
/// Whether or not this conditional stmt rebinds self with a `let self`
820-
/// or `let self = self` condition. If `requireLoadExpr` is `true`,
821-
/// additionally requires that the RHS of the self condition is a `LoadExpr`.
822-
bool rebindsSelf(ASTContext &Ctx, bool requireLoadExpr = false) const;
824+
/// or `let self = self` condition.
825+
/// - If `requiresCaptureListRef` is `true`, additionally requires that the
826+
/// RHS of the self condition references a var defined in a capture list.
827+
/// - If `requireLoadExpr` is `true`, additionally requires that the RHS of
828+
/// the self condition is a `LoadExpr`.
829+
bool rebindsSelf(ASTContext &Ctx, bool requiresCaptureListRef = false,
830+
bool requireLoadExpr = false) const;
823831

824832
static bool classof(const Stmt *S) {
825833
return S->getKind() >= StmtKind::First_LabeledConditionalStmt &&

lib/AST/DeclContext.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,25 @@ DeclContext *DeclContext::getInnermostSkippedFunctionContext() {
263263
return nullptr;
264264
}
265265

266+
ClosureExpr *DeclContext::getInnermostClosureForSelfCapture() {
267+
auto dc = this;
268+
if (auto closure = dyn_cast<ClosureExpr>(dc)) {
269+
return closure;
270+
}
271+
272+
// Stop searching if we find a type decl, since types always
273+
// redefine what 'self' means, even when nested inside a closure.
274+
if (dc->isTypeContext()) {
275+
return nullptr;
276+
}
277+
278+
if (auto parent = dc->getParent()) {
279+
return parent->getInnermostClosureForSelfCapture();
280+
}
281+
282+
return nullptr;
283+
}
284+
266285
DeclContext *DeclContext::getParentForLookup() const {
267286
if (isa<ExtensionDecl>(this)) {
268287
// If we are inside an extension, skip directly

lib/AST/Expr.cpp

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1353,22 +1353,29 @@ VarDecl *CaptureListEntry::getVar() const {
13531353
return PBD->getSingleVar();
13541354
}
13551355

1356-
bool CaptureListEntry::isSimpleSelfCapture() const {
1356+
bool CaptureListEntry::isSimpleSelfCapture(bool excludeWeakCaptures) const {
13571357
auto *Var = getVar();
13581358
auto &ctx = Var->getASTContext();
13591359

13601360
if (Var->getName() != ctx.Id_self)
13611361
return false;
13621362

1363-
if (auto *attr = Var->getAttrs().getAttribute<ReferenceOwnershipAttr>())
1364-
if (attr->get() == ReferenceOwnership::Weak)
1365-
return false;
1366-
13671363
if (PBD->getPatternList().size() != 1)
13681364
return false;
13691365

13701366
auto *expr = PBD->getInit(0);
13711367

1368+
if (auto *attr = Var->getAttrs().getAttribute<ReferenceOwnershipAttr>()) {
1369+
if (attr->get() == ReferenceOwnership::Weak && excludeWeakCaptures) {
1370+
return false;
1371+
}
1372+
}
1373+
1374+
// Look through any ImplicitConversionExpr that may contain the DRE
1375+
if (auto conversion = dyn_cast_or_null<ImplicitConversionExpr>(expr)) {
1376+
expr = conversion->getSubExpr();
1377+
}
1378+
13721379
if (auto *DRE = dyn_cast<DeclRefExpr>(expr)) {
13731380
if (auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
13741381
return VD->getName() == ctx.Id_self;

lib/AST/Stmt.cpp

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -526,19 +526,28 @@ void LabeledConditionalStmt::setCond(StmtCondition e) {
526526
}
527527

528528
/// Whether or not this conditional stmt rebinds self with a `let self`
529-
/// or `let self = self` condition. If `requireLoadExpr` is `true`,
530-
/// additionally requires that the RHS of the self condition is a `LoadExpr`.
529+
/// or `let self = self` condition.
530+
/// - If `requiresCaptureListRef` is `true`, additionally requires that the
531+
/// RHS of the self condition references a var defined in a capture list.
532+
/// - If `requireLoadExpr` is `true`, additionally requires that the RHS of
533+
/// the self condition is a `LoadExpr`.
531534
bool LabeledConditionalStmt::rebindsSelf(ASTContext &Ctx,
535+
bool requiresCaptureListRef,
532536
bool requireLoadExpr) const {
533-
return llvm::any_of(getCond(), [&Ctx, requireLoadExpr](const auto &cond) {
534-
return cond.rebindsSelf(Ctx, requireLoadExpr);
537+
return llvm::any_of(getCond(), [&Ctx, requiresCaptureListRef,
538+
requireLoadExpr](const auto &cond) {
539+
return cond.rebindsSelf(Ctx, requiresCaptureListRef, requireLoadExpr);
535540
});
536541
}
537542

538543
/// Whether or not this conditional stmt rebinds self with a `let self`
539-
/// or `let self = self` condition. If `requireLoadExpr` is `true`,
540-
/// additionally requires that the RHS of the self condition is a `LoadExpr`.
544+
/// or `let self = self` condition.
545+
/// - If `requiresCaptureListRef` is `true`, additionally requires that the
546+
/// RHS of the self condition references a var defined in a capture list.
547+
/// - If `requireLoadExpr` is `true`, additionally requires that the RHS of
548+
/// the self condition is a `LoadExpr`.
541549
bool StmtConditionElement::rebindsSelf(ASTContext &Ctx,
550+
bool requiresCaptureListRef,
542551
bool requireLoadExpr) const {
543552
auto pattern = getPatternOrNull();
544553
if (!pattern) {
@@ -578,8 +587,22 @@ bool StmtConditionElement::rebindsSelf(ASTContext &Ctx,
578587
if (auto *DRE = dyn_cast<DeclRefExpr>(
579588
exprToCheckForDRE->getSemanticsProvidingExpr())) {
580589
auto *decl = DRE->getDecl();
581-
return decl && decl->hasName() ? decl->getName().isSimpleName(Ctx.Id_self)
582-
: false;
590+
591+
bool definedInCaptureList = false;
592+
if (auto varDecl = dyn_cast_or_null<VarDecl>(DRE->getDecl())) {
593+
definedInCaptureList = varDecl->isCaptureList();
594+
}
595+
596+
if (requiresCaptureListRef && !definedInCaptureList) {
597+
return false;
598+
}
599+
600+
bool isVariableNamedSelf = false;
601+
if (decl && decl->hasName()) {
602+
isVariableNamedSelf = decl->getName().isSimpleName(Ctx.Id_self);
603+
}
604+
605+
return isVariableNamedSelf;
583606
}
584607

585608
return false;

lib/AST/UnqualifiedLookup.cpp

Lines changed: 36 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -333,8 +333,7 @@ UnqualifiedLookupFactory::getBaseDeclForResult(const DeclContext *baseDC) const
333333
/// unwrapping condition (e.g. `guard let self else { return }`).
334334
/// If this is true, then we know any implicit self reference in the
335335
/// following scope is guaranteed to be non-optional.
336-
static bool implicitSelfReferenceIsUnwrapped(const ValueDecl *selfDecl,
337-
const AbstractClosureExpr *inClosure) {
336+
static bool implicitSelfReferenceIsUnwrapped(const ValueDecl *selfDecl) {
338337
ASTContext &Ctx = selfDecl->getASTContext();
339338

340339
// Check if the implicit self decl refers to a var in a conditional stmt
@@ -352,71 +351,40 @@ static bool implicitSelfReferenceIsUnwrapped(const ValueDecl *selfDecl,
352351
return conditionalStmt->rebindsSelf(Ctx);
353352
}
354353

355-
// Finds the nearest parent closure, which would define the
356-
// permitted usage of implicit self. In closures this is most
357-
// often just `dc` itself, but in functions defined in the
358-
// closure body this would be some parent context.
359-
static ClosureExpr *closestParentClosure(DeclContext *dc) {
360-
if (!dc) {
361-
return nullptr;
362-
}
363-
364-
if (auto closure = dyn_cast<ClosureExpr>(dc)) {
365-
return closure;
366-
}
367-
368-
// Stop searching if we find a type decl, since types always
369-
// redefine what 'self' means, even when nested inside a closure.
370-
if (dc->getContextKind() == DeclContextKind::GenericTypeDecl) {
371-
return nullptr;
372-
}
373-
374-
return closestParentClosure(dc->getParent());
375-
}
376-
377354
ValueDecl *UnqualifiedLookupFactory::lookupBaseDecl(const DeclContext *baseDC) const {
378355
// Perform an unqualified lookup for the base decl of this result. This
379356
// handles cases where self was rebound (e.g. `guard let self = self`)
380-
// earlier in the scope.
381-
//
382-
// Only do this in closures that capture self weakly, since implicit self
383-
// isn't allowed to be rebound in other contexts. In other contexts, implicit
384-
// self _always_ refers to the context's self `ParamDecl`, even if there
385-
// is another local decl with the name `self` that would be found by
386-
// `lookupSingleLocalDecl`.
387-
auto closureExpr = closestParentClosure(DC);
357+
// earlier in this closure or some outer closure.
358+
auto closureExpr = DC->getInnermostClosureForSelfCapture();
388359
if (!closureExpr) {
389360
return nullptr;
390361
}
391362

392-
bool capturesSelfWeakly = false;
393-
if (auto decl = closureExpr->getCapturedSelfDecl()) {
394-
if (auto a = decl->getAttrs().getAttribute<ReferenceOwnershipAttr>()) {
395-
capturesSelfWeakly = a->get() == ReferenceOwnership::Weak;
396-
}
397-
}
398-
399-
if (!capturesSelfWeakly) {
400-
return nullptr;
401-
}
402-
403363
auto selfDecl = ASTScope::lookupSingleLocalDecl(
404364
DC->getParentSourceFile(), DeclName(Ctx.Id_self), Loc);
405365

406366
if (!selfDecl) {
407367
return nullptr;
408368
}
409369

370+
bool capturesSelfWeakly = false;
371+
if (auto decl = closureExpr->getCapturedSelfDecl()) {
372+
if (auto a = decl->getAttrs().getAttribute<ReferenceOwnershipAttr>()) {
373+
capturesSelfWeakly = a->get() == ReferenceOwnership::Weak;
374+
}
375+
}
376+
410377
// In Swift 5 mode, implicit self is allowed within non-escaping
411-
// closures even before self is unwrapped. For example, this is allowed:
378+
// `weak self` closures even before self is unwrapped.
379+
// For example, this is allowed:
412380
//
413381
// doVoidStuffNonEscaping { [weak self] in
414382
// method() // implicitly `self.method()`
415383
// }
416384
//
417385
// To support this, we have to preserve the lookup behavior from
418386
// Swift 5.7 and earlier where implicit self defaults to the closure's
419-
// `ParamDecl`. This causes the closure to capture self strongly, however,
387+
// `ParamDecl`. This causes the closure to capture self strongly,
420388
// which is not acceptable for escaping closures.
421389
//
422390
// Escaping closures, however, only need to permit implicit self once
@@ -430,8 +398,29 @@ ValueDecl *UnqualifiedLookupFactory::lookupBaseDecl(const DeclContext *baseDC) c
430398
// In these cases, using the Swift 6 lookup behavior doesn't affect
431399
// how the body is type-checked, so it can be used in Swift 5 mode
432400
// without breaking source compatibility for non-escaping closures.
433-
if (!Ctx.LangOpts.isSwiftVersionAtLeast(6) &&
434-
!implicitSelfReferenceIsUnwrapped(selfDecl, closureExpr)) {
401+
if (capturesSelfWeakly && !Ctx.LangOpts.isSwiftVersionAtLeast(6) &&
402+
!implicitSelfReferenceIsUnwrapped(selfDecl)) {
403+
return nullptr;
404+
}
405+
406+
// Closures are only allowed to rebind self in specific circumstances:
407+
// 1. In a capture list with an explicit capture.
408+
// 2. In a `guard let self = self` / `if let self = self` condition
409+
// in a closure that captures self weakly.
410+
//
411+
// These rebindings can be done by any parent closure, and apply
412+
// to all nested closures. We only need to check these structural
413+
// requirements loosely here -- more extensive validation happens
414+
// later in MiscDiagnostics::diagnoseImplicitSelfUseInClosure.
415+
//
416+
// Other types of rebindings, like an arbitrary "let `self` = foo",
417+
// are never allowed to rebind self.
418+
auto selfVD = dyn_cast<VarDecl>(selfDecl);
419+
if (!selfVD) {
420+
return nullptr;
421+
}
422+
423+
if (!(selfVD->isCaptureList() || selfVD->getParentPatternStmt())) {
435424
return nullptr;
436425
}
437426

0 commit comments

Comments
 (0)