Skip to content

Commit e4cd1d7

Browse files
authored
Merge pull request #75062 from xedin/rdar-122193606
[Concurrency] Allow sendability mismatches while overriding `@preconcurrency` properties in Swift 5 mode
2 parents 5d6f694 + cdf28bc commit e4cd1d7

File tree

5 files changed

+185
-23
lines changed

5 files changed

+185
-23
lines changed

include/swift/AST/Types.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,8 @@ enum class TypeMatchFlags {
347347
AllowCompatibleOpaqueTypeArchetypes = 1 << 5,
348348
/// Ignore the @Sendable attributes on functions when matching types.
349349
IgnoreFunctionSendability = 1 << 6,
350+
/// Ignore `any Sendable` and compositions with Sendable protocol.
351+
IgnoreSendability = 1 << 7,
350352
};
351353
using TypeMatchOptions = OptionSet<TypeMatchFlags>;
352354

lib/AST/Type.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3306,6 +3306,37 @@ static bool matches(CanType t1, CanType t2, TypeMatchOptions matchMode,
33063306
opaque1->getInterfaceType()->getCanonicalType()->matches(
33073307
opaque2->getInterfaceType()->getCanonicalType(), matchMode);
33083308

3309+
if (matchMode.contains(TypeMatchFlags::IgnoreSendability)) {
3310+
// Support `any Sendable` -> `Any` matching inside generic types
3311+
// e.g. collections and optionals (i.e. `[String: (any Sendable)?]`).
3312+
if (auto *generic1 = t1->getAs<BoundGenericType>()) {
3313+
if (auto *generic2 = t2->getAs<BoundGenericType>()) {
3314+
if (generic1->getDecl() == generic2->getDecl()) {
3315+
auto genericArgs1 = generic1->getGenericArgs();
3316+
auto genericArgs2 = generic2->getGenericArgs();
3317+
3318+
if (genericArgs1.size() == genericArgs2.size() &&
3319+
llvm::all_of(llvm::zip_equal(genericArgs1, genericArgs2),
3320+
[&](const auto &elt) -> bool {
3321+
return matches(
3322+
std::get<0>(elt)->getCanonicalType(),
3323+
std::get<1>(elt)->getCanonicalType(),
3324+
matchMode, ParameterPosition::NotParameter,
3325+
OptionalUnwrapping::None);
3326+
}))
3327+
return true;
3328+
}
3329+
}
3330+
}
3331+
3332+
// Attempting to match `any Sendable` by `Any` is allowed in this mode.
3333+
if (t1->isAny()) {
3334+
auto *PD = dyn_cast_or_null<ProtocolDecl>(t2->getAnyNominal());
3335+
if (PD && PD->isSpecificProtocol(KnownProtocolKind::Sendable))
3336+
return true;
3337+
}
3338+
}
3339+
33093340
return false;
33103341
}
33113342

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)