Skip to content

Commit ecdd42f

Browse files
committed
Make @preconcurrency import hide sendable variance
When the conformance checker and override checker detect a difference in a function type’s @sendable attribute that varies in an illegal way, they now check if the protocol/base class was imported with an @preconcurrency import, and either limit the diagnostic or suggest adding @preconcurrency to the import as appropriate. Completes rdar://91109455.
1 parent c5fde06 commit ecdd42f

9 files changed

+139
-33
lines changed

lib/Sema/TypeCheckDeclOverride.cpp

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
//===----------------------------------------------------------------------===//
1616
#include "MiscDiagnostics.h"
1717
#include "TypeCheckAvailability.h"
18+
#include "TypeCheckConcurrency.h"
1819
#include "TypeCheckDecl.h"
1920
#include "TypeCheckObjC.h"
2021
#include "TypeChecker.h"
@@ -548,12 +549,20 @@ static void diagnoseGeneralOverrideFailure(ValueDecl *decl,
548549
diags.diagnose(decl, diag::override_multiple_decls_base,
549550
decl->getName());
550551
break;
551-
case OverrideCheckingAttempt::MismatchedSendability:
552-
// FIXME: suppress if any matches brought in via @preconcurrency import?
553-
diags.diagnose(decl, diag::override_sendability_mismatch,
554-
decl->getName())
555-
.warnUntilSwiftVersion(6);
552+
case OverrideCheckingAttempt::MismatchedSendability: {
553+
SendableCheckContext fromContext(decl->getDeclContext(),
554+
SendableCheck::Explicit);
555+
auto baseDeclClass =
556+
decl->getOverriddenDecl()->getDeclContext()->getSelfClassDecl();
557+
558+
diagnoseSendabilityErrorBasedOn(baseDeclClass, fromContext,
559+
[&](DiagnosticBehavior limit) {
560+
diags.diagnose(decl, diag::override_sendability_mismatch, decl->getName())
561+
.limitBehavior(limit);
562+
return false;
563+
});
556564
break;
565+
}
557566
case OverrideCheckingAttempt::BaseName:
558567
diags.diagnose(decl, diag::override_multiple_decls_arg_mismatch,
559568
decl->getName());
@@ -1288,11 +1297,19 @@ bool OverrideMatcher::checkOverride(ValueDecl *baseDecl,
12881297
return true;
12891298

12901299
if (attempt == OverrideCheckingAttempt::MismatchedSendability) {
1291-
// FIXME: suppress if any matches brought in via @preconcurrency import?
1292-
diags.diagnose(decl, diag::override_sendability_mismatch,
1293-
decl->getName())
1294-
.warnUntilSwiftVersion(6);
1295-
diags.diagnose(baseDecl, diag::overridden_here);
1300+
SendableCheckContext fromContext(decl->getDeclContext(),
1301+
SendableCheck::Explicit);
1302+
auto baseDeclClass = baseDecl->getDeclContext()->getSelfClassDecl();
1303+
1304+
diagnoseSendabilityErrorBasedOn(baseDeclClass, fromContext,
1305+
[&](DiagnosticBehavior limit) {
1306+
diags.diagnose(decl, diag::override_sendability_mismatch,
1307+
decl->getName())
1308+
.limitBehavior(limit);
1309+
diags.diagnose(baseDecl, diag::overridden_here)
1310+
.limitBehavior(limit);
1311+
return false;
1312+
});
12961313
}
12971314
// Catch-all to make sure we don't silently accept something we shouldn't.
12981315
else if (attempt != OverrideCheckingAttempt::PerfectMatch) {

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4154,19 +4154,32 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
41544154
requirement->getName());
41554155
});
41564156
}
4157-
if (best.Kind == MatchKind::RequiresNonSendable)
4158-
diagnoseOrDefer(requirement, getASTContext().isSwiftVersionAtLeast(6),
4159-
[this, requirement, witness]
4160-
(NormalProtocolConformance *conformance) {
4161-
auto &diags = DC->getASTContext().Diags;
4157+
if (best.Kind == MatchKind::RequiresNonSendable) {
4158+
SendableCheckContext sendFrom(witness->getDeclContext(),
4159+
SendableCheck::Explicit);
41624160

4163-
diags.diagnose(getLocForDiagnosingWitness(conformance, witness),
4164-
diag::witness_not_as_sendable,
4165-
witness->getDescriptiveKind(), witness->getName(),
4166-
conformance->getProtocol()->getName())
4167-
.warnUntilSwiftVersion(6);
4168-
diags.diagnose(requirement, diag::less_sendable_reqt_here);
4169-
});
4161+
auto behavior = sendFrom.diagnosticBehavior(Conformance->getProtocol());
4162+
if (behavior != DiagnosticBehavior::Ignore) {
4163+
bool isError = behavior < DiagnosticBehavior::Warning;
4164+
4165+
diagnoseOrDefer(requirement, isError,
4166+
[this, requirement, witness, sendFrom](
4167+
NormalProtocolConformance *conformance) {
4168+
diagnoseSendabilityErrorBasedOn(conformance->getProtocol(), sendFrom,
4169+
[&](DiagnosticBehavior limit) {
4170+
auto &diags = DC->getASTContext().Diags;
4171+
diags.diagnose(getLocForDiagnosingWitness(conformance, witness),
4172+
diag::witness_not_as_sendable,
4173+
witness->getDescriptiveKind(), witness->getName(),
4174+
conformance->getProtocol()->getName())
4175+
.limitBehavior(limit);
4176+
diags.diagnose(requirement, diag::less_sendable_reqt_here)
4177+
.limitBehavior(limit);
4178+
return false;
4179+
});
4180+
});
4181+
}
4182+
}
41704183

41714184
auto check = checkWitness(requirement, best);
41724185

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1-
public class NonStrictClass { }
2-
31
public struct NonStrictStruct { }
2+
3+
open class NonStrictClass {
4+
open func send(_ body: @Sendable () -> Void) {}
5+
open func dontSend(_ body: () -> Void) {}
6+
}
7+
8+
public protocol NonStrictProtocol {
9+
func send(_ body: @Sendable () -> Void)
10+
func dontSend(_ body: () -> Void)
11+
}
Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
11
public struct StrictStruct: Hashable { }
22

3-
open class StrictClass { }
3+
open class StrictClass {
4+
open func send(_ body: @Sendable () -> Void) {}
5+
open func dontSend(_ body: () -> Void) {}
6+
}
7+
8+
public protocol StrictProtocol {
9+
func send(_ body: @Sendable () -> Void)
10+
func dontSend(_ body: () -> Void)
11+
}

test/Concurrency/sendable_preconcurrency.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,23 @@ func testA(ns: NS, mt: MyType, mt2: MyType2, mt3: MyType3, sc: StrictClass, nsc:
3939
}
4040

4141
extension NonStrictStruct: @unchecked Sendable { }
42+
43+
class StrictSubclass: StrictClass {
44+
override func send(_ body: () -> ()) {}
45+
override func dontSend(_ body: @Sendable () -> ()) {} // expected-warning {{declaration 'dontSend' has a type with different sendability from any potential overrides}}
46+
}
47+
48+
struct StrictConformer: StrictProtocol {
49+
func send(_ body: () -> Void) {}
50+
func dontSend(_ body: @Sendable () -> Void) {} // expected-warning {{sendability of function types in instance method 'dontSend' does not match requirement in protocol 'StrictProtocol'}}
51+
}
52+
53+
class NonStrictSubclass: NonStrictClass {
54+
override func send(_ body: () -> ()) {}
55+
override func dontSend(_ body: @Sendable () -> ()) {} // no-warning
56+
}
57+
58+
struct NonStrictConformer: NonStrictProtocol {
59+
func send(_ body: () -> Void) {}
60+
func dontSend(_ body: @Sendable () -> Void) {} // no-warning
61+
}

test/Concurrency/sendable_without_preconcurrency.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,23 @@ func testA(ns: NS, mt: MyType, mt2: MyType2, sc: StrictClass, nsc: NonStrictClas
3333
}
3434

3535
extension NonStrictStruct: @unchecked Sendable { }
36+
37+
class StrictSubclass: StrictClass {
38+
override func send(_ body: () -> ()) {}
39+
override func dontSend(_ body: () -> ()) {}
40+
}
41+
42+
struct StrictConformer: StrictProtocol {
43+
func send(_ body: () -> Void) {}
44+
func dontSend(_ body: () -> Void) {}
45+
}
46+
47+
class NonStrictSubclass: NonStrictClass {
48+
override func send(_ body: () -> ()) {}
49+
override func dontSend(_ body: () -> ()) {}
50+
}
51+
52+
struct NonStrictConformer: NonStrictProtocol {
53+
func send(_ body: () -> Void) {}
54+
func dontSend(_ body: () -> Void) {}
55+
}

test/Concurrency/sendable_without_preconcurrency_2.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,23 @@ func testA(ns: NS, mt: MyType, mt2: MyType2, sc: StrictClass, nsc: NonStrictClas
3434
}
3535

3636
extension NonStrictStruct: @unchecked Sendable { }
37+
38+
class StrictSubclass: StrictClass {
39+
override func send(_ body: () -> ()) {}
40+
override func dontSend(_ body: @Sendable () -> ()) {} // expected-warning {{declaration 'dontSend' has a type with different sendability from any potential overrides}}
41+
}
42+
43+
struct StrictConformer: StrictProtocol {
44+
func send(_ body: () -> Void) {}
45+
func dontSend(_ body: @Sendable () -> Void) {} // expected-warning {{sendability of function types in instance method 'dontSend' does not match requirement in protocol 'StrictProtocol'}}
46+
}
47+
48+
class NonStrictSubclass: NonStrictClass {
49+
override func send(_ body: () -> ()) {}
50+
override func dontSend(_ body: @Sendable () -> ()) {} // expected-warning {{declaration 'dontSend' has a type with different sendability from any potential overrides}}
51+
}
52+
53+
struct NonStrictConformer: NonStrictProtocol {
54+
func send(_ body: () -> Void) {}
55+
func dontSend(_ body: @Sendable () -> Void) {} // expected-warning {{sendability of function types in instance method 'dontSend' does not match requirement in protocol 'NonStrictProtocol'}}
56+
}

test/attr/attr_concurrent.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,8 @@ class SuperSendable {
135135

136136
class SubSendable: SuperSendable {
137137
override func runsInBackground(_: () -> Void) {}
138-
override func runsInForeground(_: @Sendable () -> Void) {} // expected-warning {{declaration 'runsInForeground' has a type with different sendability from any potential overrides; this is an error in Swift 6}}
139-
override func runnableInBackground() -> () -> Void { fatalError() } // expected-warning {{declaration 'runnableInBackground()' has a type with different sendability from any potential overrides; this is an error in Swift 6}}
138+
override func runsInForeground(_: @Sendable () -> Void) {} // expected-warning {{declaration 'runsInForeground' has a type with different sendability from any potential overrides}}
139+
override func runnableInBackground() -> () -> Void { fatalError() } // expected-warning {{declaration 'runnableInBackground()' has a type with different sendability from any potential overrides}}
140140
override func runnableInForeground() -> @Sendable () -> Void { fatalError() }
141141
}
142142

@@ -149,7 +149,7 @@ protocol AbstractSendable {
149149

150150
struct ConcreteSendable: AbstractSendable {
151151
func runsInBackground(_: () -> Void) {}
152-
func runsInForeground(_: @Sendable () -> Void) {} // expected-warning {{sendability of function types in instance method 'runsInForeground' does not match requirement in protocol 'AbstractSendable'; this is an error in Swift 6}}
153-
func runnableInBackground() -> () -> Void { fatalError() } // expected-warning {{sendability of function types in instance method 'runnableInBackground()' does not match requirement in protocol 'AbstractSendable'; this is an error in Swift 6}}
152+
func runsInForeground(_: @Sendable () -> Void) {} // expected-warning {{sendability of function types in instance method 'runsInForeground' does not match requirement in protocol 'AbstractSendable'}}
153+
func runnableInBackground() -> () -> Void { fatalError() } // expected-warning {{sendability of function types in instance method 'runnableInBackground()' does not match requirement in protocol 'AbstractSendable'}}
154154
func runnableInForeground() -> @Sendable () -> Void { fatalError() }
155155
}

test/decl/protocol/conforms/associated_type.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ struct X1b : P1 {
6464
struct SendableX1b : P1 {
6565
typealias A = @Sendable (Int) -> Int
6666

67-
func f(_: (Int) -> Int) { } // expected-warning {{sendability of function types in instance method 'f' does not match requirement in protocol 'P1'; this is an error in Swift 6}}
67+
func f(_: (Int) -> Int) { } // expected-warning {{sendability of function types in instance method 'f' does not match requirement in protocol 'P1'}}
6868
}
6969

7070
struct X1c : P1 {
@@ -99,15 +99,15 @@ struct X2b : P2 { // expected-error{{type 'X2b' does not conform to protocol 'P2
9999
}
100100

101101
struct X2c : P2 {
102-
func f(_: @Sendable (Int) -> Int) { } // expected-warning{{sendability of function types in instance method 'f' does not match requirement in protocol 'P2'; this is an error in Swift 6}}
103-
func g(_: @Sendable (Int) -> Int) { } // expected-warning{{sendability of function types in instance method 'g' does not match requirement in protocol 'P2'; this is an error in Swift 6}}
102+
func f(_: @Sendable (Int) -> Int) { } // expected-warning{{sendability of function types in instance method 'f' does not match requirement in protocol 'P2'}}
103+
func g(_: @Sendable (Int) -> Int) { } // expected-warning{{sendability of function types in instance method 'g' does not match requirement in protocol 'P2'}}
104104
func h(_: @Sendable (Int) -> Int) { }
105105
func i(_: @Sendable (Int) -> Int) { }
106106
}
107107

108108
struct X2d : P2 { // expected-error{{type 'X2d' does not conform to protocol 'P2'}}
109109
func f(_: @escaping @Sendable (Int) -> Int) { } // expected-note{{candidate has non-matching type '(@escaping @Sendable (Int) -> Int) -> ()'}}
110-
func g(_: @escaping @Sendable (Int) -> Int) { } // expected-warning{{sendability of function types in instance method 'g' does not match requirement in protocol 'P2'; this is an error in Swift 6}}
110+
func g(_: @escaping @Sendable (Int) -> Int) { } // expected-warning{{sendability of function types in instance method 'g' does not match requirement in protocol 'P2'}}
111111
func h(_: @escaping @Sendable (Int) -> Int) { } // expected-note{{candidate has non-matching type '(@escaping @Sendable (Int) -> Int) -> ()'}}
112112
func i(_: @escaping @Sendable (Int) -> Int) { }
113113
}

0 commit comments

Comments
 (0)