Skip to content

Commit 88f2a51

Browse files
committed
[Concurrency] Allow sendability mismatches while overriding @preconcurrency properties in Swift 5 mode
Downgrade a mismatch on `any Sendable` -> `Any` to a warning until Swift 6 to enable class authors to introduce concurrency annotations to overridable properties. Resolves: rdar://122193606 (cherry picked from commit cdf28bc)
1 parent 2a6135a commit 88f2a51

File tree

3 files changed

+152
-23
lines changed

3 files changed

+152
-23
lines changed

lib/Sema/TypeCheckDeclOverride.cpp

Lines changed: 52 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1203,6 +1203,30 @@ bool OverrideMatcher::checkOverride(ValueDecl *baseDecl,
12031203
Type declTy = getDeclComparisonType();
12041204
Type owningTy = dc->getDeclaredInterfaceType();
12051205
auto isClassContext = classDecl != nullptr;
1206+
bool allowsSendabilityMismatches =
1207+
attempt == OverrideCheckingAttempt::MismatchedSendability ||
1208+
(attempt == OverrideCheckingAttempt::PerfectMatch &&
1209+
baseDecl->preconcurrency());
1210+
bool mismatchedOnSendability = false;
1211+
1212+
auto diagnoseSendabilityMismatch = [&]() {
1213+
SendableCheckContext fromContext(decl->getDeclContext(),
1214+
SendableCheck::Explicit);
1215+
auto baseDeclClass = baseDecl->getDeclContext()->getSelfClassDecl();
1216+
1217+
diagnoseSendabilityErrorBasedOn(
1218+
baseDeclClass, fromContext, [&](DiagnosticBehavior limit) {
1219+
diags
1220+
.diagnose(decl, diag::override_sendability_mismatch,
1221+
decl->getName())
1222+
.limitBehaviorUntilSwiftVersion(limit, 6)
1223+
.limitBehaviorIf(
1224+
fromContext.preconcurrencyBehavior(baseDeclClass));
1225+
diags.diagnose(baseDecl, diag::overridden_here);
1226+
return false;
1227+
});
1228+
};
1229+
12061230
if (declIUOAttr == matchDeclIUOAttr && declTy->isEqual(baseTy)) {
12071231
// Nothing to do.
12081232

@@ -1263,50 +1287,55 @@ bool OverrideMatcher::checkOverride(ValueDecl *baseDecl,
12631287
CanType parentPropertyCanTy =
12641288
parentPropertyTy->getReducedType(
12651289
decl->getInnermostDeclContext()->getGenericSignatureOfContext());
1266-
if (!propertyTy->matches(parentPropertyCanTy,
1267-
TypeMatchFlags::AllowOverride)) {
1268-
diags.diagnose(property, diag::override_property_type_mismatch,
1269-
property->getName(), propertyTy, parentPropertyTy);
1270-
noteFixableMismatchedTypes(decl, baseDecl);
1271-
diags.diagnose(baseDecl, diag::property_override_here);
1272-
return true;
1290+
1291+
TypeMatchOptions options;
1292+
options |= TypeMatchFlags::AllowOverride;
1293+
if (!propertyTy->matches(parentPropertyCanTy, options)) {
1294+
if (allowsSendabilityMismatches) {
1295+
options |= TypeMatchFlags::IgnoreSendability;
1296+
options |= TypeMatchFlags::IgnoreFunctionSendability;
1297+
1298+
mismatchedOnSendability =
1299+
propertyTy->matches(parentPropertyCanTy, options);
1300+
}
1301+
1302+
if (!mismatchedOnSendability) {
1303+
diags.diagnose(property, diag::override_property_type_mismatch,
1304+
property->getName(), propertyTy, parentPropertyTy);
1305+
noteFixableMismatchedTypes(decl, baseDecl);
1306+
diags.diagnose(baseDecl, diag::property_override_here);
1307+
return true;
1308+
}
12731309
}
12741310

12751311
// Differing only in Optional vs. ImplicitlyUnwrappedOptional is fine.
1276-
bool IsSilentDifference = false;
1312+
bool optionalVsIUO = false;
12771313
if (auto propertyTyNoOptional = propertyTy->getOptionalObjectType())
12781314
if (auto parentPropertyTyNoOptional =
12791315
parentPropertyTy->getOptionalObjectType())
12801316
if (propertyTyNoOptional->isEqual(parentPropertyTyNoOptional))
1281-
IsSilentDifference = true;
1317+
optionalVsIUO = true;
12821318

12831319
// The overridden property must not be mutable.
12841320
if (cast<AbstractStorageDecl>(baseDecl)->supportsMutation() &&
1285-
!IsSilentDifference) {
1321+
!(optionalVsIUO || mismatchedOnSendability)) {
12861322
diags.diagnose(property, diag::override_mutable_covariant_property,
12871323
property->getName(), parentPropertyTy, propertyTy);
12881324
diags.diagnose(baseDecl, diag::property_override_here);
12891325
return true;
12901326
}
1327+
1328+
if (mismatchedOnSendability && !emittedMatchError) {
1329+
diagnoseSendabilityMismatch();
1330+
return checkSingleOverride(decl, baseDecl);
1331+
}
12911332
}
12921333

12931334
if (emittedMatchError)
12941335
return true;
12951336

12961337
if (attempt == OverrideCheckingAttempt::MismatchedSendability) {
1297-
SendableCheckContext fromContext(decl->getDeclContext(),
1298-
SendableCheck::Explicit);
1299-
auto baseDeclClass = baseDecl->getDeclContext()->getSelfClassDecl();
1300-
1301-
diagnoseSendabilityErrorBasedOn(baseDeclClass, fromContext,
1302-
[&](DiagnosticBehavior limit) {
1303-
diags.diagnose(decl, diag::override_sendability_mismatch,
1304-
decl->getName())
1305-
.limitBehaviorUntilSwiftVersion(limit, 6)
1306-
.limitBehaviorIf(fromContext.preconcurrencyBehavior(baseDeclClass));
1307-
diags.diagnose(baseDecl, diag::overridden_here);
1308-
return false;
1309-
});
1338+
diagnoseSendabilityMismatch();
13101339
}
13111340
// Catch-all to make sure we don't silently accept something we shouldn't.
13121341
else if (attempt != OverrideCheckingAttempt::PerfectMatch) {

test/Concurrency/predates_concurrency.swift

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,3 +252,53 @@ extension MainActorPreconcurrency: NotIsolated {
252252
}
253253
}
254254
}
255+
256+
// Override matching with @preconcurrency properties.
257+
do {
258+
class Base {
259+
@preconcurrency
260+
open var test1 : ([any Sendable])? // expected-note {{overridden declaration is here}}
261+
262+
@preconcurrency
263+
open var test2: [String: [Int: any Sendable]] // expected-note {{overridden declaration is here}}
264+
265+
@preconcurrency
266+
open var test3: any Sendable // expected-note {{overridden declaration is here}}
267+
268+
@preconcurrency
269+
open var test4: (((Any)?) -> Void)? { // expected-note {{overridden declaration is here}}
270+
nil
271+
}
272+
273+
init() {
274+
self.test1 = nil
275+
self.test2 = [:]
276+
self.test3 = 42
277+
}
278+
}
279+
280+
class Test : Base {
281+
override var test1: [Any]? {
282+
// expected-warning@-1 {{declaration 'test1' has a type with different sendability from any potential overrides; this is an error in the Swift 6 language mode}}
283+
get { nil }
284+
set { }
285+
}
286+
287+
override var test2: [String: [Int: Any]] {
288+
// expected-warning@-1 {{declaration 'test2' has a type with different sendability from any potential overrides; this is an error in the Swift 6 language mode}}
289+
get { [:] }
290+
set {}
291+
}
292+
293+
override var test3: Any {
294+
// expected-warning@-1 {{declaration 'test3' has a type with different sendability from any potential overrides; this is an error in the Swift 6 language mode}}
295+
get { 42 }
296+
set { }
297+
}
298+
299+
override var test4: (((any Sendable)?) -> Void)? {
300+
// expected-warning@-1 {{declaration 'test4' has a type with different sendability from any potential overrides; this is an error in the Swift 6 language mode}}
301+
nil
302+
}
303+
}
304+
}

test/Concurrency/predates_concurrency_swift6.swift

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,3 +112,53 @@ func aFailedExperiment(@_unsafeSendable _ body: @escaping () -> Void) { }
112112

113113
func anothingFailedExperiment(@_unsafeMainActor _ body: @escaping () -> Void) { }
114114
// expected-warning@-1{{'_unsafeMainActor' attribute has been removed in favor of @preconcurrency}}
115+
116+
// Override matching with @preconcurrency properties.
117+
do {
118+
class Base {
119+
@preconcurrency
120+
open var test1 : ([any Sendable])? // expected-note {{overridden declaration is here}}
121+
122+
@preconcurrency
123+
open var test2: [String: [Int: any Sendable]] // expected-note {{overridden declaration is here}}
124+
125+
@preconcurrency
126+
open var test3: any Sendable // expected-note {{overridden declaration is here}}
127+
128+
@preconcurrency
129+
open var test4: (((Any)?) -> Void)? { // expected-note {{overridden declaration is here}}
130+
nil
131+
}
132+
133+
init() {
134+
self.test1 = nil
135+
self.test2 = [:]
136+
self.test3 = 42
137+
}
138+
}
139+
140+
class Test : Base {
141+
override var test1: [Any]? {
142+
// expected-error@-1 {{declaration 'test1' has a type with different sendability from any potential overrides}}
143+
get { nil }
144+
set { }
145+
}
146+
147+
override var test2: [String: [Int: Any]] {
148+
// expected-error@-1 {{declaration 'test2' has a type with different sendability from any potential overrides}}
149+
get { [:] }
150+
set {}
151+
}
152+
153+
override var test3: Any {
154+
// expected-error@-1 {{declaration 'test3' has a type with different sendability from any potential overrides}}
155+
get { 42 }
156+
set { }
157+
}
158+
159+
override var test4: (((any Sendable)?) -> Void)? {
160+
// expected-error@-1 {{declaration 'test4' has a type with different sendability from any potential overrides}}
161+
nil
162+
}
163+
}
164+
}

0 commit comments

Comments
 (0)