From eb71bbd46d054f0e3f4efb7a0453190a51d92094 Mon Sep 17 00:00:00 2001 From: Konrad 'ktoso' Malawski Date: Tue, 15 Jul 2025 09:16:48 +0900 Subject: [PATCH 1/2] [Concurrency] add fixit to add final to non-sendable class -> Sendable The previous message was just suggesting unchecked Sendable, but instead we should be suggesting to add final to the class. We also don't outright suggest using unchecked Sendable -- following https://github.com/swiftlang/swift/pull/81738 precedent. Resolves rdar://155790695 --- include/swift/AST/DiagnosticsSema.def | 3 +-- lib/Sema/TypeCheckConcurrency.cpp | 6 +++--- test/Concurrency/concurrency_warnings.swift | 2 +- test/Concurrency/concurrent_value_checking.swift | 2 +- test/Concurrency/sendable_conformance_checking.swift | 4 ++-- test/Concurrency/sendable_objc_protocol_attr.swift | 6 +++--- test/Distributed/actor_protocols.swift | 2 +- test/Macros/macro_expand_extensions.swift | 2 +- test/decl/class/actor/basic.swift | 2 +- test/decl/protocol/special/Actor.swift | 6 +++--- 10 files changed, 17 insertions(+), 18 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 0c646d2c93458..f0cf2d2818c9e 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -5898,8 +5898,7 @@ ERROR(concurrent_value_outside_source_file,none, "%kind0; use '@unchecked Sendable' for retroactive conformance", (const ValueDecl *)) ERROR(concurrent_value_nonfinal_class,none, - "non-final class %0 cannot conform to 'Sendable'; " - "use '@unchecked Sendable'", (DeclName)) + "non-final class %0 can not conform to the 'Sendable' protocol", (DeclName)) ERROR(concurrent_value_inherit,none, "'Sendable' class %1 cannot inherit from another class" "%select{| other than 'NSObject'}0", diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index 86b6670b2a4cb..1fd5c9628724e 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -7270,9 +7270,9 @@ bool swift::checkSendableConformance( // An non-final class cannot conform to `Sendable`. if (!classDecl->isSemanticallyFinal()) { - classDecl->diagnose(diag::concurrent_value_nonfinal_class, - classDecl->getName()) - .limitBehaviorUntilSwiftVersion(behavior, 6); + classDecl->diagnose(diag::concurrent_value_nonfinal_class,classDecl->getName()) + .fixItInsert(classDecl->getStartLoc(), "final") + .limitBehaviorUntilSwiftVersion(behavior, 6); if (behavior == DiagnosticBehavior::Unspecified) return true; diff --git a/test/Concurrency/concurrency_warnings.swift b/test/Concurrency/concurrency_warnings.swift index c9cc307fc77b4..d646292109381 100644 --- a/test/Concurrency/concurrency_warnings.swift +++ b/test/Concurrency/concurrency_warnings.swift @@ -17,7 +17,7 @@ let rs = GlobalCounter() // expected-warning {{let 'rs' is not concurrency-safe import GlobalVariables -class MyError: Error { // expected-warning{{non-final class 'MyError' cannot conform to 'Sendable'; use '@unchecked Sendable'}} +class MyError: Error { // expected-warning{{non-final class 'MyError' can not conform to the 'Sendable' protocol; this is an error in the Swift 6 language mode}} var storage = 0 // expected-warning{{stored property 'storage' of 'Sendable'-conforming class 'MyError' is mutable}} } diff --git a/test/Concurrency/concurrent_value_checking.swift b/test/Concurrency/concurrent_value_checking.swift index 7dc503268a0d3..e02afa274debc 100644 --- a/test/Concurrency/concurrent_value_checking.swift +++ b/test/Concurrency/concurrent_value_checking.swift @@ -377,7 +377,7 @@ extension C6_Restated_Extension: @unchecked Sendable {} final class C7: Sendable { } -class C9: Sendable { } // expected-warning{{non-final class 'C9' cannot conform to 'Sendable'; use '@unchecked Sendable'}} +class C9: Sendable { } // expected-warning{{non-final class 'C9' can not conform to the 'Sendable' protocol; this is an error in the Swift 6 language mode}} @available(*, unavailable) extension HasUnavailableSendable : @unchecked Sendable { } diff --git a/test/Concurrency/sendable_conformance_checking.swift b/test/Concurrency/sendable_conformance_checking.swift index 92475038fe3c7..b881c6ddb1992 100644 --- a/test/Concurrency/sendable_conformance_checking.swift +++ b/test/Concurrency/sendable_conformance_checking.swift @@ -158,7 +158,7 @@ actor A10: AsyncThrowingProtocolWithNotSendable { } // rdar://86653457 - Crash due to missing Sendable conformances. -// expected-warning @+1 {{non-final class 'Klass' cannot conform to 'Sendable'; use '@unchecked Sendable'}} +// expected-warning @+1 {{non-final class 'Klass' can not conform to the 'Sendable' protocol; this is an error in the Swift 6 language mode}} class Klass: Sendable {} // expected-complete-and-tns-warning @+1 {{type 'S' does not conform to the 'Sendable' protocol}} final class SubKlass: Klass<[S]> {} @@ -184,7 +184,7 @@ extension MultiConformance: @unchecked Sendable {} // expected-warning {{redunda @available(SwiftStdlib 5.1, *) actor MyActor { - // expected-warning@+1 {{non-final class 'Nested' cannot conform to 'Sendable'; use '@unchecked Sendable'; this is an error in the Swift 6 language mode}} + // expected-warning@+1 {{non-final class 'Nested' can not conform to the 'Sendable' protocol; this is an error in the Swift 6 language mode}} class Nested: Sendable {} } diff --git a/test/Concurrency/sendable_objc_protocol_attr.swift b/test/Concurrency/sendable_objc_protocol_attr.swift index 15527a7a91a55..ffe07d3aa08a4 100644 --- a/test/Concurrency/sendable_objc_protocol_attr.swift +++ b/test/Concurrency/sendable_objc_protocol_attr.swift @@ -41,7 +41,7 @@ extension MyObjCProtocol { } class K : NSObject, MyObjCProtocol { - // expected-warning@-1 {{non-final class 'K' cannot conform to 'Sendable'; use '@unchecked Sendable'}} + // expected-warning@-1 {{non-final class 'K' can not conform to the 'Sendable' protocol; this is an error in the Swift 6 language mode}} let test: String = "k" } @@ -50,7 +50,7 @@ class UncheckedK : NSObject, MyObjCProtocol, @unchecked Sendable { // ok } class KRefined : NSObject, MyRefinedObjCProtocol { - // expected-warning@-1 {{non-final class 'KRefined' cannot conform to 'Sendable'; use '@unchecked Sendable'}} + // expected-warning@-1 {{non-final class 'KRefined' can not conform to the 'Sendable' protocol; this is an error in the Swift 6 language mode}} let test: String = "refined" } @@ -76,7 +76,7 @@ do { } class C : A, MyObjCProtocol { - // expected-warning@-1 {{non-final class 'C' cannot conform to 'Sendable'; use '@unchecked Sendable'}} + // expected-warning@-1 {{non-final class 'C' can not conform to the 'Sendable' protocol; this is an error in the Swift 6 language mode}} // expected-warning@-2 {{'Sendable' class 'C' cannot inherit from another class other than 'NSObject'}} let test: String = "c" } diff --git a/test/Distributed/actor_protocols.swift b/test/Distributed/actor_protocols.swift index d3e728ff5de16..b9f6a2f34140c 100644 --- a/test/Distributed/actor_protocols.swift +++ b/test/Distributed/actor_protocols.swift @@ -95,7 +95,7 @@ actor A3: AnyActor {} // expected-warning {{'AnyActor' is deprecated: Use 'any A distributed actor DA3: AnyActor {} // expected-warning {{'AnyActor' is deprecated: Use 'any Actor' with 'DistributedActor.asLocalActor' instead}} class C3: AnyActor { // expected-warning {{'AnyActor' is deprecated: Use 'any Actor' with 'DistributedActor.asLocalActor' instead}} - // expected-warning@-1 {{non-final class 'C3' cannot conform to 'Sendable'; use '@unchecked Sendable'}} + // expected-warning@-1 {{non-final class 'C3' cannot conform to 'Sendable'}} } struct S3: AnyActor { // expected-warning {{'AnyActor' is deprecated: Use 'any Actor' with 'DistributedActor.asLocalActor' instead}} diff --git a/test/Macros/macro_expand_extensions.swift b/test/Macros/macro_expand_extensions.swift index b333b7d12d802..25e7b715db01a 100644 --- a/test/Macros/macro_expand_extensions.swift +++ b/test/Macros/macro_expand_extensions.swift @@ -269,7 +269,7 @@ macro AddSendable() = #externalMacro(module: "MacroDefinition", type: "SendableM final class SendableClass { } -// expected-warning@+2 {{non-final class 'InvalidSendableClass' cannot conform to 'Sendable'; use '@unchecked Sendable'}} +// expected-warning@+2 {{non-final class 'InvalidSendableClass' cannot conform to 'Sendable'}} @AddSendable class InvalidSendableClass { } diff --git a/test/decl/class/actor/basic.swift b/test/decl/class/actor/basic.swift index 72c784864848a..c53ad31700030 100644 --- a/test/decl/class/actor/basic.swift +++ b/test/decl/class/actor/basic.swift @@ -7,7 +7,7 @@ actor MyActor { } class MyActorSubclass1: MyActor { } // expected-error@-1{{actor types do not support inheritance}} // expected-error@-2{{type 'MyActorSubclass1' cannot conform to the 'Actor' protocol}} -// expected-warning@-3 {{non-final class 'MyActorSubclass1' cannot conform to 'Sendable'; use '@unchecked Sendable'; this is an error in the Swift 6 language mode}} +// expected-warning@-3 {{non-final class 'MyActorSubclass1' cannot conform to 'Sendable'; this is an error in the Swift 6 language mode}} actor MyActorSubclass2: MyActor { } // expected-error{{actor types do not support inheritance}} diff --git a/test/decl/protocol/special/Actor.swift b/test/decl/protocol/special/Actor.swift index 6e11f8628413c..bb0f528f80981 100644 --- a/test/decl/protocol/special/Actor.swift +++ b/test/decl/protocol/special/Actor.swift @@ -44,7 +44,7 @@ actor A7 { @available(SwiftStdlib 5.1, *) class C1: Actor { // expected-error@-1{{non-actor type 'C1' cannot conform to the 'Actor' protocol}} - // expected-warning@-2{{non-final class 'C1' cannot conform to 'Sendable'; use '@unchecked Sendable'}} + // expected-warning@-2{{non-final class 'C1' cannot conform to 'Sendable'}} nonisolated var unownedExecutor: UnownedSerialExecutor { fatalError("") } @@ -53,7 +53,7 @@ class C1: Actor { @available(SwiftStdlib 5.1, *) class C2: Actor { // expected-error@-1{{non-actor type 'C2' cannot conform to the 'Actor' protocol}} -// expected-warning@-2{{non-final class 'C2' cannot conform to 'Sendable'; use '@unchecked Sendable'}} + // expected-warning@-2{{non-final class 'C2' cannot conform to 'Sendable'}} // FIXME: this should be an isolation violation var unownedExecutor: UnownedSerialExecutor { fatalError("") @@ -65,7 +65,7 @@ class C3: Actor { // expected-error@-1{{type 'C3' does not conform to protocol 'Actor'}} // expected-note@-2{{add stubs for conformance}} // expected-error@-3{{non-actor type 'C3' cannot conform to the 'Actor' protocol}} - // expected-warning@-4{{non-final class 'C3' cannot conform to 'Sendable'; use '@unchecked Sendable'}} + // expected-warning@-4{{non-final class 'C3' cannot conform to 'Sendable'}} nonisolated func enqueue(_ job: UnownedJob) { } } From 159ffc6adc75628455747f9191a65e38505f49f7 Mon Sep 17 00:00:00 2001 From: Konrad 'ktoso' Malawski Date: Tue, 15 Jul 2025 11:16:13 +0900 Subject: [PATCH 2/2] fix some tests due to diagnostic rewording --- include/swift/AST/DiagnosticsSema.def | 2 +- test/Concurrency/concurrency_warnings.swift | 2 +- test/Concurrency/concurrent_value_checking.swift | 2 +- test/Concurrency/sendable_conformance_checking.swift | 4 ++-- test/Concurrency/sendable_objc_protocol_attr.swift | 6 +++--- test/Distributed/actor_protocols.swift | 2 +- test/Macros/macro_expand_extensions.swift | 2 +- test/decl/class/actor/basic.swift | 2 +- test/decl/protocol/special/Actor.swift | 6 +++--- 9 files changed, 14 insertions(+), 14 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index f0cf2d2818c9e..b7c48b5a29633 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -5898,7 +5898,7 @@ ERROR(concurrent_value_outside_source_file,none, "%kind0; use '@unchecked Sendable' for retroactive conformance", (const ValueDecl *)) ERROR(concurrent_value_nonfinal_class,none, - "non-final class %0 can not conform to the 'Sendable' protocol", (DeclName)) + "non-final class %0 cannot conform to the 'Sendable' protocol", (DeclName)) ERROR(concurrent_value_inherit,none, "'Sendable' class %1 cannot inherit from another class" "%select{| other than 'NSObject'}0", diff --git a/test/Concurrency/concurrency_warnings.swift b/test/Concurrency/concurrency_warnings.swift index d646292109381..d825ada3d9284 100644 --- a/test/Concurrency/concurrency_warnings.swift +++ b/test/Concurrency/concurrency_warnings.swift @@ -17,7 +17,7 @@ let rs = GlobalCounter() // expected-warning {{let 'rs' is not concurrency-safe import GlobalVariables -class MyError: Error { // expected-warning{{non-final class 'MyError' can not conform to the 'Sendable' protocol; this is an error in the Swift 6 language mode}} +class MyError: Error { // expected-warning{{non-final class 'MyError' cannot conform to the 'Sendable' protocol; this is an error in the Swift 6 language mode}} var storage = 0 // expected-warning{{stored property 'storage' of 'Sendable'-conforming class 'MyError' is mutable}} } diff --git a/test/Concurrency/concurrent_value_checking.swift b/test/Concurrency/concurrent_value_checking.swift index e02afa274debc..a209edc26ce66 100644 --- a/test/Concurrency/concurrent_value_checking.swift +++ b/test/Concurrency/concurrent_value_checking.swift @@ -377,7 +377,7 @@ extension C6_Restated_Extension: @unchecked Sendable {} final class C7: Sendable { } -class C9: Sendable { } // expected-warning{{non-final class 'C9' can not conform to the 'Sendable' protocol; this is an error in the Swift 6 language mode}} +class C9: Sendable { } // expected-warning{{non-final class 'C9' cannot conform to the 'Sendable' protocol; this is an error in the Swift 6 language mode}} @available(*, unavailable) extension HasUnavailableSendable : @unchecked Sendable { } diff --git a/test/Concurrency/sendable_conformance_checking.swift b/test/Concurrency/sendable_conformance_checking.swift index b881c6ddb1992..5f77d9c7b7602 100644 --- a/test/Concurrency/sendable_conformance_checking.swift +++ b/test/Concurrency/sendable_conformance_checking.swift @@ -158,7 +158,7 @@ actor A10: AsyncThrowingProtocolWithNotSendable { } // rdar://86653457 - Crash due to missing Sendable conformances. -// expected-warning @+1 {{non-final class 'Klass' can not conform to the 'Sendable' protocol; this is an error in the Swift 6 language mode}} +// expected-warning @+1 {{non-final class 'Klass' cannot conform to the 'Sendable' protocol; this is an error in the Swift 6 language mode}} class Klass: Sendable {} // expected-complete-and-tns-warning @+1 {{type 'S' does not conform to the 'Sendable' protocol}} final class SubKlass: Klass<[S]> {} @@ -184,7 +184,7 @@ extension MultiConformance: @unchecked Sendable {} // expected-warning {{redunda @available(SwiftStdlib 5.1, *) actor MyActor { - // expected-warning@+1 {{non-final class 'Nested' can not conform to the 'Sendable' protocol; this is an error in the Swift 6 language mode}} + // expected-warning@+1 {{non-final class 'Nested' cannot conform to the 'Sendable' protocol; this is an error in the Swift 6 language mode}} class Nested: Sendable {} } diff --git a/test/Concurrency/sendable_objc_protocol_attr.swift b/test/Concurrency/sendable_objc_protocol_attr.swift index ffe07d3aa08a4..2b94fc80fa1eb 100644 --- a/test/Concurrency/sendable_objc_protocol_attr.swift +++ b/test/Concurrency/sendable_objc_protocol_attr.swift @@ -41,7 +41,7 @@ extension MyObjCProtocol { } class K : NSObject, MyObjCProtocol { - // expected-warning@-1 {{non-final class 'K' can not conform to the 'Sendable' protocol; this is an error in the Swift 6 language mode}} + // expected-warning@-1 {{non-final class 'K' cannot conform to the 'Sendable' protocol; this is an error in the Swift 6 language mode}} let test: String = "k" } @@ -50,7 +50,7 @@ class UncheckedK : NSObject, MyObjCProtocol, @unchecked Sendable { // ok } class KRefined : NSObject, MyRefinedObjCProtocol { - // expected-warning@-1 {{non-final class 'KRefined' can not conform to the 'Sendable' protocol; this is an error in the Swift 6 language mode}} + // expected-warning@-1 {{non-final class 'KRefined' cannot conform to the 'Sendable' protocol; this is an error in the Swift 6 language mode}} let test: String = "refined" } @@ -76,7 +76,7 @@ do { } class C : A, MyObjCProtocol { - // expected-warning@-1 {{non-final class 'C' can not conform to the 'Sendable' protocol; this is an error in the Swift 6 language mode}} + // expected-warning@-1 {{non-final class 'C' cannot conform to the 'Sendable' protocol; this is an error in the Swift 6 language mode}} // expected-warning@-2 {{'Sendable' class 'C' cannot inherit from another class other than 'NSObject'}} let test: String = "c" } diff --git a/test/Distributed/actor_protocols.swift b/test/Distributed/actor_protocols.swift index b9f6a2f34140c..17717892517e4 100644 --- a/test/Distributed/actor_protocols.swift +++ b/test/Distributed/actor_protocols.swift @@ -95,7 +95,7 @@ actor A3: AnyActor {} // expected-warning {{'AnyActor' is deprecated: Use 'any A distributed actor DA3: AnyActor {} // expected-warning {{'AnyActor' is deprecated: Use 'any Actor' with 'DistributedActor.asLocalActor' instead}} class C3: AnyActor { // expected-warning {{'AnyActor' is deprecated: Use 'any Actor' with 'DistributedActor.asLocalActor' instead}} - // expected-warning@-1 {{non-final class 'C3' cannot conform to 'Sendable'}} + // expected-warning@-1 {{non-final class 'C3' cannot conform to the 'Sendable' protocol}} } struct S3: AnyActor { // expected-warning {{'AnyActor' is deprecated: Use 'any Actor' with 'DistributedActor.asLocalActor' instead}} diff --git a/test/Macros/macro_expand_extensions.swift b/test/Macros/macro_expand_extensions.swift index 25e7b715db01a..7196eea12465a 100644 --- a/test/Macros/macro_expand_extensions.swift +++ b/test/Macros/macro_expand_extensions.swift @@ -269,7 +269,7 @@ macro AddSendable() = #externalMacro(module: "MacroDefinition", type: "SendableM final class SendableClass { } -// expected-warning@+2 {{non-final class 'InvalidSendableClass' cannot conform to 'Sendable'}} +// expected-warning@+2 {{non-final class 'InvalidSendableClass' cannot conform to the 'Sendable' protocol}} @AddSendable class InvalidSendableClass { } diff --git a/test/decl/class/actor/basic.swift b/test/decl/class/actor/basic.swift index c53ad31700030..23749ee114d15 100644 --- a/test/decl/class/actor/basic.swift +++ b/test/decl/class/actor/basic.swift @@ -7,7 +7,7 @@ actor MyActor { } class MyActorSubclass1: MyActor { } // expected-error@-1{{actor types do not support inheritance}} // expected-error@-2{{type 'MyActorSubclass1' cannot conform to the 'Actor' protocol}} -// expected-warning@-3 {{non-final class 'MyActorSubclass1' cannot conform to 'Sendable'; this is an error in the Swift 6 language mode}} +// expected-warning@-3 {{non-final class 'MyActorSubclass1' cannot conform to the 'Sendable' protocol; this is an error in the Swift 6 language mode}} actor MyActorSubclass2: MyActor { } // expected-error{{actor types do not support inheritance}} diff --git a/test/decl/protocol/special/Actor.swift b/test/decl/protocol/special/Actor.swift index bb0f528f80981..9a42697489511 100644 --- a/test/decl/protocol/special/Actor.swift +++ b/test/decl/protocol/special/Actor.swift @@ -44,7 +44,7 @@ actor A7 { @available(SwiftStdlib 5.1, *) class C1: Actor { // expected-error@-1{{non-actor type 'C1' cannot conform to the 'Actor' protocol}} - // expected-warning@-2{{non-final class 'C1' cannot conform to 'Sendable'}} + // expected-warning@-2{{non-final class 'C1' cannot conform to the 'Sendable' protocol}} nonisolated var unownedExecutor: UnownedSerialExecutor { fatalError("") } @@ -53,7 +53,7 @@ class C1: Actor { @available(SwiftStdlib 5.1, *) class C2: Actor { // expected-error@-1{{non-actor type 'C2' cannot conform to the 'Actor' protocol}} - // expected-warning@-2{{non-final class 'C2' cannot conform to 'Sendable'}} + // expected-warning@-2{{non-final class 'C2' cannot conform to the 'Sendable' protocol}} // FIXME: this should be an isolation violation var unownedExecutor: UnownedSerialExecutor { fatalError("") @@ -65,7 +65,7 @@ class C3: Actor { // expected-error@-1{{type 'C3' does not conform to protocol 'Actor'}} // expected-note@-2{{add stubs for conformance}} // expected-error@-3{{non-actor type 'C3' cannot conform to the 'Actor' protocol}} - // expected-warning@-4{{non-final class 'C3' cannot conform to 'Sendable'}} + // expected-warning@-4{{non-final class 'C3' cannot conform to the 'Sendable' protocol}} nonisolated func enqueue(_ job: UnownedJob) { } }