diff --git a/include/swift/AST/DiagnosticGroups.def b/include/swift/AST/DiagnosticGroups.def index 8972554df073a..2c0a7002ba27f 100644 --- a/include/swift/AST/DiagnosticGroups.def +++ b/include/swift/AST/DiagnosticGroups.def @@ -71,6 +71,7 @@ GROUP(StringInterpolationConformance, "string-interpolation-conformance") GROUP(TemporaryPointers, "temporary-pointers") GROUP(TrailingClosureMatching, "trailing-closure-matching") GROUP(UnknownWarningGroup, "unknown-warning-group") +GROUP(WeakMutability, "weak-mutability") #define UNDEFINE_DIAGNOSTIC_GROUPS_MACROS #include "swift/AST/DefineDiagnosticGroupsMacros.h" diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 5d40bdb8843bc..dda1576360021 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -7304,6 +7304,14 @@ WARNING(variable_tuple_elt_never_mutated, none, "variable %0 was never mutated; " "consider changing the pattern to 'case (..., let %1, ...)'", (Identifier, StringRef)) +GROUPED_WARNING(weak_variable_never_mutated, WeakMutability, none, + "weak variable %0 was never mutated; " + "consider %select{removing 'var' to make it|changing to 'let'}1 constant", + (Identifier, bool)) +GROUPED_WARNING(weak_variable_tuple_elt_never_mutated, WeakMutability, none, + "weak variable %0 was never mutated; " + "consider changing the pattern to 'case (..., let %1, ...)'", + (Identifier, StringRef)) WARNING(variable_never_read, none, "variable %0 was written to, but never read", (Identifier)) diff --git a/include/swift/AST/Stmt.h b/include/swift/AST/Stmt.h index 799e824fde7c8..daa053d2fe7fb 100644 --- a/include/swift/AST/Stmt.h +++ b/include/swift/AST/Stmt.h @@ -728,6 +728,8 @@ class alignas(1 << PatternAlignInBits) StmtConditionElement { /// RHS of the self condition references a var defined in a capture list. /// - If `requireLoadExpr` is `true`, additionally requires that the RHS of /// the self condition is a `LoadExpr`. + /// TODO: Remove `requireLoadExpr` after full-on of the ImmutableWeakCaptures + /// feature bool rebindsSelf(ASTContext &Ctx, bool requiresCaptureListRef = false, bool requireLoadExpr = false) const; @@ -838,8 +840,6 @@ class LabeledConditionalStmt : public LabeledStmt { /// or `let self = self` condition. /// - If `requiresCaptureListRef` is `true`, additionally requires that the /// RHS of the self condition references a var defined in a capture list. - /// - If `requireLoadExpr` is `true`, additionally requires that the RHS of - /// the self condition is a `LoadExpr`. bool rebindsSelf(ASTContext &Ctx, bool requiresCaptureListRef = false, bool requireLoadExpr = false) const; diff --git a/include/swift/Basic/Features.def b/include/swift/Basic/Features.def index e09faab097747..24e34150f6673 100644 --- a/include/swift/Basic/Features.def +++ b/include/swift/Basic/Features.def @@ -293,6 +293,7 @@ UPCOMING_FEATURE(InternalImportsByDefault, 409, 7) MIGRATABLE_UPCOMING_FEATURE(MemberImportVisibility, 444, 7) MIGRATABLE_UPCOMING_FEATURE(InferIsolatedConformances, 470, 7) MIGRATABLE_UPCOMING_FEATURE(NonisolatedNonsendingByDefault, 461, 7) +UPCOMING_FEATURE(ImmutableWeakCaptures, 481, 7) // Optional language features / modes diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 140872ecbf9b6..9aae06161637a 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -1366,9 +1366,9 @@ CaptureListEntry CaptureListEntry::createParsed( SourceRange ownershipRange, Identifier name, SourceLoc nameLoc, SourceLoc equalLoc, Expr *initializer, DeclContext *DC) { - auto introducer = - (ownershipKind != ReferenceOwnership::Weak ? VarDecl::Introducer::Let - : VarDecl::Introducer::Var); + bool forceVar = ownershipKind == ReferenceOwnership::Weak && + !Ctx.LangOpts.hasFeature(Feature::ImmutableWeakCaptures); + auto introducer = forceVar ? VarDecl::Introducer::Var : VarDecl::Introducer::Let; auto *VD = new (Ctx) VarDecl(/*isStatic==*/false, introducer, nameLoc, name, DC); diff --git a/lib/AST/FeatureSet.cpp b/lib/AST/FeatureSet.cpp index 7376cead2839e..ba6250ca1308e 100644 --- a/lib/AST/FeatureSet.cpp +++ b/lib/AST/FeatureSet.cpp @@ -680,6 +680,7 @@ static bool usesFeatureAlwaysInheritActorContext(Decl *decl) { static bool usesFeatureDefaultIsolationPerFile(Decl *D) { return isa(D); } +UNINTERESTING_FEATURE(ImmutableWeakCaptures) // ---------------------------------------------------------------------------- // MARK: - FeatureSet diff --git a/lib/AST/Stmt.cpp b/lib/AST/Stmt.cpp index 273ab7b9ca8cf..5810226fcff89 100644 --- a/lib/AST/Stmt.cpp +++ b/lib/AST/Stmt.cpp @@ -514,8 +514,6 @@ void LabeledConditionalStmt::setCond(StmtCondition e) { /// or `let self = self` condition. /// - If `requiresCaptureListRef` is `true`, additionally requires that the /// RHS of the self condition references a var defined in a capture list. -/// - If `requireLoadExpr` is `true`, additionally requires that the RHS of -/// the self condition is a `LoadExpr`. bool LabeledConditionalStmt::rebindsSelf(ASTContext &Ctx, bool requiresCaptureListRef, bool requireLoadExpr) const { @@ -529,8 +527,6 @@ bool LabeledConditionalStmt::rebindsSelf(ASTContext &Ctx, /// or `let self = self` condition. /// - If `requiresCaptureListRef` is `true`, additionally requires that the /// RHS of the self condition references a var defined in a capture list. -/// - If `requireLoadExpr` is `true`, additionally requires that the RHS of -/// the self condition is a `LoadExpr`. bool StmtConditionElement::rebindsSelf(ASTContext &Ctx, bool requiresCaptureListRef, bool requireLoadExpr) const { diff --git a/lib/Sema/MiscDiagnostics.cpp b/lib/Sema/MiscDiagnostics.cpp index d61f1a2412099..35262b706bd85 100644 --- a/lib/Sema/MiscDiagnostics.cpp +++ b/lib/Sema/MiscDiagnostics.cpp @@ -1784,15 +1784,34 @@ class ImplicitSelfUsageChecker : public BaseDiagnosticWalker { return false; } - // Require `LoadExpr`s when validating the self binding. - // This lets us reject invalid examples like: - // - // let `self` = self ?? .somethingElse - // guard let self = self else { return } - // method() // <- implicit self is not allowed - // - return conditionalStmt->rebindsSelf(Ctx, /*requiresCaptureListRef*/ false, - /*requireLoadExpr*/ true); + if (Ctx.LangOpts.hasFeature(Feature::ImmutableWeakCaptures)) { + // Require that the RHS of the `let self = self` condition + // refers to a variable defined in a capture list. + // This lets us reject invalid examples like: + // + // var `self` = self ?? .somethingElse + // guard let self = self else { return } + // method() // <- implicit self is not allowed + // + // In 5.10, instead of this check, compiler was checking that RHS of the + // self binding is loaded from a mutable variable. This is incorrect, but + // before immutable weak captures compiler was trying to maintain this + // behavior in Swift 5 mode for source compatibility. With immutable weak + // captures this does not work anymore, because even in Swift 5 mode there + // is no `LoadExpr` to use. + // + return conditionalStmt->rebindsSelf(Ctx, /*requiresCaptureListRef*/ true); + } else { + // Require `LoadExpr`s when validating the self binding. + // This lets us reject invalid examples like: + // + // let `self` = self ?? .somethingElse + // guard let self = self else { return } + // method() // <- implicit self is not allowed + // + return conditionalStmt->rebindsSelf(Ctx, /*requiresCaptureListRef*/ false, + /*requireLoadExpr*/ true); + } } static bool @@ -4043,9 +4062,12 @@ VarDeclUsageChecker::~VarDeclUsageChecker() { // If this variable has WeakStorageType, then it can be mutated in ways we // don't know. - if (var->getInterfaceType()->is()) + if (var->getInterfaceType()->is() && + (access & RK_CaptureList) && + !DC->getASTContext().LangOpts.hasFeature( + Feature::ImmutableWeakCaptures)) access |= RK_Written; - + // Diagnose variables that were never used (other than their // initialization). // @@ -4216,6 +4238,8 @@ VarDeclUsageChecker::~VarDeclUsageChecker() { if (isUsedInInactive(var)) continue; + bool isWeak = var->getInterfaceType()->is(); + // If this is a parameter explicitly marked 'var', remove it. if (FixItLoc.isInvalid()) { bool suggestCaseLet = false; @@ -4226,10 +4250,14 @@ VarDeclUsageChecker::~VarDeclUsageChecker() { suggestCaseLet = isa(stmt); } if (suggestCaseLet) - Diags.diagnose(var->getLoc(), diag::variable_tuple_elt_never_mutated, + Diags.diagnose(var->getLoc(), + isWeak ? diag::weak_variable_tuple_elt_never_mutated + : diag::variable_tuple_elt_never_mutated, var->getName(), var->getNameStr()); else - Diags.diagnose(var->getLoc(), diag::variable_never_mutated, + Diags.diagnose(var->getLoc(), + isWeak ? diag::weak_variable_never_mutated + : diag::variable_never_mutated, var->getName(), true); } @@ -4242,7 +4270,9 @@ VarDeclUsageChecker::~VarDeclUsageChecker() { suggestLet = !isa(stmt); } - auto diag = Diags.diagnose(var->getLoc(), diag::variable_never_mutated, + auto diag = Diags.diagnose(var->getLoc(), + isWeak ? diag::weak_variable_never_mutated + : diag::variable_never_mutated, var->getName(), suggestLet); if (suggestLet) diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 9549443ad3566..75df3803ce162 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -5265,11 +5265,6 @@ Type TypeChecker::checkReferenceOwnershipAttr(VarDecl *var, Type type, case ReferenceOwnershipOptionality::Allowed: break; case ReferenceOwnershipOptionality::Required: - if (var->isLet()) { - var->diagnose(diag::invalid_ownership_is_let, ownershipKind); - attr->setInvalid(); - } - if (!isOptional) { attr->setInvalid(); diff --git a/test/Concurrency/weak_ref_sendability.swift b/test/Concurrency/weak_ref_sendability.swift new file mode 100644 index 0000000000000..f0c6f9abdc5c4 --- /dev/null +++ b/test/Concurrency/weak_ref_sendability.swift @@ -0,0 +1,292 @@ +// RUN: %target-swift-frontend -emit-sil -swift-version 6 -target %target-swift-5.1-abi-triple -verify -verify-additional-prefix old- %s -o /dev/null +// RUN: %target-swift-frontend -emit-sil -swift-version 6 -target %target-swift-5.1-abi-triple -verify -verify-additional-prefix new- -enable-upcoming-feature ImmutableWeakCaptures %s -o /dev/null + +// This test validates the behavior of transfer non sendable around ownership +// constructs like non copyable types, consuming/borrowing parameters, and inout +// parameters. + +// REQUIRES: concurrency +// REQUIRES: swift_feature_ImmutableWeakCaptures + +final class S: Sendable { + func foo() {} +} + +// expected-old-note@+2 13{{class 'NS' does not conform to the 'Sendable' protocol}} +// expected-new-note@+1 12{{class 'NS' does not conform to the 'Sendable' protocol}} +final class NS { + func bar() {} +} + +func getS() -> S { S() } +func getNS() -> NS { NS() } + +final class CheckOptionality1: Sendable { + // expected-old-error@+4 {{'weak' variable should have optional type 'S?'}} + // expected-old-error@+3 {{stored property 'x' of 'Sendable'-conforming class 'CheckOptionality1' is mutable}} + // expected-new-error@+2 {{'weak' variable should have optional type 'S?'}} + // expected-new-error@+1 {{stored property 'x' of 'Sendable'-conforming class 'CheckOptionality1' is mutable}} + weak var x: S = getS() +} + +final class CheckOptionality2: Sendable { + // expected-error@+1 {{'weak' variable should have optional type 'S?'}} + weak let x: S = getS() +} + +final class CheckSendability1: Sendable { + // expected-error@+1 {{stored property 'x' of 'Sendable'-conforming class 'CheckSendability1' is mutable}} + weak var x: S? = nil + + weak var y: S? { + get { x } + set { x = newValue } + } +} + +final class CheckSendability2: Sendable { + // expected-error@+1 {{stored property 'x' of 'Sendable'-conforming class 'CheckSendability2' is mutable}} + weak var x: NS? = nil +} + +final class CheckSendability3: Sendable { + weak let x: S? = nil +} + +final class CheckSendability4: Sendable { + // expected-error@+1 {{stored property 'x' of 'Sendable'-conforming class 'CheckSendability4' contains non-Sendable type 'NS'}} + weak let x: NS? = nil +} + +final class CheckSendability5: Sendable { + // expected-error@+1 {{stored property 'x' of 'Sendable'-conforming class 'CheckSendability5' is mutable}} + unowned var x: S = getS() +} + +final class CheckSendability6: Sendable { + // expected-error@+1 {{stored property 'x' of 'Sendable'-conforming class 'CheckSendability6' is mutable}} + unowned var x: NS = getNS() +} + +final class CheckSendability7: Sendable { + unowned let x: S = getS() +} + +final class CheckSendability8: Sendable { + // expected-error@+1 {{stored property 'x' of 'Sendable'-conforming class 'CheckSendability8' has non-Sendable type 'NS'}} + unowned let x: NS = getNS() +} + +final class CheckSendability9: Sendable { + // expected-error@+1 {{stored property 'x' of 'Sendable'-conforming class 'CheckSendability9' is mutable}} + unowned(unsafe) var x: S = getS() +} + +final class CheckSendability10: Sendable { + // expected-error@+1 {{stored property 'x' of 'Sendable'-conforming class 'CheckSendability10' is mutable}} + unowned(unsafe) var x: NS = getNS() +} + +final class CheckSendability11: Sendable { + unowned(unsafe) let x: S = getS() +} + +final class CheckSendability12: Sendable { + // expected-error@+1 {{stored property 'x' of 'Sendable'-conforming class 'CheckSendability12' has non-Sendable type 'NS'}} + unowned(unsafe) let x: NS = getNS() +} + + +func checkWeakCapture1(_ strongRef: S) -> @Sendable () -> Void { + // expected-warning@+1 {{weak variable 'weakRef' was never mutated; consider changing to 'let' constant}} + weak var weakRef: S? = strongRef + return { + // expected-error@+1 {{reference to captured var 'weakRef' in concurrently-executing code}} + weakRef?.foo() + } +} + +func checkWeakCapture2(_ strongRef: S) -> @Sendable () -> Void { + weak let weakRef: S? = strongRef + return { + weakRef?.foo() + } +} + +func checkWeakCapture3(_ strongRef: S) -> @Sendable () -> Void { + return { [weak weakRef = strongRef] in + // TODO: Add expected old error, when https://github.com/swiftlang/swift/issues/80014 is fixed + // See also https://forums.swift.org/t/lets-debug-missing-rbi-data-race-diagnostics/78910 + weakRef?.foo() + // expected-new-error@+1 {{cannot assign to value: 'weakRef' is an immutable capture}} + weakRef = nil + } +} + +func checkWeakCapture4(_ strongRef: NS) -> @Sendable () -> Void { + // expected-warning@+1 {{weak variable 'weakRef' was never mutated; consider changing to 'let' constant}} + weak var weakRef: NS? = strongRef + return { + // expected-error@+2 {{capture of 'weakRef' with non-Sendable type 'NS?' in a '@Sendable' closure}} + // expected-error@+1 {{reference to captured var 'weakRef' in concurrently-executing code}} + weakRef?.bar() + } +} + +func checkWeakCapture5(_ strongRef: NS) -> @Sendable () -> Void { + weak let weakRef: NS? = strongRef + return { + // expected-error@+1 {{capture of 'weakRef' with non-Sendable type 'NS?' in a '@Sendable' closure}} + weakRef?.bar() + } +} + +func checkWeakCapture6(_ strongRef: NS) -> @Sendable () -> Void { + return { [weak weakRef = strongRef] in + // expected-old-error@+3 {{capture of 'weakRef' with non-Sendable type 'NS?' in a '@Sendable' closure}} + // For some reason the next error masks this error. + // This case is split into two, to verify that when unmasked error is triggered. + weakRef?.bar() + // expected-new-error@+1 {{cannot assign to value: 'weakRef' is an immutable capture}} + weakRef = nil + } +} + +func checkWeakCapture7(_ strongRef: NS) -> @Sendable () -> Void { + return { [weak weakRef = strongRef] in + // expected-error@+1 {{capture of 'weakRef' with non-Sendable type 'NS?' in a '@Sendable' closure}} + weakRef?.bar() + } +} + +func checkUnownedCapture1(_ strongRef: S) -> @Sendable () -> Void { + // expected-old-warning@+2 {{variable 'unownedRef' was never mutated; consider changing to 'let' constant}} + // expected-new-warning@+1 {{variable 'unownedRef' was never mutated; consider changing to 'let' constant}} + unowned var unownedRef: S = strongRef + return { + // expected-old-error@+2 {{reference to captured var 'unownedRef' in concurrently-executing code}} + // expected-new-error@+1 {{reference to captured var 'unownedRef' in concurrently-executing code}} + unownedRef.foo() + } +} + +func checkUnownedCapture2(_ strongRef: S) -> @Sendable () -> Void { + unowned let unownedRef: S = strongRef + return { + unownedRef.foo() + } +} + +func checkUnownedCapture3(_ strongRef: S) -> @Sendable () -> Void { + return { [unowned unownedRef = strongRef] in + // TODO: Add expected old error, when https://github.com/swiftlang/swift/issues/80014 is fixed + // See also https://forums.swift.org/t/lets-debug-missing-rbi-data-race-diagnostics/78910 + unownedRef.foo() + // expected-old-error@+2 {{cannot assign to value: 'unownedRef' is an immutable capture}} + // expected-new-error@+1 {{cannot assign to value: 'unownedRef' is an immutable capture}} + unownedRef = strongRef + } +} + +func checkUnownedCapture4(_ strongRef: NS) -> @Sendable () -> Void { + // expected-old-warning@+2 {{variable 'unownedRef' was never mutated; consider changing to 'let' constant}} + // expected-new-warning@+1 {{variable 'unownedRef' was never mutated; consider changing to 'let' constant}} + unowned var unownedRef: NS = strongRef + return { + // expected-error@+2 {{capture of 'unownedRef' with non-Sendable type 'NS' in a '@Sendable' closure}} + // expected-error@+1 {{reference to captured var 'unownedRef' in concurrently-executing code}} + unownedRef.bar() + } +} + +func checkUnownedCapture5(_ strongRef: NS) -> @Sendable () -> Void { + unowned let unownedRef: NS = strongRef + return { + // expected-error@+1 {{capture of 'unownedRef' with non-Sendable type 'NS' in a '@Sendable' closure}} + unownedRef.bar() + } +} + +func checkUnownedCapture6(_ strongRef: NS) -> @Sendable () -> Void { + return { [unowned unownedRef = strongRef] in + // For some reason the next error masks this error. + // This case is split into two, to verify that when unmasked error is triggered. + unownedRef.bar() + // expected-old-error@+2 {{cannot assign to value: 'unownedRef' is an immutable capture}} + // expected-new-error@+1 {{cannot assign to value: 'unownedRef' is an immutable capture}} + unownedRef = strongRef + } +} + +func checkUnownedCapture7(_ strongRef: NS) -> @Sendable () -> Void { + return { [unowned unownedRef = strongRef] in + // expected-error@+1 {{capture of 'unownedRef' with non-Sendable type 'NS' in a '@Sendable' closure}} + unownedRef.bar() + } +} + +func checkUnsafeCapture1(_ strongRef: S) -> @Sendable () -> Void { + // expected-old-warning@+2 {{variable 'unownedRef' was never mutated; consider changing to 'let' constant}} + // expected-new-warning@+1 {{variable 'unownedRef' was never mutated; consider changing to 'let' constant}} + unowned(unsafe) var unownedRef: S = strongRef + return { + // expected-old-error@+2 {{reference to captured var 'unownedRef' in concurrently-executing code}} + // expected-new-error@+1 {{reference to captured var 'unownedRef' in concurrently-executing code}} + unownedRef.foo() + } +} + +func checkUnsafeCapture2(_ strongRef: S) -> @Sendable () -> Void { + unowned(unsafe) let unownedRef: S = strongRef + return { + unownedRef.foo() + } +} + +func checkUnsafeCapture3(_ strongRef: S) -> @Sendable () -> Void { + return { [unowned(unsafe) unownedRef = strongRef] in + // TODO: Add expected old error, when https://github.com/swiftlang/swift/issues/80014 is fixed + // See also https://forums.swift.org/t/lets-debug-missing-rbi-data-race-diagnostics/78910 + unownedRef.foo() + // expected-old-error@+2 {{cannot assign to value: 'unownedRef' is an immutable capture}} + // expected-new-error@+1 {{cannot assign to value: 'unownedRef' is an immutable capture}} + unownedRef = strongRef + } +} + +func checkUnsafeCapture4(_ strongRef: NS) -> @Sendable () -> Void { + // expected-old-warning@+2 {{variable 'unownedRef' was never mutated; consider changing to 'let' constant}} + // expected-new-warning@+1 {{variable 'unownedRef' was never mutated; consider changing to 'let' constant}} + unowned(unsafe) var unownedRef: NS = strongRef + return { + // expected-error@+2 {{capture of 'unownedRef' with non-Sendable type 'NS' in a '@Sendable' closure}} + // expected-error@+1 {{reference to captured var 'unownedRef' in concurrently-executing code}} + unownedRef.bar() + } +} + +func checkUnsafeCapture5(_ strongRef: NS) -> @Sendable () -> Void { + unowned(unsafe) let unownedRef: NS = strongRef + return { + // expected-error@+1 {{capture of 'unownedRef' with non-Sendable type 'NS' in a '@Sendable' closure}} + unownedRef.bar() + } +} + +func checkUnsafeCapture6(_ strongRef: NS) -> @Sendable () -> Void { + return { [unowned(unsafe) unownedRef = strongRef] in + // For some reason the next error masks this error. + // This case is split into two, to verify that when unmasked error is triggered. + unownedRef.bar() + // expected-old-error@+2 {{cannot assign to value: 'unownedRef' is an immutable capture}} + // expected-new-error@+1 {{cannot assign to value: 'unownedRef' is an immutable capture}} + unownedRef = strongRef + } +} + +func checkUnsafeCapture7(_ strongRef: NS) -> @Sendable () -> Void { + return { [unowned(unsafe) unownedRef = strongRef] in + // expected-error@+1 {{capture of 'unownedRef' with non-Sendable type 'NS' in a '@Sendable' closure}} + unownedRef.bar() + } +} diff --git a/test/DebugInfo/WeakCapture.swift b/test/DebugInfo/WeakCapture.swift index b3c86fdd54c42..d67839d5053b6 100644 --- a/test/DebugInfo/WeakCapture.swift +++ b/test/DebugInfo/WeakCapture.swift @@ -1,4 +1,8 @@ // RUN: %target-swift-frontend %s -emit-ir -g -o - | %FileCheck %s +// RUN: %target-swift-frontend %s -enable-upcoming-feature ImmutableWeakCaptures -emit-ir -g -o - | %FileCheck %s + +// REQUIRES: swift_feature_ImmutableWeakCaptures + class A { init(handler: (() -> ())) { } } diff --git a/test/DebugInfo/guard-let-scope4.swift b/test/DebugInfo/guard-let-scope4.swift index d404d8ca1e88c..22f36d691dddb 100644 --- a/test/DebugInfo/guard-let-scope4.swift +++ b/test/DebugInfo/guard-let-scope4.swift @@ -1,13 +1,14 @@ // RUN: %target-swift-frontend -g -Xllvm -sil-print-types -emit-sil %s -parse-as-library -module-name a | %FileCheck %s +// RUN: %target-swift-frontend -g -Xllvm -sil-print-types -emit-sil %s -parse-as-library -module-name a -enable-upcoming-feature ImmutableWeakCaptures | %FileCheck %s --check-prefixes=CHECK,CHECK-HAS-WEAK-LET +// REQUIRES: swift_feature_ImmutableWeakCaptures open class C { - public func fun() {} - public func run() { { [weak self] in guard let self else { fatalError("cannot happen") } // CHECK: sil_scope [[LAMBDA:[0-9]+]] { loc "{{.*}}":6:5 // CHECK: sil_scope [[BODY:[0-9]+]] { loc "{{.*}}":6:19 parent [[LAMBDA]] // CHECK: sil_scope [[LET:[0-9]+]] { loc "{{.*}}":7:7 parent [[BODY]] + // CHECK-HAS-WEAK-LET: sil_scope [[TMP:[0-9]+]] { loc "{{.*}}":7:17 parent [[LET]] // CHECK: sil_scope [[GUARD:[0-9]+]] { loc "{{.*}}":7:17 parent [[LET]] // CHECK: debug_value {{.*}} : $C, let, name "self", {{.*}}, scope [[GUARD]] // CHECK: function_ref {{.*}}3fun{{.*}}, scope [[GUARD]] @@ -15,4 +16,8 @@ open class C { self.fun() }() } + public func fun() {} } + + + diff --git a/test/DebugInfo/weak-self-capture.swift b/test/DebugInfo/weak-self-capture.swift index 600ddbff0bb54..0daf9a8309480 100644 --- a/test/DebugInfo/weak-self-capture.swift +++ b/test/DebugInfo/weak-self-capture.swift @@ -1,4 +1,8 @@ // RUN: %target-swift-frontend %s -emit-ir -g -o - | %FileCheck %s +// RUN: %target-swift-frontend %s -enable-upcoming-feature ImmutableWeakCaptures -emit-ir -g -o - | %FileCheck %s + +// REQUIRES: swift_feature_ImmutableWeakCaptures + public class ClosureMaker { var a : Int diff --git a/test/Interpreter/weak.swift b/test/Interpreter/weak.swift index e196684e45012..7390809fde835 100644 --- a/test/Interpreter/weak.swift +++ b/test/Interpreter/weak.swift @@ -1,5 +1,8 @@ // RUN: %target-run-simple-swift | %FileCheck %s +// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-feature -Xfrontend ImmutableWeakCaptures) | %FileCheck %s --check-prefixes=CHECK + // REQUIRES: executable_test +// REQUIRES: swift_feature_ImmutableWeakCaptures protocol Protocol : class { func noop() @@ -76,6 +79,18 @@ func testWeakInLet() { testWeakInLet() +func testWeakLet() { + print("testWeakLet") // CHECK-LABEL: testWeakLet + + var obj: SwiftClassBase? = SwiftClass() // CHECK: SwiftClass Created + weak let weakRef = obj + printState(weakRef) // CHECK-NEXT: is present + obj = nil // CHECK-NEXT: SwiftClass Destroyed + printState(weakRef) // CHECK-NEXT: is nil +} + +testWeakLet() + //======================== Test Classbound Protocols ======================== diff --git a/test/Interpreter/weak_objc.swift b/test/Interpreter/weak_objc.swift index 3bb3447b766f1..72ccead84d643 100644 --- a/test/Interpreter/weak_objc.swift +++ b/test/Interpreter/weak_objc.swift @@ -1,8 +1,14 @@ // RUN: %target-build-swift %s -Xfrontend -disable-objc-attr-requires-foundation-module -o %t-main // RUN: %target-codesign %t-main // RUN: %target-run %t-main | %FileCheck %s + +// RUN: %target-build-swift %s -Xfrontend -disable-objc-attr-requires-foundation-module -enable-upcoming-feature ImmutableWeakCaptures -o %t-main-weak-let +// RUN: %target-codesign %t-main-weak-let +// RUN: %target-run %t-main-weak-let | %FileCheck %s --check-prefixes=CHECK,CHECK-WEAK-LET + // REQUIRES: executable_test // REQUIRES: objc_interop +// REQUIRES: swift_feature_ImmutableWeakCaptures import Foundation @@ -47,3 +53,29 @@ func testObjCClass() { } testObjCClass() + +func testObjCWeakLet() { + print("testObjCWeakLet") // CHECK-WEAK-LET: testObjCWeakLet + + var c : ObjCClassBase = ObjCClass() // CHECK-WEAK-LET: ObjCClass Created + weak let w : ObjCClassBase? = c + printState(w) // CHECK-WEAK-LET-NEXT: is present + c = ObjCClassBase() // CHECK-WEAK-LET-NEXT: ObjCClass Destroyed + printState(w) // CHECK-WEAK-LET-NEXT: is nil +} + +testObjCWeakLet() + +func testObjCWeakLetCapture() { + print("testObjCWeakLetCapture") // CHECK-WEAK-LET: testObjCWeakLetCapture + + var c : ObjCClassBase = ObjCClass() // CHECK-WEAK-LET: ObjCClass Created + let closure: () -> ObjCClassBase? = { [weak c] in c } + printState(closure()) // CHECK-WEAK-LET-NEXT: is present + printState(closure()) // CHECK-WEAK-LET-NEXT: is present + c = ObjCClassBase() // CHECK-WEAK-LET-NEXT: ObjCClass Destroyed + printState(closure()) // CHECK-WEAK-LET-NEXT: is nil + printState(closure()) // CHECK-WEAK-LET-NEXT: is nil +} + +testObjCWeakLetCapture() diff --git a/test/Parse/invalid.swift b/test/Parse/invalid.swift index e886233d6e5c3..443ba558ce098 100644 --- a/test/Parse/invalid.swift +++ b/test/Parse/invalid.swift @@ -119,7 +119,7 @@ prefix func %(x: T) -> T { return x } // No error expected - the < is conside struct Weak { // expected-error {{'class' constraint can only appear on protocol declarations}} // expected-note@-1 {{did you mean to write an 'AnyObject' constraint?}} {{16-21=AnyObject}} - weak let value: T // expected-error {{'weak' must be a mutable variable, because it may change at runtime}} expected-error {{'weak' variable should have optional type 'T?'}} expected-error {{'weak' must not be applied to non-class-bound 'T'; consider adding a protocol conformance that has a class bound}} + weak let value: T // expected-error {{'weak' variable should have optional type 'T?'}} expected-error {{'weak' must not be applied to non-class-bound 'T'; consider adding a protocol conformance that has a class bound}} } let x: () = () diff --git a/test/SILGen/capture-transitive.swift b/test/SILGen/capture-transitive.swift index 0d77d7ff31c38..fb90b7516f93d 100644 --- a/test/SILGen/capture-transitive.swift +++ b/test/SILGen/capture-transitive.swift @@ -1,4 +1,7 @@ -// RUN: %target-swift-emit-silgen %s | %FileCheck %s +// RUN: %target-swift-emit-silgen %s | %FileCheck %s --check-prefixes=CHECK,CHECK-NO-WEAK-LET +// RUN: %target-swift-emit-silgen -enable-upcoming-feature ImmutableWeakCaptures %s | %FileCheck %s --check-prefixes=CHECK,CHECK-HAS-WEAK-LET + +// REQUIRES: swift_feature_ImmutableWeakCaptures // https://github.com/apple/swift/issues/50924 diff --git a/test/SILGen/capture_order.swift b/test/SILGen/capture_order.swift index 55de099718024..1e5e08d7f4b08 100644 --- a/test/SILGen/capture_order.swift +++ b/test/SILGen/capture_order.swift @@ -1,4 +1,6 @@ -// RUN: %target-swift-emit-silgen %s -verify +// RUN: %target-swift-emit-silgen -enable-upcoming-feature ImmutableWeakCaptures %s -verify + +// REQUIRES: swift_feature_ImmutableWeakCaptures /// We emit an invalid forward capture as an 'undef'; make sure /// we cover the various possible cases. @@ -28,7 +30,7 @@ public func captureBeforeDefWeakVar(obj: AnyObject) -> () -> AnyObject? { return weakObj // expected-note {{captured here}} } let closure = getter - weak var weakObj: AnyObject? = obj // expected-note{{captured value declared here}} + weak let weakObj: AnyObject? = obj // expected-note{{captured value declared here}} return closure } @@ -125,7 +127,7 @@ class ะก_47389 { let bar = { [weak self] in // expected-error@-1 {{closure captures 'bar' before it is declared}} // expected-note@-2 {{captured value declared here}} - // expected-warning@-3 {{variable 'self' was written to, but never read}} + // expected-warning@-3 {{capture 'self' was never used}} bar2() } func bar2() { diff --git a/test/SILGen/dynamic_self.swift b/test/SILGen/dynamic_self.swift index 540699b1507ee..c437432dd4204 100644 --- a/test/SILGen/dynamic_self.swift +++ b/test/SILGen/dynamic_self.swift @@ -1,10 +1,12 @@ -// RUN: %target-swift-emit-silgen -Xllvm -sil-print-types -swift-version 4 %s -disable-objc-attr-requires-foundation-module -enable-objc-interop | %FileCheck %s -// RUN: %target-swift-emit-sil -Xllvm -sil-print-types -swift-version 4 -O %s -disable-objc-attr-requires-foundation-module -enable-objc-interop -// RUN: %target-swift-emit-ir -swift-version 4 %s -disable-objc-attr-requires-foundation-module -enable-objc-interop +// RUN: %target-swift-emit-silgen -Xllvm -sil-print-types -swift-version 4 -enable-upcoming-feature ImmutableWeakCaptures %s -disable-objc-attr-requires-foundation-module -enable-objc-interop | %FileCheck %s +// RUN: %target-swift-emit-sil -Xllvm -sil-print-types -swift-version 4 -O -enable-upcoming-feature ImmutableWeakCaptures %s -disable-objc-attr-requires-foundation-module -enable-objc-interop +// RUN: %target-swift-emit-ir -swift-version 4 -enable-upcoming-feature ImmutableWeakCaptures %s -disable-objc-attr-requires-foundation-module -enable-objc-interop -// RUN: %target-swift-emit-silgen -Xllvm -sil-print-types -swift-version 5 %s -disable-objc-attr-requires-foundation-module -enable-objc-interop | %FileCheck %s -// RUN: %target-swift-emit-sil -Xllvm -sil-print-types -swift-version 5 -O %s -disable-objc-attr-requires-foundation-module -enable-objc-interop -// RUN: %target-swift-emit-ir -swift-version 5 %s -disable-objc-attr-requires-foundation-module -enable-objc-interop +// RUN: %target-swift-emit-silgen -Xllvm -sil-print-types -swift-version 5 -enable-upcoming-feature ImmutableWeakCaptures %s -disable-objc-attr-requires-foundation-module -enable-objc-interop | %FileCheck %s +// RUN: %target-swift-emit-sil -Xllvm -sil-print-types -swift-version 5 -O -enable-upcoming-feature ImmutableWeakCaptures %s -disable-objc-attr-requires-foundation-module -enable-objc-interop +// RUN: %target-swift-emit-ir -swift-version 5 -enable-upcoming-feature ImmutableWeakCaptures %s -disable-objc-attr-requires-foundation-module -enable-objc-interop + +// REQUIRES: swift_feature_ImmutableWeakCaptures protocol P { func f() -> Self diff --git a/test/SILGen/unowned-class-bound-generic-parameter.swift b/test/SILGen/unowned-class-bound-generic-parameter.swift index 64f3f76303961..f2755390982a4 100644 --- a/test/SILGen/unowned-class-bound-generic-parameter.swift +++ b/test/SILGen/unowned-class-bound-generic-parameter.swift @@ -34,4 +34,4 @@ func f1(t: T) { func f2(u: U, tt: Array>) { _ = tt.map { _ in Wrapper(t: u) } -} \ No newline at end of file +} diff --git a/test/SILGen/weak.swift b/test/SILGen/weak.swift index 73a443d8f740a..0e3a6f5f9276a 100644 --- a/test/SILGen/weak.swift +++ b/test/SILGen/weak.swift @@ -1,4 +1,3 @@ - // RUN: %target-swift-emit-silgen -Xllvm -sil-print-types -module-name weak -Xllvm -sil-full-demangle %s | %FileCheck %s class C { @@ -67,6 +66,16 @@ func testClosureOverWeak() { takeClosure { bC!.f() } } +func testClosureOverWeakLet() { + weak let bC = C() + takeClosure { bC!.f() } +} + +func testClosureOverWeakCapture() { + let bC = C() + takeClosure { [weak bC] in bC!.f() } +} + class CC { weak var x: CC? diff --git a/test/SILOptimizer/definite_init_diagnostics.swift b/test/SILOptimizer/definite_init_diagnostics.swift index 1daa73eff217f..7de0b0b56a3e2 100644 --- a/test/SILOptimizer/definite_init_diagnostics.swift +++ b/test/SILOptimizer/definite_init_diagnostics.swift @@ -103,6 +103,7 @@ func test2() { // Weak + // expected-warning@+1 {{weak variable 'w1' was never mutated; consider changing to 'let' constant}} {{8-11=let}} weak var w1 : SomeClass? _ = w1 // ok: default-initialized @@ -110,7 +111,7 @@ func test2() { // expected-warning@+3 {{instance will be immediately deallocated because variable 'w2' is 'weak'}} // expected-note@+2 {{a strong reference is required to prevent the instance from being deallocated}} // expected-note@+1 {{'w2' declared here}} - weak var w2 = SomeClass() + weak let w2 = SomeClass() _ = w2 // ok diff --git a/test/Sema/diag_unowned_immediate_deallocation.swift b/test/Sema/diag_unowned_immediate_deallocation.swift index d0770630f5472..315a23c9e989c 100644 --- a/test/Sema/diag_unowned_immediate_deallocation.swift +++ b/test/Sema/diag_unowned_immediate_deallocation.swift @@ -1,4 +1,6 @@ -// RUN: %target-typecheck-verify-swift -module-name ModuleName +// RUN: %target-typecheck-verify-swift -module-name ModuleName -enable-upcoming-feature ImmutableWeakCaptures + +// REQUIRES: swift_feature_ImmutableWeakCaptures protocol ClassProtocol : class { init() @@ -271,7 +273,7 @@ func testUnownedVariableBindingDiag() throws { } func testMultipleBindingDiag() { - weak var c1 = C(), c2: C? = C(), c3: C? = D() + weak let c1 = C(), c2: C? = C(), c3: C? = D() // expected-warning@-1 {{instance will be immediately deallocated because variable 'c1' is 'weak'}} // expected-note@-2 {{a strong reference is required to prevent the instance from being deallocated}} // expected-note@-3 {{'c1' declared here}} @@ -297,7 +299,7 @@ func testMultipleBindingDiag() { } func testTupleAndParenBinding() throws { - weak var ((c1), c2, c3): (C?, C?, C?) = (C() as C, (D()), try D(throwing: ())) + weak let ((c1), c2, c3): (C?, C?, C?) = (C() as C, (D()), try D(throwing: ())) // expected-warning@-1 {{instance will be immediately deallocated because variable 'c1' is 'weak'}} // expected-note@-2 {{a strong reference is required to prevent the instance from being deallocated}} // expected-note@-3 {{'c1' declared here}} @@ -323,11 +325,11 @@ func testTupleAndParenBinding() throws { } func testInitializationThroughClassArchetypeDiag(_ t: T, _ p: ClassProtocol) throws { - weak var t1: T? = T() // expected-warning {{instance will be immediately deallocated because variable 't1' is 'weak'}} + weak let t1: T? = T() // expected-warning {{instance will be immediately deallocated because variable 't1' is 'weak'}} // expected-note@-1 {{a strong reference is required to prevent the instance from being deallocated}} // expected-note@-2 {{'t1' declared here}} - weak var t2: ClassProtocol? = T(failable: ()) // expected-warning {{instance will be immediately deallocated because variable 't2' is 'weak'}} + weak let t2: ClassProtocol? = T(failable: ()) // expected-warning {{instance will be immediately deallocated because variable 't2' is 'weak'}} // expected-note@-1 {{a strong reference is required to prevent the instance from being deallocated}} // expected-note@-2 {{'t2' declared here}} @@ -342,7 +344,7 @@ func testInitializationThroughClassArchetypeDiag(_ t: T, _ p: let optionalTType: T.Type? = T.self let optionalPType: ClassProtocol.Type? = type(of: p) - weak var t5 = optionalTType?.init(failable: ()) // expected-warning {{instance will be immediately deallocated because variable 't5' is 'weak'}} + weak let t5 = optionalTType?.init(failable: ()) // expected-warning {{instance will be immediately deallocated because variable 't5' is 'weak'}} // expected-note@-1 {{a strong reference is required to prevent the instance from being deallocated}} // expected-note@-2 {{'t5' declared here}} @@ -404,12 +406,12 @@ class C1 { } func testInitializationThroughMetaclassDiag(_ t: C.Type) { - weak var c1: C? = t.init() // expected-warning {{instance will be immediately deallocated because variable 'c1' is 'weak'}} + weak let c1: C? = t.init() // expected-warning {{instance will be immediately deallocated because variable 'c1' is 'weak'}} // expected-note@-1 {{a strong reference is required to prevent the instance from being deallocated}} // expected-note@-2 {{'c1' declared here}} let optionalCType: C.Type? = t - weak var c2 = optionalCType?.init(failable: ()) // expected-warning {{instance will be immediately deallocated because variable 'c2' is 'weak'}} + weak let c2 = optionalCType?.init(failable: ()) // expected-warning {{instance will be immediately deallocated because variable 'c2' is 'weak'}} // expected-note@-1 {{a strong reference is required to prevent the instance from being deallocated}} // expected-note@-2 {{'c2' declared here}} @@ -439,7 +441,7 @@ func testInitializationThroughTupleElementDiag() { class E {} func testGenericWeakClassDiag() { - weak var e = E() + weak let e = E() // expected-warning@-1 {{instance will be immediately deallocated because variable 'e' is 'weak'}} // expected-note@-2 {{a strong reference is required to prevent the instance from being deallocated}} // expected-note@-3 {{'e' declared here}} @@ -465,7 +467,7 @@ extension Optional { } func testDontDiagnoseOnUnrelatedInitializer() { - weak var c = C?(dontDiagnoseOnThis: ()) + weak let c = C?(dontDiagnoseOnThis: ()) unowned let c1 = C?(dontDiagnoseOnThis: ())! _ = c; _ = c1 } @@ -476,8 +478,8 @@ class F { } func testDontDiagnoseThroughMembers() { - weak var c1 = F().c - weak var c2 = F().makeC() + weak let c1 = F().c + weak let c2 = F().makeC() _ = c1; _ = c2 } @@ -488,7 +490,7 @@ func testDontDiagnoseOnStrongVariable() { } func testDontDiagnoseThroughImmediatelyEvaluatedClosure() { - weak var c1 = { C() }() + weak let c1 = { C() }() unowned let c2 = { C() }() _ = c1; _ = c2 } diff --git a/test/decl/var/properties.swift b/test/decl/var/properties.swift index 8d91ba88e9997..783dfb6cd38ac 100644 --- a/test/decl/var/properties.swift +++ b/test/decl/var/properties.swift @@ -114,10 +114,11 @@ var x15: Int { // For the purpose of this test we need to use an attribute that cannot be // applied to the getter. weak - var foo: SomeClass? = SomeClass() // expected-warning {{variable 'foo' was written to, but never read}} - // expected-warning@-1 {{instance will be immediately deallocated because variable 'foo' is 'weak'}} - // expected-note@-2 {{a strong reference is required to prevent the instance from being deallocated}} - // expected-note@-3 {{'foo' declared here}} + var foo: SomeClass? = SomeClass() + // expected-warning@-1 {{variable 'foo' was never used; consider replacing with '_' or removing it}} + // expected-warning@-2 {{instance will be immediately deallocated because variable 'foo' is 'weak'}} + // expected-note@-3 {{a strong reference is required to prevent the instance from being deallocated}} + // expected-note@-4 {{'foo' declared here}} return 0 } diff --git a/test/decl/var/variables.swift b/test/decl/var/variables.swift index e6733c272d139..f6f2d88a6d7f5 100644 --- a/test/decl/var/variables.swift +++ b/test/decl/var/variables.swift @@ -87,8 +87,11 @@ var shouldWarnWithoutSugar = (arrayOfEmptyTuples as Array<()>) // expected-warni class SomeClass {} -// weak let's should be rejected -weak let V = SomeClass() // expected-error {{'weak' must be a mutable variable, because it may change at runtime}} + +weak let V = SomeClass() // ok since SE-0481 +// expected-warning@-1 {{instance will be immediately deallocated because variable 'V' is 'weak'}} +// expected-note@-2 {{'V' declared here}} +// expected-note@-3 {{a strong reference is required to prevent the instance from being deallocated}} let a = b ; let b = a // expected-error@-1 {{circular reference}} diff --git a/test/expr/closure/closures.swift b/test/expr/closure/closures.swift index 8d131c1456260..8e13cfd346726 100644 --- a/test/expr/closure/closures.swift +++ b/test/expr/closure/closures.swift @@ -1,4 +1,7 @@ -// RUN: %target-typecheck-verify-swift -disable-availability-checking +// RUN: %target-typecheck-verify-swift -disable-availability-checking -verify-additional-prefix no-weak-let- +// RUN: %target-typecheck-verify-swift -disable-availability-checking -enable-upcoming-feature ImmutableWeakCaptures -verify-additional-prefix has-weak-let- + +// REQUIRES: swift_feature_ImmutableWeakCaptures var func6 : (_ fn : (Int,Int) -> Int) -> () var func6a : ((Int, Int) -> Int) -> () @@ -262,12 +265,29 @@ class ExplicitSelfRequiredTest { // because its `sawError` flag is set to true. To preserve the "capture 'y' was never used" warnings // above, we put these cases in their own method. 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 {{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}} - 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 {{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}} - 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}} + 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-no-weak-let-warning@-1 {{variable 'self' was written to, but never read}} + // expected-has-weak-let-warning@-2 {{capture 'self' was never used}} + + 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-no-weak-let-warning@-1 {{variable 'self' was written to, but never read}} + // expected-has-weak-let-warning@-2 {{capture 'self' was never used}} + + 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-no-weak-let-warning@-1 {{variable 'self' was written to, but never read}} + // expected-has-weak-let-warning@-2 {{capture 'self' was never used}} + + doVoidStuff({ [weak self] in _ = method() }) // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} + // expected-no-weak-let-warning@-1 {{variable 'self' was written to, but never read}} + // expected-has-weak-let-warning@-2 {{capture 'self' was never used}} + + doVoidStuffNonEscaping({ [weak self] in _ = method() }) // expected-warning {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} + // expected-no-weak-let-warning@-1 {{variable 'self' was written to, but never read}} + // expected-has-weak-let-warning@-2 {{capture 'self' was never used}} + + doStuff({ [weak self] in method() }) // expected-error {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} + // expected-no-weak-let-warning@-1 {{variable 'self' was written to, but never read}} + // expected-has-weak-let-warning@-2 {{capture 'self' was never used}} } } @@ -374,7 +394,8 @@ extension SomeClass { //expected-error@-3{{reference to property 'in' in closure requires explicit use of 'self' to make capture semantics explicit}} //expected-note@-4{{reference 'self.' explicitly}} - // expected-warning @+1 {{variable 'self' was written to, but never read}} + // expected-no-weak-let-warning@+2 {{variable 'self' was written to, but never read}} + // expected-has-weak-let-warning@+1 {{capture 'self' was never used}} doStuff { [weak self&field] in 42 } // expected-error {{expected ']' at end of capture list}} } @@ -511,6 +532,7 @@ func lvalueCapture(c: GenericClass) { _ = wc cc = wc! + wc = cc } } @@ -1521,22 +1543,30 @@ final class AutoclosureTests { withEscapingAutoclosure(bar()) // expected-warning {{call to method 'bar' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note {{reference 'self.' explicitly}} } } - - doVoidStuff { [weak self] in // expected-warning {{variable 'self' was written to, but never read}} + + // expected-no-weak-let-warning@+2 {{variable 'self' was written to, but never read}} + // expected-has-weak-let-warning@+1 {{capture 'self' was never used}} + doVoidStuff { [weak self] in withNonEscapingAutoclosure(bar()) // expected-error {{call to method 'bar' in closure requires explicit use of 'self' to make capture semantics explicit}} } - - doVoidStuff { [weak self] in // expected-warning {{variable 'self' was written to, but never read}} + + // expected-no-weak-let-warning@+2 {{variable 'self' was written to, but never read}} + // expected-has-weak-let-warning@+1 {{capture 'self' was never used}} + doVoidStuff { [weak self] in withEscapingAutoclosure(bar()) // expected-error {{call to method 'bar' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note {{reference 'self.' explicitly}} } - - doVoidStuff { [weak self] in // expected-warning {{variable 'self' was written to, but never read}} + + // expected-no-weak-let-warning@+2 {{variable 'self' was written to, but never read}} + // expected-has-weak-let-warning@+1 {{capture 'self' was never used}} + doVoidStuff { [weak self] in doVoidStuff { // expected-note {{capture 'self' explicitly to enable implicit 'self' in this closure}} withNonEscapingAutoclosure(bar()) // expected-error {{all to method 'bar' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note {{reference 'self.' explicitly}} } } - - doVoidStuff { [weak self] in // expected-warning {{variable 'self' was written to, but never read}} + + // expected-no-weak-let-warning@+2 {{variable 'self' was written to, but never read}} + // expected-has-weak-let-warning@+1 {{capture 'self' was never used}} + doVoidStuff { [weak self] in doVoidStuff { withEscapingAutoclosure(bar()) // expected-error {{call to method 'bar' in closure requires explicit use of 'self' to make capture semantics explicit}} expected-note {{reference 'self.' explicitly}} } @@ -1603,9 +1633,13 @@ final class AutoclosureTests { let someOptional: Self? = Self() var `self` = self ?? someOptional // expected-warning {{'self' was never mutated; consider changing to 'let' constant}} guard let self = self else { return } +#if hasFeature(ImmutableWeakCaptures) + method() // expected-has-weak-let-error{{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} +#else // This is not supposed to be permitted, but has been allowed since Swift 5.8, // so we have to continue allowing it to maintain source compatibility. method() +#endif } doVoidStuff { // expected-note {{capture 'self' explicitly to enable implicit 'self' in this closure}} @@ -1771,13 +1805,17 @@ class rdar129475277 { func method() {} func test1() { - takesEscapingWithAllowedImplicitSelf { [weak self] in // expected-warning {{variable 'self' was written to, but never read}} + // expected-no-weak-let-warning@+2 {{variable 'self' was written to, but never read}} + // expected-has-weak-let-warning@+1 {{capture 'self' was never used}} + takesEscapingWithAllowedImplicitSelf { [weak self] in takesEscapingWithAllowedImplicitSelf { method() // expected-warning {{call to method 'method' in closure requires explicit use of 'self' to make capture semantics explicit}} } } - takesEscapingWithAllowedImplicitSelf { [weak self] in // expected-warning {{variable 'self' was written to, but never read}} + // expected-no-weak-let-warning@+2 {{variable 'self' was written to, but never read}} + // expected-has-weak-let-warning@+1 {{capture 'self' was never used}} + takesEscapingWithAllowedImplicitSelf { [weak self] in takesEscapingWithAllowedImplicitSelf { doVoidStuffNonEscaping { withNonEscapingAutoclosure(bar()) // expected-warning {{call to method 'bar' in closure requires explicit use of 'self' to make capture semantics explicit}} @@ -1785,7 +1823,9 @@ class rdar129475277 { } } - takesEscapingWithAllowedImplicitSelf { [weak self] in // expected-warning {{variable 'self' was written to, but never read}} + // expected-no-weak-let-warning@+2 {{variable 'self' was written to, but never read}} + // expected-has-weak-let-warning@+1 {{capture 'self' was never used}} + takesEscapingWithAllowedImplicitSelf { [weak self] in withNonEscapingAutoclosure(bar()) // expected-warning {{call to method 'bar' in closure requires explicit use of 'self' to make capture semantics explicit}} } } @@ -1818,7 +1858,9 @@ class TestExtensionOnOptionalSelf { extension TestExtensionOnOptionalSelf? { func foo() { - _ = { [weak self] in // expected-warning {{variable 'self' was written to, but never read}} + // expected-no-weak-let-warning@+2 {{variable 'self' was written to, but never read}} + // expected-has-weak-let-warning@+1 {{capture 'self' was never used}} + _ = { [weak self] in foo() // expected-error {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} } @@ -1826,7 +1868,9 @@ extension TestExtensionOnOptionalSelf? { foo() } - _ = { [weak self] in // expected-warning {{variable 'self' was written to, but never read}} + // expected-no-weak-let-warning@+2 {{variable 'self' was written to, but never read}} + // expected-has-weak-let-warning@+1 {{capture 'self' was never used}} + _ = { [weak self] in _ = { foo() } diff --git a/test/expr/closure/closures_swift6.swift b/test/expr/closure/closures_swift6.swift index 5a44c7b813191..18859b75848f1 100644 --- a/test/expr/closure/closures_swift6.swift +++ b/test/expr/closure/closures_swift6.swift @@ -1,4 +1,12 @@ -// RUN: %target-typecheck-verify-swift -swift-version 6 +// There seems to be a minor bug in the diagnostic of the self-capture. +// Diagnostic algorithm does not @lvalue DeclRefExpr wrapped into LoadExpr, +// and enabling ImmutableWeakCaptures removes the LoadExpr. +// As a result, diagnostic messages change slightly. + +// RUN: %target-typecheck-verify-swift -swift-version 6 -verify-additional-prefix no-weak-let- +// RUN: %target-typecheck-verify-swift -swift-version 6 -verify-additional-prefix has-weak-let- -enable-upcoming-feature ImmutableWeakCaptures + +// REQUIRES: swift_feature_ImmutableWeakCaptures func doStuff(_ fn : @escaping () -> Int) {} func doVoidStuff(_ fn : @escaping () -> ()) {} @@ -936,7 +944,9 @@ class TestExtensionOnOptionalSelf { extension TestExtensionOnOptionalSelf? { func foo() { _ = { [weak self] in - foo() // expected-error {{implicit use of 'self' in closure; use 'self.' to make capture semantics explicit}} + // expected-no-weak-let-error@+2 {{implicit use of 'self' in closure; use 'self.' to make capture semantics explicit}} + // expected-has-weak-let-error@+1 {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} + foo() } _ = { @@ -946,8 +956,11 @@ extension TestExtensionOnOptionalSelf? { } _ = { [weak self] in - _ = { - foo() // expected-error {{implicit use of 'self' in closure; use 'self.' to make capture semantics explicit}} + _ = { // expected-has-weak-let-note {{capture 'self' explicitly to enable implicit 'self' in this closure}} + // expected-no-weak-let-error@+3 {{implicit use of 'self' in closure; use 'self.' to make capture semantics explicit}} + // expected-has-weak-let-error@+2 {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} + // expected-has-weak-let-note@+1 {{reference 'self.' explicitly}} + foo() self.foo() self?.bar() } @@ -967,7 +980,9 @@ extension TestExtensionOnOptionalSelf? { extension TestExtensionOnOptionalSelf { func foo() { _ = { [weak self] in - foo() // expected-error {{implicit use of 'self' in closure; use 'self.' to make capture semantics explicit}} + // expected-no-weak-let-error@+2 {{implicit use of 'self' in closure; use 'self.' to make capture semantics explicit}} + // expected-has-weak-let-error@+1 {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} + foo() self.foo() self?.bar() } @@ -978,8 +993,11 @@ extension TestExtensionOnOptionalSelf { } _ = { [weak self] in - _ = { - foo() // expected-error {{implicit use of 'self' in closure; use 'self.' to make capture semantics explicit}} + _ = { // expected-has-weak-let-note {{capture 'self' explicitly to enable implicit 'self' in this closure}} + // expected-no-weak-let-error@+3 {{implicit use of 'self' in closure; use 'self.' to make capture semantics explicit}} + // expected-has-weak-let-error@+2 {{call to method 'foo' in closure requires explicit use of 'self' to make capture semantics explicit}} + // expected-has-weak-let-note@+1 {{reference 'self.' explicitly}} + foo() self.foo() } } diff --git a/test/expr/closure/implicit_weak_capture.swift b/test/expr/closure/implicit_weak_capture.swift index 08475c21d0eeb..870d90505e013 100644 --- a/test/expr/closure/implicit_weak_capture.swift +++ b/test/expr/closure/implicit_weak_capture.swift @@ -1,7 +1,9 @@ // RUN: %target-run-simple-swift(-Xfrontend -disable-availability-checking -swift-version 6) +// RUN: %target-run-simple-swift(-Xfrontend -disable-availability-checking -swift-version 6 -enable-upcoming-feature ImmutableWeakCaptures) // REQUIRES: concurrency // REQUIRES: executable_test +// REQUIRES: swift_feature_ImmutableWeakCaptures // rdar://102155748 // UNSUPPORTED: back_deployment_runtime diff --git a/test/stdlib/WeakMirror.swift b/test/stdlib/WeakMirror.swift index 02ad4ab150d7f..baefe1451b718 100644 --- a/test/stdlib/WeakMirror.swift +++ b/test/stdlib/WeakMirror.swift @@ -14,9 +14,9 @@ // RUN: if [ %target-runtime == "objc" ]; \ // RUN: then \ // RUN: %target-clang %S/Inputs/Mirror/Mirror.mm -c -o %t/Mirror.mm.o -g && \ -// RUN: %target-build-swift -Xfrontend -disable-access-control %s -I %S/Inputs/Mirror/ -Xlinker %t/Mirror.mm.o -o %t/Mirror; \ +// RUN: %target-build-swift -Xfrontend -disable-access-control -Xfrontend -enable-experimental-feature -Xfrontend ImmutableWeakCaptures %s -I %S/Inputs/Mirror/ -Xlinker %t/Mirror.mm.o -o %t/Mirror; \ // RUN: else \ -// RUN: %target-build-swift %s -Xfrontend -disable-access-control -o %t/Mirror; \ +// RUN: %target-build-swift %s -Xfrontend -disable-access-control -Xfrontend -enable-experimental-feature -Xfrontend ImmutableWeakCaptures -o %t/Mirror; \ // RUN: fi // RUN: %target-codesign %t/Mirror // RUN: %target-run %t/Mirror @@ -24,6 +24,7 @@ // REQUIRES: executable_test // REQUIRES: shell // REQUIRES: reflection +// REQUIRES: swift_feature_ImmutableWeakCaptures import StdlibUnittest @@ -123,6 +124,89 @@ mirrors.test("class/NativeSwiftClassHasNativeWeakReferenceNoLeak") { expectNil(verifier) } +class NativeSwiftClassHasWeakLet { + weak let weakProperty: AnyObject? + let x: Int + init(x: Int, weakProperty: AnyObject?) { + self.x = x + self.weakProperty = weakProperty + } +} + +class NativeSwiftClassHasNativeClassBoundExistentialLet { + weak let weakProperty: NativeClassBoundExistential? + let x: Int + init(x: Int, weakProperty: NativeClassBoundExistential?) { + self.x = x + self.weakProperty = weakProperty + } +} + +struct StructHasNativeWeakLetReference { + weak let weakProperty: AnyObject? + let x: Int + init(x: Int, weakProperty: AnyObject?) { + self.x = x + self.weakProperty = weakProperty + } +} + +mirrors.test("class/NativeSwiftClassHasNativeWeakLetReference") { + let child = NativeSwiftClass(x: 2020) + let parent = NativeSwiftClassHasWeakLet(x: 1010, weakProperty: child) + let mirror = Mirror(reflecting: parent) + let children = Array(mirror.children) + let extractedChild = children[0].1 as! NativeSwiftClass + expectNotEqual(parent.x, extractedChild.x) + expectEqual(ObjectIdentifier(child), ObjectIdentifier(extractedChild)) + expectEqual(child.x, extractedChild.x) + print(extractedChild) +} + +mirrors.test("class/NativeSwiftClassHasNativeClassBoundExistentialLet") { + let child = NativeSwiftClass(x: 2020) as NativeClassBoundExistential + let parent = NativeSwiftClassHasNativeClassBoundExistentialLet(x: 1010, weakProperty: child) + let mirror = Mirror(reflecting: parent) + let children = Array(mirror.children) + let extractedChild = children[0].1 as! NativeSwiftClass + expectNotEqual(parent.x, extractedChild.x) + expectEqual(ObjectIdentifier(child), ObjectIdentifier(extractedChild)) + expectEqual(child.x, extractedChild.x) + print(extractedChild) +} + +mirrors.test("struct/StructHasNativeWeakLetReference") { + let child = NativeSwiftClass(x: 2020) + var parent = StructHasNativeWeakLetReference(x: 1010, weakProperty: child) + let mirror = Mirror(reflecting: parent) + let children = Array(mirror.children) + let extractedChild = children[0].1 as! NativeSwiftClass + expectNotEqual(parent.x, extractedChild.x) + expectEqual(ObjectIdentifier(child), ObjectIdentifier(extractedChild)) + expectEqual(child.x, extractedChild.x) + print(extractedChild) +} + +// https://github.com/apple/swift/issues/51384 +// Using 'Mirror' to access a weak reference results in object being +// retained indefinitely +mirrors.test("class/NativeSwiftClassHasNativeWeakLetReferenceNoLeak") { + weak var verifier: AnyObject? + do { + let child = NativeSwiftClass(x: 2020) + verifier = child + let parent = NativeSwiftClassHasWeakLet(x: 1010, weakProperty: child) + let mirror = Mirror(reflecting: parent) + let children = Array(mirror.children) + let extractedChild = children[0].1 as! NativeSwiftClass + expectNotNil(extractedChild) + expectNotNil(verifier) + // If child is destroyed, the above cast and checks will fail. + _fixLifetime(child) + } + expectNil(verifier) +} + #if _runtime(_ObjC) import Foundation @@ -292,6 +376,156 @@ mirrors.test("struct/StructHasObjCClassBoundExistential") { print(extractedChild) } +class NativeSwiftClassHasObjCClassBoundExistentialLet { + weak let weakProperty: ObjCClassExistential? + let x: Int + init(x: Int, weakProperty: ObjCClassExistential?) { + self.x = x + self.weakProperty = weakProperty + } +} + +class ObjCClassHasWeakLet : NSObject { + weak let weakProperty: AnyObject? + let x: Int + init(x: Int, weakProperty: AnyObject?) { + self.x = x + self.weakProperty = weakProperty + } +} + +class ObjCClassHasNativeClassBoundExistentialLet : NSObject { + weak let weakProperty: NativeClassBoundExistential? + let x: Int + init(x: Int, weakProperty: NativeClassBoundExistential?) { + self.x = x + self.weakProperty = weakProperty + } +} + +class ObjCClassHasObjCClassBoundExistentialLet : NSObject { + weak let weakProperty: ObjCClassExistential? + let x: Int + init(x: Int, weakProperty: ObjCClassExistential?) { + self.x = x + self.weakProperty = weakProperty + } +} + +struct StructHasObjCWeakLetReference { + weak let weakProperty: ObjCClass? + let x: Int + init(x: Int, weakProperty: ObjCClass?) { + self.x = x + self.weakProperty = weakProperty + } +} + +struct StructHasObjCClassBoundExistentialLet { + weak let weakProperty: ObjCClassExistential? + let x: Int + init(x: Int, weakProperty: ObjCClassExistential?) { + self.x = x + self.weakProperty = weakProperty + } +} + +mirrors.test("class/NativeSwiftClassHasObjCWeakLetReference") { + let child = ObjCClass(x: 2020) + let parent = NativeSwiftClassHasWeakLet(x: 1010, weakProperty: child) + let mirror = Mirror(reflecting: parent) + let children = Array(mirror.children) + let extractedChild = children[0].1 as! ObjCClass + expectNotEqual(parent.x, extractedChild.x) + expectEqual(ObjectIdentifier(child), ObjectIdentifier(extractedChild)) + expectEqual(child.x, extractedChild.x) + print(extractedChild) +} + +mirrors.test("class/NativeSwiftClassHasObjCClassBoundExistentialLet") { + let child = ObjCClass(x: 2020) as ObjCClassExistential + let parent = NativeSwiftClassHasObjCClassBoundExistentialLet(x: 1010, weakProperty: child) + let mirror = Mirror(reflecting: parent) + let children = Array(mirror.children) + let extractedChild = children[0].1 as! ObjCClass + expectNotEqual(parent.x, extractedChild.x) + expectEqual(ObjectIdentifier(child), ObjectIdentifier(extractedChild)) + expectEqual(child.x, extractedChild.x) + print(extractedChild) +} + +mirrors.test("class/ObjCClassHasNativeWeakLet") { + let child = NativeSwiftClass(x: 2020) + let parent = ObjCClassHasWeakLet(x: 1010, weakProperty: child) + let mirror = Mirror(reflecting: parent) + let children = Array(mirror.children) + let extractedChild = children[0].1 as! NativeSwiftClass + expectNotEqual(parent.x, extractedChild.x) + expectEqual(ObjectIdentifier(child), ObjectIdentifier(extractedChild)) + expectEqual(child.x, extractedChild.x) + print(extractedChild) +} + +mirrors.test("class/ObjcCClassHasObjCWeakLetReference") { + let child = ObjCClass(x: 2020) + let parent = ObjCClassHasWeakLet(x: 1010, weakProperty: child) + let mirror = Mirror(reflecting: parent) + let children = Array(mirror.children) + let extractedChild = children[0].1 as! ObjCClass + expectNotEqual(parent.x, extractedChild.x) + expectEqual(ObjectIdentifier(child), ObjectIdentifier(extractedChild)) + expectEqual(child.x, extractedChild.x) + print(extractedChild) +} + +mirrors.test("class/ObjCClassHasNativeClassBoundExistentialLet") { + let child = NativeSwiftClass(x: 2020) as NativeClassBoundExistential + let parent = ObjCClassHasNativeClassBoundExistentialLet(x: 1010, weakProperty: child) + let mirror = Mirror(reflecting: parent) + let children = Array(mirror.children) + let extractedChild = children[0].1 as! NativeSwiftClass + expectNotEqual(parent.x, extractedChild.x) + expectEqual(ObjectIdentifier(child), ObjectIdentifier(extractedChild)) + expectEqual(child.x, extractedChild.x) + print(extractedChild) +} + +mirrors.test("class/ObjCClassHasObjCClassBoundExistentialLet") { + let child = ObjCClass(x: 2020) as ObjCClassExistential + let parent = ObjCClassHasObjCClassBoundExistentialLet(x: 1010, weakProperty: child) + let mirror = Mirror(reflecting: parent) + let children = Array(mirror.children) + let extractedChild = children[0].1 as! ObjCClass + expectNotEqual(parent.x, extractedChild.x) + expectEqual(ObjectIdentifier(child), ObjectIdentifier(extractedChild)) + expectEqual(child.x, extractedChild.x) + print(extractedChild) +} + +mirrors.test("struct/StructHasObjCWeakLetReference") { + let child = ObjCClass(x: 2020) + var parent = StructHasObjCWeakLetReference(x: 1010, weakProperty: child) + let mirror = Mirror(reflecting: parent) + let children = Array(mirror.children) + let extractedChild = children[0].1 as! ObjCClass + expectNotEqual(parent.x, extractedChild.x) + expectEqual(ObjectIdentifier(child), ObjectIdentifier(extractedChild)) + expectEqual(child.x, extractedChild.x) + print(extractedChild) +} + +mirrors.test("struct/StructHasObjCClassBoundExistentialLet") { + let child = ObjCClass(x: 2020) as ObjCClassExistential + var parent = StructHasObjCClassBoundExistentialLet(x: 1010, weakProperty: child) + let mirror = Mirror(reflecting: parent) + let children = Array(mirror.children) + let extractedChild = children[0].1 as! ObjCClass + expectNotEqual(parent.x, extractedChild.x) + expectEqual(ObjectIdentifier(child), ObjectIdentifier(extractedChild)) + expectEqual(child.x, extractedChild.x) + print(extractedChild) +} + #endif runAllTests()