Skip to content

Commit bbec066

Browse files
committed
Warn about incorrect Sendable for overrides
1 parent 784d760 commit bbec066

File tree

5 files changed

+35
-7
lines changed

5 files changed

+35
-7
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2596,6 +2596,9 @@ WARNING(generic_param_usable_from_inline_warn,none,
25962596
ERROR(override_multiple_decls_base,none,
25972597
"declaration %0 cannot override more than one superclass declaration",
25982598
(DeclName))
2599+
ERROR(override_sendability_mismatch,none,
2600+
"declaration %0 has a type with different sendability from any potential "
2601+
"overrides", (DeclName))
25992602
ERROR(override_multiple_decls_arg_mismatch,none,
26002603
"declaration %0 has different argument labels from any potential "
26012604
"overrides", (DeclName))

include/swift/AST/Types.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,9 @@ enum class TypeMatchFlags {
295295
/// to be non-escaping, but Swift currently does not.
296296
IgnoreNonEscapingForOptionalFunctionParam = 1 << 4,
297297
/// Allow compatible opaque archetypes.
298-
AllowCompatibleOpaqueTypeArchetypes = 1 << 5
298+
AllowCompatibleOpaqueTypeArchetypes = 1 << 5,
299+
/// Ignore the @Sendable attributes on functions when matching types.
300+
IgnoreFunctionSendability = 1 << 6,
299301
};
300302
using TypeMatchOptions = OptionSet<TypeMatchFlags>;
301303

lib/AST/Type.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3174,6 +3174,11 @@ static bool matchesFunctionType(CanAnyFunctionType fn1, CanAnyFunctionType fn2,
31743174
ext1 = ext1.withConcurrent(false);
31753175
}
31763176

3177+
if (matchMode.contains(TypeMatchFlags::IgnoreFunctionSendability)) {
3178+
ext1 = ext1.withConcurrent(false);
3179+
ext2 = ext2.withConcurrent(false);
3180+
}
3181+
31773182
// If specified, allow an escaping function parameter to override a
31783183
// non-escaping function parameter when the parameter is optional.
31793184
// Note that this is checking 'ext2' rather than 'ext1' because parameters

lib/Sema/TypeCheckDeclOverride.cpp

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,7 @@ static bool noteFixableMismatchedTypes(ValueDecl *decl, const ValueDecl *base) {
518518
namespace {
519519
enum class OverrideCheckingAttempt {
520520
PerfectMatch,
521+
MismatchedSendability,
521522
MismatchedOptional,
522523
MismatchedTypes,
523524
BaseName,
@@ -547,6 +548,12 @@ static void diagnoseGeneralOverrideFailure(ValueDecl *decl,
547548
diags.diagnose(decl, diag::override_multiple_decls_base,
548549
decl->getName());
549550
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);
556+
break;
550557
case OverrideCheckingAttempt::BaseName:
551558
diags.diagnose(decl, diag::override_multiple_decls_arg_mismatch,
552559
decl->getName());
@@ -886,6 +893,7 @@ SmallVector<OverrideMatch, 2> OverrideMatcher::match(
886893
DeclName name;
887894
switch (attempt) {
888895
case OverrideCheckingAttempt::PerfectMatch:
896+
case OverrideCheckingAttempt::MismatchedSendability:
889897
case OverrideCheckingAttempt::MismatchedOptional:
890898
case OverrideCheckingAttempt::MismatchedTypes:
891899
name = decl->getName();
@@ -976,6 +984,8 @@ SmallVector<OverrideMatch, 2> OverrideMatcher::match(
976984
matchMode |= TypeMatchFlags::AllowNonOptionalForIUOParam;
977985
matchMode |= TypeMatchFlags::IgnoreNonEscapingForOptionalFunctionParam;
978986
}
987+
if (attempt == OverrideCheckingAttempt::MismatchedSendability)
988+
matchMode |= TypeMatchFlags::IgnoreFunctionSendability;
979989

980990
auto declFnTy = getDeclComparisonType()->getAs<AnyFunctionType>();
981991
auto parentDeclTy = getMemberTypeForComparison(parentDecl, decl);
@@ -1277,8 +1287,15 @@ bool OverrideMatcher::checkOverride(ValueDecl *baseDecl,
12771287
if (emittedMatchError)
12781288
return true;
12791289

1290+
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);
1296+
}
12801297
// Catch-all to make sure we don't silently accept something we shouldn't.
1281-
if (attempt != OverrideCheckingAttempt::PerfectMatch) {
1298+
else if (attempt != OverrideCheckingAttempt::PerfectMatch) {
12821299
OverrideMatch match{decl, /*isExact=*/false};
12831300
diagnoseGeneralOverrideFailure(decl, match, attempt);
12841301
}
@@ -1374,11 +1391,12 @@ bool swift::checkOverrides(ValueDecl *decl) {
13741391
switch (attempt) {
13751392
case OverrideCheckingAttempt::PerfectMatch:
13761393
break;
1377-
case OverrideCheckingAttempt::MismatchedOptional:
1394+
case OverrideCheckingAttempt::MismatchedSendability:
13781395
// Don't keep looking if the user didn't indicate it's an override.
13791396
if (!decl->getAttrs().hasAttribute<OverrideAttr>())
13801397
return false;
13811398
break;
1399+
case OverrideCheckingAttempt::MismatchedOptional:
13821400
case OverrideCheckingAttempt::MismatchedTypes:
13831401
break;
13841402
case OverrideCheckingAttempt::BaseName:

test/attr/attr_concurrent.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -128,15 +128,15 @@ func testExplicitConcurrentClosure() {
128128

129129
class SuperSendable {
130130
func runsInBackground(_: @Sendable () -> Void) {}
131-
func runsInForeground(_: () -> Void) {} // expected-note {{potential overridden instance method 'runsInForeground' here}}
132-
func runnableInBackground() -> @Sendable () -> Void { fatalError() } // expected-note {{potential overridden instance method 'runnableInBackground()' here}}
131+
func runsInForeground(_: () -> Void) {} // expected-note {{overridden declaration is here}}
132+
func runnableInBackground() -> @Sendable () -> Void { fatalError() } // expected-note {{overridden declaration is here}}
133133
func runnableInForeground() -> () -> Void { fatalError() }
134134
}
135135

136136
class SubSendable: SuperSendable {
137137
override func runsInBackground(_: () -> Void) {}
138-
override func runsInForeground(_: @Sendable () -> Void) {} // expected-error {{method does not override any method from its superclass}}
139-
override func runnableInBackground() -> () -> Void { fatalError() } // expected-error {{method does not override any method from its superclass}}
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}}
140140
override func runnableInForeground() -> @Sendable () -> Void { fatalError() }
141141
}
142142

0 commit comments

Comments
 (0)