Skip to content

Commit 83de87a

Browse files
authored
Merge pull request #75178 from xedin/rdar-122193606-6.0
[6.0][Concurrency] Allow sendability mismatches while overriding `@preconcurrency` properties in Swift 5 mode
2 parents 8dfd8e8 + 88f2a51 commit 83de87a

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
@@ -3309,6 +3309,37 @@ static bool matches(CanType t1, CanType t2, TypeMatchOptions matchMode,
33093309
opaque1->getInterfaceType()->getCanonicalType()->matches(
33103310
opaque2->getInterfaceType()->getCanonicalType(), matchMode);
33113311

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

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)