diff --git a/CHANGELOG.md b/CHANGELOG.md index 3116a4c8f5b18..92bc39b389b35 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,48 @@ CHANGELOG _**Note:** This is in reverse chronological order, so newer entries are added to the top._ +## Swift 6.0 + +* [SE-0365][]: + + Implicit `self` is now permitted for `weak self` captures, after `self` is unwrapped. + + For example, the usage of implicit `self` below is now permitted: + + ```swift + class ViewController { + let button: Button + + func setup() { + button.tapHandler = { [weak self] in + guard let self else { return } + dismiss() // refers to `self.dismiss()` + } + } + + func dismiss() { ... } + } + ``` + + In Swift 5 language modes, implicit `self` is permitted for `weak self` captures in _non-escaping_ closures even before `self` is unwrapped. For example, this code compiles successfully in Swift 5 language mode: + + ```swift + class ExampleClass { + func makeArray() -> [String] { + // `Array.map` takes a non-escaping closure: + ["foo", "bar", "baaz"].map { [weak self] string in + double(string) // implicitly refers to `self!.double(string)` + } + } + + func double(_ string: String) -> String { + string + string + } + } + ``` + + In Swift 6, the above code will no longer compile. `weak self` captures in non-escaping closures now have the same behavior as captures in escaping closures (as described in [SE-0365][]). Code relying on the previous behavior will need to be updated to either unwrap `self` (e.g. by adding a `guard let self else return` statement), or to use a different capture method (e.g. using `[self]` or `[unowned self]` instead of `[weak self]`). + ## Swift 5.8 * [SE-0362][]: @@ -9559,6 +9601,7 @@ Swift 1.0 [SE-0357]: [SE-0358]: [SE-0362]: +[SE-0365]: [SR-75]: [SR-106]: diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 9f9eec5c5467b..c6954cc7b579b 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -1201,6 +1201,12 @@ NOTE(optional_key_path_root_base_chain, none, NOTE(optional_key_path_root_base_unwrap, none, "unwrap the optional using '!.' to access unwrapped type member %0", (DeclNameRef)) + +ERROR(optional_self_not_unwrapped,none, + "explicit use of 'self' is required when 'self' is optional, " + "to make control flow explicit", ()) +NOTE(optional_self_chain,none, + "reference 'self?.' explicitly", ()) ERROR(missing_unwrap_optional_try,none, "value of optional type %0 not unwrapped; did you mean to use 'try!' " @@ -3929,8 +3935,6 @@ NOTE(note_reference_self_explicitly,none, NOTE(note_other_self_capture,none, "variable other than 'self' captured here under the name 'self' does not " "enable implicit 'self'", ()) -NOTE(note_self_captured_weakly,none, - "weak capture of 'self' here does not enable implicit 'self'", ()) ERROR(implicit_use_of_self_in_closure,none, "implicit use of 'self' in closure; use 'self.' to make" " capture semantics explicit", ()) diff --git a/include/swift/AST/NameLookup.h b/include/swift/AST/NameLookup.h index d8b9bfe306958..9133b0ba7d18a 100644 --- a/include/swift/AST/NameLookup.h +++ b/include/swift/AST/NameLookup.h @@ -103,15 +103,20 @@ struct LookupResultEntry { /// extension (if it found something at that level). DeclContext *BaseDC; + /// The declaration that defines the base of the call to `Value`. + /// This is always available, as long as `BaseDC` is not null. + ValueDecl *BaseDecl; + /// The declaration corresponds to the given name; i.e. the decl we are /// looking up. ValueDecl *Value; public: - LookupResultEntry(ValueDecl *value) : BaseDC(nullptr), Value(value) {} + LookupResultEntry(ValueDecl *value) + : BaseDC(nullptr), BaseDecl(nullptr), Value(value) {} - LookupResultEntry(DeclContext *baseDC, ValueDecl *value) - : BaseDC(baseDC), Value(value) {} + LookupResultEntry(DeclContext *baseDC, ValueDecl *baseDecl, ValueDecl *value) + : BaseDC(baseDC), BaseDecl(baseDecl), Value(value) {} ValueDecl *getValueDecl() const { return Value; } diff --git a/include/swift/AST/Stmt.h b/include/swift/AST/Stmt.h index c2ac3af8ba02a..174ad2c42eeef 100644 --- a/include/swift/AST/Stmt.h +++ b/include/swift/AST/Stmt.h @@ -574,7 +574,7 @@ class DoStmt : public LabeledStmt { }; /// Either an "if let" case or a simple boolean expression can appear as the -/// condition of an 'if' or 'while' statement. +/// condition of an 'if', 'guard', or 'while' statement. using StmtCondition = MutableArrayRef; /// This is the common base class between statements that can have labels, and diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index c888081addbb9..9031e90e957aa 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -56,25 +56,7 @@ ValueDecl *LookupResultEntry::getBaseDecl() const { if (BaseDC == nullptr) return nullptr; - if (auto *AFD = dyn_cast(BaseDC)) - return AFD->getImplicitSelfDecl(); - - if (auto *PBI = dyn_cast(BaseDC)) { - auto *selfDecl = PBI->getImplicitSelfDecl(); - assert(selfDecl); - return selfDecl; - } - - if (auto *CE = dyn_cast(BaseDC)) { - auto *selfDecl = CE->getCapturedSelfDecl(); - assert(selfDecl); - assert(selfDecl->isSelfParamCapture()); - return selfDecl; - } - - auto *nominalDecl = BaseDC->getSelfNominalTypeDecl(); - assert(nominalDecl); - return nominalDecl; + return BaseDecl; } void LookupResult::filter( diff --git a/lib/AST/UnqualifiedLookup.cpp b/lib/AST/UnqualifiedLookup.cpp index 207a2338bf1f5..1f775ea6eff1d 100644 --- a/lib/AST/UnqualifiedLookup.cpp +++ b/lib/AST/UnqualifiedLookup.cpp @@ -19,6 +19,7 @@ #include "swift/AST/ASTVisitor.h" #include "swift/AST/DebuggerClient.h" #include "swift/AST/ImportCache.h" +#include "swift/AST/Initializer.h" #include "swift/AST/ModuleNameLookup.h" #include "swift/AST/NameLookup.h" #include "swift/AST/NameLookupRequests.h" @@ -76,6 +77,8 @@ namespace { private: SelfBounds findSelfBounds(const DeclContext *dc); + ValueDecl *lookupBaseDecl(const DeclContext *baseDC) const; + ValueDecl *getBaseDeclForResult(const DeclContext *baseDC) const; // Classify this declaration. // Types are formally members of the metatype. @@ -343,14 +346,84 @@ void UnqualifiedLookupFactory::ResultFinderForTypeContext::findResults( SmallVector Lookup; contextForLookup->lookupQualified(selfBounds, Name, baseNLOptions, Lookup); for (auto Result : Lookup) { - results.emplace_back(const_cast(whereValueIsMember(Result)), - Result); + auto baseDC = const_cast(whereValueIsMember(Result)); + auto baseDecl = getBaseDeclForResult(baseDC); + results.emplace_back(baseDC, baseDecl, Result); #ifndef NDEBUG factory->addedResult(results.back()); #endif } } +ValueDecl * +UnqualifiedLookupFactory::ResultFinderForTypeContext::getBaseDeclForResult( + const DeclContext *baseDC) const { + if (baseDC == nullptr) { + return nullptr; + } + + if (auto localBaseDecl = lookupBaseDecl(baseDC)) { + return localBaseDecl; + } + + if (auto *AFD = dyn_cast(baseDC)) { + return const_cast(AFD->getImplicitSelfDecl()); + } + + if (auto *PBI = dyn_cast(baseDC)) { + auto *selfDecl = PBI->getImplicitSelfDecl(); + assert(selfDecl); + return selfDecl; + } + + else if (auto *CE = dyn_cast(baseDC)) { + auto *selfDecl = CE->getCapturedSelfDecl(); + assert(selfDecl); + assert(selfDecl->isSelfParamCapture()); + return selfDecl; + } + + auto *nominalDecl = baseDC->getSelfNominalTypeDecl(); + assert(nominalDecl); + return nominalDecl; +} + +ValueDecl *UnqualifiedLookupFactory::ResultFinderForTypeContext::lookupBaseDecl( + const DeclContext *baseDC) const { + // Perform an unqualified lookup for the base decl of this result. This + // handles cases where self was rebound (e.g. `guard let self = self`) + // earlier in the scope. + // + // Only do this in closures that capture self weakly, since implicit self + // isn't allowed to be rebound in other contexts. In other contexts, implicit + // self _always_ refers to the context's self `ParamDecl`, even if there + // is another local decl with the name `self` that would be found by + // `lookupSingleLocalDecl`. + bool isInWeakSelfClosure = false; + if (auto closureExpr = dyn_cast(factory->DC)) { + if (auto decl = closureExpr->getCapturedSelfDecl()) { + if (auto a = decl->getAttrs().getAttribute()) { + isInWeakSelfClosure = a->get() == ReferenceOwnership::Weak; + } + } + } + + // We can only change the behavior of lookup in Swift 6 and later, + // due to a bug in Swift 5 where implicit self is always allowed + // for weak self captures in non-escaping closures. + if (!factory->Ctx.LangOpts.isSwiftVersionAtLeast(6)) { + return nullptr; + } + + if (isInWeakSelfClosure) { + return ASTScope::lookupSingleLocalDecl(factory->DC->getParentSourceFile(), + DeclName(factory->Ctx.Id_self), + factory->Loc); + } + + return nullptr; +} + // TODO (someday): Instead of adding unavailable entries to Results, // then later shunting them aside, just put them in the right place // to begin with. diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 39fe1e1511dc5..2d382752d2869 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -1371,6 +1371,22 @@ bool MemberAccessOnOptionalBaseFailure::diagnoseAsError() { .fixItInsert(sourceRange.End, "!."); } } else { + // Check whether or not the base of this optional unwrap is implicit self + // This can only happen with a [weak self] capture, and is not permitted. + if (auto dotExpr = getAsExpr(locator->getAnchor())) { + if (auto baseDeclRef = dyn_cast(dotExpr->getBase())) { + ASTContext &Ctx = baseDeclRef->getDecl()->getASTContext(); + if (baseDeclRef->isImplicit() && + baseDeclRef->getDecl()->getName().isSimpleName(Ctx.Id_self)) { + emitDiagnostic(diag::optional_self_not_unwrapped); + + emitDiagnostic(diag::optional_self_chain) + .fixItInsertAfter(sourceRange.End, "self?."); + return true; + } + } + } + emitDiagnostic(diag::optional_base_not_unwrapped, baseType, Member, unwrappedBaseType); diff --git a/lib/Sema/MiscDiagnostics.cpp b/lib/Sema/MiscDiagnostics.cpp index 0c5a4d0e7e900..a2961b3c3cf74 100644 --- a/lib/Sema/MiscDiagnostics.cpp +++ b/lib/Sema/MiscDiagnostics.cpp @@ -1566,9 +1566,29 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E, if (!DRE || !DRE->isImplicit()) return false; - auto var = dyn_cast(DRE->getDecl()); - if (!var || !isEnclosingSelfReference(var, inClosure)) + // If this decl isn't named "self", then it isn't an implicit self capture + // and we have no reason to reject it. + ASTContext &Ctx = DRE->getDecl()->getASTContext(); + if (!DRE->getDecl()->getName().isSimpleName(Ctx.Id_self)) { return false; + } + + // If this is an explicit `weak self` capture, then implicit self is + // allowed once the closure's self param is unwrapped. We need to validate + // that the unwrapped `self` decl specifically refers to an unwrapped copy + // of the closure's `self` param, and not something else like in `guard + // let self = .someOptionalVariable else { return }` or `let self = + // someUnrelatedVariable`. If self hasn't been unwrapped yet and is still + // an optional, we would have already hit an error elsewhere. + // + // We can only enable this behavior in Swift 6 and later, due to a + // bug in Swift 5 where implicit self was always allowed for + // weak self captures (even before self was unwrapped) in + // non-escaping closures. + if (Ctx.LangOpts.isSwiftVersionAtLeast(6) && + closureHasWeakSelfCapture(inClosure)) { + return !implicitWeakSelfReferenceIsValid(DRE); + } // Defensive check for type. If the expression doesn't have type here, it // should have been diagnosed somewhere else. @@ -1586,13 +1606,85 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E, if (!ty->hasReferenceSemantics()) return false; + if (auto closureExpr = dyn_cast(inClosure)) { + if (auto selfDecl = closureExpr->getCapturedSelfDecl()) { + // If this capture is using the name `self` actually referring + // to some other variable (e.g. with `[self = "hello"]`) + // then implicit self is not allowed. + if (!selfDecl->isSelfParamCapture()) { + return true; + } + } + } + + if (auto var = dyn_cast(DRE->getDecl())) { + if (!isEnclosingSelfReference(var, inClosure)) { + return false; + } + } + return true; } + static bool implicitWeakSelfReferenceIsValid(const DeclRefExpr *DRE) { + ASTContext &Ctx = DRE->getDecl()->getASTContext(); + + // Check if the implicit self decl refers to a var in a conditional stmt + LabeledConditionalStmt *conditionalStmt = nullptr; + if (auto var = dyn_cast(DRE->getDecl())) { + if (auto parentStmt = var->getParentPatternStmt()) { + conditionalStmt = dyn_cast(parentStmt); + } + } + + if (conditionalStmt == nullptr) { + return false; + } + + // Find the condition that defined the self decl, + // and check that both its LHS and RHS are 'self' + for (auto cond : conditionalStmt->getCond()) { + if (auto OSP = dyn_cast(cond.getPattern())) { + if (OSP->getSubPattern()->getBoundName() != Ctx.Id_self) { + continue; + } + + if (auto LE = dyn_cast(cond.getInitializer())) { + if (auto selfDRE = dyn_cast(LE->getSubExpr())) { + return (selfDRE->getDecl()->getName().isSimpleName(Ctx.Id_self)); + } + } + } + } + + return false; + } + + static bool closureHasWeakSelfCapture(const AbstractClosureExpr *ACE) { + if (auto closureExpr = dyn_cast(ACE)) { + if (auto selfDecl = closureExpr->getCapturedSelfDecl()) { + return selfDecl->getType()->is(); + } + } + + return false; + } + /// Return true if this is a closure expression that will require explicit /// use or capture of "self." for qualification of member references. - static bool isClosureRequiringSelfQualification( - const AbstractClosureExpr *CE) { + static bool + isClosureRequiringSelfQualification(const AbstractClosureExpr *CE, + ASTContext &Ctx) { + // If this closure capture self weakly, then we have to validate each + // usage of implicit self individually, even in a nonescaping closure. + // + // We can only do this in Swift 6 mode, since we didn't do this in Swift 5 + // (and changing this behavior causes new errors to be emitted). + if (Ctx.LangOpts.isSwiftVersionAtLeast(6) && + closureHasWeakSelfCapture(CE)) { + return true; + } + // If the closure's type was inferred to be noescape, then it doesn't // need qualification. if (AnyFunctionRef(const_cast(CE)) @@ -1622,7 +1714,7 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E, if (auto *CE = dyn_cast(E)) { // If this is a potentially-escaping closure expression, start looking // for references to self if we aren't already. - if (isClosureRequiringSelfQualification(CE)) + if (isClosureRequiringSelfQualification(CE, Ctx)) Closures.push_back(CE); } @@ -1642,9 +1734,9 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E, // We know that isImplicitSelfParamUseLikelyToCauseCycle is true, // which means all these casts are valid. return !cast(cast(selfRef)->getDecl()) - ->isSelfParameter(); + ->isSelfParameter(); }; - + SourceLoc memberLoc = SourceLoc(); if (auto *MRE = dyn_cast(E)) if (isImplicitSelfParamUseLikelyToCauseCycle(MRE->getBase(), ACE)) { @@ -1673,7 +1765,6 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E, return { false, E }; } - // Catch any other implicit uses of self with a generic diagnostic. if (isImplicitSelfParamUseLikelyToCauseCycle(E, ACE)) Diags.diagnose(E->getLoc(), diag::implicit_use_of_self_in_closure) .warnUntilSwiftVersionIf(shouldOnlyWarn(E), 6); @@ -1683,7 +1774,7 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E, Expr *walkToExprPost(Expr *E) override { if (auto *CE = dyn_cast(E)) { - if (isClosureRequiringSelfQualification(CE)) { + if (isClosureRequiringSelfQualification(CE, Ctx)) { assert(Closures.size() > 0); Closures.pop_back(); } @@ -1704,7 +1795,6 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E, // capture 'self' explicitly will result in an error, and using // 'self.' explicitly will be accessing something other than the // self param. - // FIXME: We could offer a special fixit in the [weak self] case to insert 'self?.'... return; } emitFixItsForExplicitClosure(Diags, memberLoc, CE); @@ -1725,11 +1815,7 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E, // If we've already captured something with the name "self" other than // the actual self param, offer special diagnostics. if (auto *VD = closureExpr->getCapturedSelfDecl()) { - // Either this is a weak capture of self... - if (VD->getType()->is()) { - Diags.diagnose(VD->getLoc(), diag::note_self_captured_weakly); - // ...or something completely different. - } else { + if (!VD->getType()->is()) { Diags.diagnose(VD->getLoc(), diag::note_other_self_capture); } @@ -1811,16 +1897,16 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E, } }; + auto &ctx = DC->getASTContext(); AbstractClosureExpr *ACE = nullptr; if (DC->isLocalContext()) { while (DC->getParent()->isLocalContext() && !ACE) { if (auto *closure = dyn_cast(DC)) - if (DiagnoseWalker::isClosureRequiringSelfQualification(closure)) + if (DiagnoseWalker::isClosureRequiringSelfQualification(closure, ctx)) ACE = const_cast(closure); DC = DC->getParent(); } } - auto &ctx = DC->getASTContext(); const_cast(E)->walk(DiagnoseWalker(ctx, ACE)); } @@ -2512,7 +2598,7 @@ class VarDeclUsageChecker : public ASTWalker { VarDecls[vd] |= Flag; } - + void markBaseOfStorageUse(Expr *E, ConcreteDeclRef decl, unsigned flags); void markBaseOfStorageUse(Expr *E, bool isMutating); @@ -3193,7 +3279,7 @@ VarDeclUsageChecker::~VarDeclUsageChecker() { auto initExpr = SC->getCond()[0].getInitializer(); if (initExpr->getStartLoc().isValid()) { unsigned noParens = initExpr->canAppendPostfixExpression(); - + // If the subexpr is an "as?" cast, we can rewrite it to // be an "is" test. ConditionalCheckedCastExpr *CCE = nullptr; @@ -3574,7 +3660,7 @@ void VarDeclUsageChecker::handleIfConfig(IfConfigDecl *ICD) { // conservatively mark it read and written. This will silence "variable // unused" and "could be marked let" warnings for it. if (auto *DRE = dyn_cast(E)) - VDUC.addMark(DRE->getDecl(), RK_Read|RK_Written); + VDUC.addMark(DRE->getDecl(), RK_Read | RK_Written); else if (auto *declRef = dyn_cast(E)) { auto name = declRef->getName(); auto loc = declRef->getLoc(); diff --git a/lib/Sema/PreCheckExpr.cpp b/lib/Sema/PreCheckExpr.cpp index 95b58449bf3a8..24e2ec469cd75 100644 --- a/lib/Sema/PreCheckExpr.cpp +++ b/lib/Sema/PreCheckExpr.cpp @@ -856,14 +856,14 @@ namespace { /// Update a direct callee expression node that has a function reference kind /// based on seeing a call to this callee. templategetFunctionRefKind())> + typename = decltype(((E*)nullptr)->getFunctionRefKind())> void tryUpdateDirectCalleeImpl(E *callee, int) { callee->setFunctionRefKind(addingDirectCall(callee->getFunctionRefKind())); } /// Version of tryUpdateDirectCalleeImpl for when the callee /// expression type doesn't carry a reference. - template + template void tryUpdateDirectCalleeImpl(E *callee, ...) { } /// The given expression is the direct callee of a call expression; mark it to diff --git a/lib/Sema/TypeCheckNameLookup.cpp b/lib/Sema/TypeCheckNameLookup.cpp index a957da09715e1..c0215a4894a19 100644 --- a/lib/Sema/TypeCheckNameLookup.cpp +++ b/lib/Sema/TypeCheckNameLookup.cpp @@ -92,18 +92,22 @@ namespace { /// \param baseDC The declaration context through which we found the /// declaration. /// + /// \param baseDecl The declaration that defines the base of the + /// call to `found` + /// /// \param foundInType The type through which we found the /// declaration. /// /// \param isOuter Whether this is an outer result (i.e. a result that isn't /// from the innermost scope with results) - void add(ValueDecl *found, DeclContext *baseDC, Type foundInType, - bool isOuter) { + void add(ValueDecl *found, DeclContext *baseDC, ValueDecl *baseDecl, + Type foundInType, bool isOuter) { DeclContext *foundDC = found->getDeclContext(); auto addResult = [&](ValueDecl *result) { if (Known.insert({{result, baseDC}, false}).second) { - Result.add(LookupResultEntry(baseDC, result), isOuter); + // HERE, need to look up base decl + Result.add(LookupResultEntry(baseDC, baseDecl, result), isOuter); if (isOuter) FoundOuterDecls.push_back(result); else @@ -267,7 +271,8 @@ LookupResult TypeChecker::lookupUnqualified(DeclContext *dc, DeclNameRef name, assert(foundInType && "bogus base declaration?"); } - builder.add(found.getValueDecl(), found.getDeclContext(), foundInType, + builder.add(found.getValueDecl(), found.getDeclContext(), + found.getBaseDecl(), foundInType, /*isOuter=*/idx >= lookup.getIndexOfFirstOuterResult()); } return result; @@ -327,7 +332,7 @@ LookupResult TypeChecker::lookupMember(DeclContext *dc, dc->lookupQualified(type, name, subOptions, lookupResults); for (auto found : lookupResults) - builder.add(found, nullptr, type, /*isOuter=*/false); + builder.add(found, nullptr, nullptr, type, /*isOuter=*/false); return result; } diff --git a/test/expr/closure/closures.swift b/test/expr/closure/closures.swift index 90b9348424d0c..1f8eb3f7b7ae7 100644 --- a/test/expr/closure/closures.swift +++ b/test/expr/closure/closures.swift @@ -176,10 +176,8 @@ class ExplicitSelfRequiredTest { doVoidStuff({ _ = "\(x)"}) // 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}} {{18-18= [self] in}} expected-note{{reference 'self.' explicitly}} {{26-26=self.}} doVoidStuff({ [y = self] in x += 1 }) // expected-warning {{capture 'y' was never used}} 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}} {{20-20=self, }} expected-note{{reference 'self.' explicitly}} {{33-33=self.}} doStuff({ [y = self] in x+1 }) // expected-warning {{capture 'y' was never used}} 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}} {{16-16=self, }} expected-note{{reference 'self.' explicitly}} {{29-29=self.}} - doVoidStuff({ [weak self] in x += 1 }) // expected-note {{weak capture of 'self' here does not enable implicit 'self'}} expected-warning {{variable 'self' was written to, but never read}} expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} - doStuff({ [weak self] in x+1 }) // expected-note {{weak capture of 'self' here does not enable implicit 'self'}} expected-warning {{variable 'self' was written to, but never read}} expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} - doVoidStuff({ [self = self.x] in x += 1 }) // expected-note {{variable other than 'self' captured here under the name 'self' does not enable implicit 'self'}} expected-warning {{capture 'self' was never used}} expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} - doStuff({ [self = self.x] in x+1 }) // expected-note {{variable other than 'self' captured here under the name 'self' does not enable implicit 'self'}} expected-warning {{capture 'self' was never used}} expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} + doVoidStuff({ [self = ExplicitSelfRequiredTest()] in x += 1 }) // expected-note {{variable other than 'self' captured here under the name 'self' does not enable implicit 'self'}} expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-warning {{capture 'self' was never used}} + doStuff({ [self = ExplicitSelfRequiredTest()] in x+1 }) // expected-note {{variable other than 'self' captured here under the name 'self' does not enable implicit 'self'}} expected-error {{reference to property 'x' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-warning {{capture 'self' was never used}} // Methods follow the same rules as properties, uses of 'self' without capturing must be marked with "self." doStuff { method() } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{14-14= [self] in}} expected-note{{reference 'self.' explicitly}} {{15-15=self.}} @@ -188,10 +186,8 @@ class ExplicitSelfRequiredTest { doVoidStuff { () -> () in _ = method() } // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{18-18= [self]}} expected-note{{reference 'self.' explicitly}} {{35-35=self.}} doVoidStuff { [y = self] in _ = method() } // expected-warning {{capture 'y' was never used}} expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{20-20=self, }} expected-note{{reference 'self.' explicitly}} {{37-37=self.}} doStuff({ [y = self] in method() }) // expected-warning {{capture 'y' was never used}} expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{capture 'self' explicitly to enable implicit 'self' in this closure}} {{16-16=self, }} expected-note{{reference 'self.' explicitly}} {{29-29=self.}} - doVoidStuff({ [weak self] in _ = method() }) // expected-note {{weak capture of 'self' here does not enable implicit 'self'}} expected-warning {{variable 'self' was written to, but never read}} expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} - doStuff({ [weak self] in method() }) // expected-note {{weak capture of 'self' here does not enable implicit 'self'}} expected-warning {{variable 'self' was written to, but never read}} expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} - doVoidStuff({ [self = self.x] in _ = method() }) // expected-note {{variable other than 'self' captured here under the name 'self' does not enable implicit 'self'}} expected-warning {{capture 'self' was never used}} expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} - doStuff({ [self = self.x] in method() }) // expected-note {{variable other than 'self' captured here under the name 'self' does not enable implicit 'self'}} expected-warning {{capture 'self' was never used}} expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} + doVoidStuff({ [self = ExplicitSelfRequiredTest()] in _ = method() }) // expected-note {{variable other than 'self' captured here under the name 'self' does not enable implicit 'self'}} expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-warning {{capture 'self' was never used}} + doStuff({ [self = ExplicitSelfRequiredTest()] in method() }) // expected-note {{variable other than 'self' captured here under the name 'self' does not enable implicit 'self'}} expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-warning {{capture 'self' was never used}} doVoidStuff { _ = self.method() } doVoidStuff { [self] in _ = method() } doVoidStuff { [self = self] in _ = method() } @@ -227,7 +223,7 @@ class ExplicitSelfRequiredTest { } // expected-note@+2 {{capture 'self' explicitly to enable implicit 'self' in this closure}} {{14-14= [self] in}} // Note: Trailing whitespace on the following line is intentional and should not be removed! - doStuff { + doStuff { method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note{{reference 'self.' explicitly}} {{7-7=self.}} } // expected-note@+1 {{capture 'self' explicitly to enable implicit 'self' in this closure}} {{14-14= [self] in}} @@ -260,6 +256,15 @@ class ExplicitSelfRequiredTest { return 42 } + + func weakSelfError() { + 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}} + doVoidStuffNonEscaping({ [weak self] in x += 1 }) // expected-warning {{variable 'self' was written to, but never read}} + 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}} + 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}} + doVoidStuffNonEscaping({ [weak self] in _ = method() }) // expected-warning {{variable 'self' was written to, but never read}} + 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}} + } } // If the implicit self is of value type, no diagnostic should be produced. @@ -643,7 +648,7 @@ func callitArgsFn(_ : Int, _ f: () -> () -> T) -> T { f()() } -func callitGenericArg(_ a: T, _ f: () -> T) -> T { +func callitGenericArg(_ a: T, _ f: () -> T) -> T { f() } @@ -659,7 +664,7 @@ func test_55680_Tuple() -> Int { // expected-error@+2{{conflicting arguments to generic parameter 'T' ('()' vs. 'Int')}} // expected-note@+1:3{{generic parameter 'T' inferred as 'Int' from context}} callitTuple(1) { // expected-note@:18{{generic parameter 'T' inferred as '()' from closure return expression}} - (print("hello"), 0) + (print("hello"), 0) } } @@ -675,7 +680,7 @@ func test_55680_Args() -> Int { // expected-error@+2{{conflicting arguments to generic parameter 'T' ('()' vs. 'Int')}} // expected-note@+1:3{{generic parameter 'T' inferred as 'Int' from context}} callitArgs(1) { // expected-note@:17{{generic parameter 'T' inferred as '()' from closure return expression}} - print("hello") + print("hello") } } @@ -683,7 +688,7 @@ func test_55680_ArgsFn() -> Int { // expected-error@+2{{conflicting arguments to generic parameter 'T' ('()' vs. 'Int')}} // expected-note@+1:3{{generic parameter 'T' inferred as 'Int' from context}} callitArgsFn(1) { // expected-note@:19{{generic parameter 'T' inferred as '()' from closure return expression}} - { print("hello") } + { print("hello") } } } @@ -733,6 +738,104 @@ public class TestImplicitCaptureOfExplicitCaptureOfSelfInEscapingClosure { } } +public class TestImplicitSelfForWeakSelfCapture { + static var staticOptional: TestImplicitSelfForWeakSelfCapture? = .init() + func method() { } + + private init() { + doVoidStuff { [weak self] in + method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} + guard let self = self else { return } + method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} + self.method() + } + + doVoidStuff { [weak self] in + if let self = self { + method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} + self.method() + } + } + + doVoidStuff { [weak self] in + guard let self = self else { return } // expected-warning {{value 'self' was defined but never used; consider replacing with boolean test}} + doVoidStuff { // expected-note {{capture 'self' explicitly to enable implicit 'self' in this closure}} + method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note {{reference 'self.' explicitly}} + } + } + + doVoidStuff { [weak self] in + guard let self = self ?? TestImplicitSelfForWeakSelfCapture.staticOptional else { return } + method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} + self.method() + } + + doVoidStuffNonEscaping { [weak self] in + method() + guard let self = self else { return } + method() + self.method() + } + + doVoidStuffNonEscaping { [weak self] in + if let self = self { + method() + self.method() + } + } + + doVoidStuff { [weak self] in + let `self`: TestImplicitSelfForWeakSelfCapture? = self ?? TestImplicitSelfForWeakSelfCapture.staticOptional + guard let self = self else { return } // expected-warning {{value 'self' was defined but never used; consider replacing with boolean test}} + method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} + } + + doVoidStuffNonEscaping { [weak self] in + let `self`: TestImplicitSelfForWeakSelfCapture? = self ?? TestImplicitSelfForWeakSelfCapture.staticOptional + guard let self = self else { return } + method() + self.method() + } + + doVoidStuffNonEscaping { [weak self] in + guard let self = self ?? TestImplicitSelfForWeakSelfCapture.staticOptional else { return } + method() + self.method() + } + } +} + +public class TestRebindingSelfIsDisallowed { + let count: Void = () + + private init() { + doVoidStuff { + let `self` = "self shouldn't become a string" + let _: Int = count // expected-error{{cannot convert value of type 'Void' to specified type 'Int'}} + } + + doVoidStuffNonEscaping { + let `self` = "self shouldn't become a string" + let _: Int = count // expected-error{{cannot convert value of type 'Void' to specified type 'Int'}} + } + + doVoidStuff { [weak self] in + let `self` = "self shouldn't become a string" + let _: Int = count // expected-error{{cannot convert value of type 'Void' to specified type 'Int'}} + } + + doVoidStuffNonEscaping { [weak self] in + let `self` = "self shouldn't become a string" + let _: Int = count // expected-error{{cannot convert value of type 'Void' to specified type 'Int'}} + } + } + + func method() { + let `self` = "self shouldn't become a string" + let _: Int = count // expected-error{{cannot convert value of type 'Void' to specified type 'Int'}} + } +} + // https://github.com/apple/swift/issues/59716 ["foo"].map { s in if s == "1" { return } // expected-error{{cannot convert value of type '()' to closure result type 'Bool'}} diff --git a/test/expr/closure/closures_swift6.swift b/test/expr/closure/closures_swift6.swift index 92197278ff457..e25567cbfcdda 100644 --- a/test/expr/closure/closures_swift6.swift +++ b/test/expr/closure/closures_swift6.swift @@ -7,8 +7,18 @@ func doVoidStuffNonEscaping(_ fn: () -> ()) {} class ExplicitSelfRequiredTest { var x = 42 - func method() { + func method() -> Int { 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.}} + return 42 + } + + func weakSelfError() { + 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}} + 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}} + 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}} + 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}} + 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}} + 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}} } } @@ -70,3 +80,101 @@ class C_56501 { } } } + +public class TestImplicitSelfForWeakSelfCapture { + static let staticOptional: TestImplicitSelfForWeakSelfCapture? = .init() + func method() { } + + private init() { + 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}} + guard let self = self else { return } + method() + } + + doVoidStuff { [weak self] in + if let self = self { + method() + } + } + + doVoidStuff { [weak self] in + guard let self = self else { return } + doVoidStuff { // expected-note {{capture 'self' explicitly to enable implicit 'self' in this closure}} + method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note {{reference 'self.' explicitly}} + } + } + + doVoidStuff { [weak self] in + guard let self = self ?? TestImplicitSelfForWeakSelfCapture.staticOptional else { return } + method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} + } + + 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}} + guard let self = self else { return } + method() + } + + doVoidStuffNonEscaping { [weak self] in + if let self = self { + method() + } + } + + doVoidStuff { [weak self] in + let `self`: TestImplicitSelfForWeakSelfCapture? = self ?? TestImplicitSelfForWeakSelfCapture.staticOptional + guard let self = self else { return } + method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} + } + + doVoidStuffNonEscaping { [weak self] in + let `self`: TestImplicitSelfForWeakSelfCapture? = self ?? TestImplicitSelfForWeakSelfCapture.staticOptional + guard let self = self else { return } + method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} + } + + doVoidStuffNonEscaping { [weak self] in + guard let self = self else { return } + doVoidStuff { // expected-note {{capture 'self' explicitly to enable implicit 'self' in this closure}} + method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note {{reference 'self.' explicitly}} + } + } + + doVoidStuffNonEscaping { [weak self] in + guard let self = self ?? TestImplicitSelfForWeakSelfCapture.staticOptional else { return } + method() // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} + } + } +} + +public class TestRebindingSelfIsDisallowed { + let count: Void = () + + private init() { + doVoidStuff { + let `self` = "self shouldn't become a string" + let _: Int = count // expected-error{{cannot convert value of type 'Void' to specified type 'Int'}} + } + + doVoidStuffNonEscaping { + let `self` = "self shouldn't become a string" + let _: Int = count // expected-error{{cannot convert value of type 'Void' to specified type 'Int'}} + } + + doVoidStuff { [weak self] in + let `self` = "self shouldn't become a string" + let _: Int = count // expected-error{{reference to property 'count' in closure requires explicit use of 'self' to make capture semantics explicit}} + } + + doVoidStuffNonEscaping { [weak self] in + let `self` = "self shouldn't become a string" + let _: Int = count // expected-error{{reference to property 'count' in closure requires explicit use of 'self' to make capture semantics explicit}} + } + } + + func method() { + let `self` = "self shouldn't become a string" + let _: Int = count // expected-error{{cannot convert value of type 'Void' to specified type 'Int'}} + } +} \ No newline at end of file