Skip to content

Commit cdf28bc

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
1 parent 8a281ef commit cdf28bc

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

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

12761312
// Differing only in Optional vs. ImplicitlyUnwrappedOptional is fine.
1277-
bool IsSilentDifference = false;
1313+
bool optionalVsIUO = false;
12781314
if (auto propertyTyNoOptional = propertyTy->getOptionalObjectType())
12791315
if (auto parentPropertyTyNoOptional =
12801316
parentPropertyTy->getOptionalObjectType())
12811317
if (propertyTyNoOptional->isEqual(parentPropertyTyNoOptional))
1282-
IsSilentDifference = true;
1318+
optionalVsIUO = true;
12831319

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

12941335
if (emittedMatchError)
12951336
return true;
12961337

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