Skip to content

Commit 6d45229

Browse files
committed
[Concurrency] Downgrade errors to warnings when Sendable requirement is inferred from a preconcurrency protocol
If a type gets `Sendable` conformace requirement through another `@preconcurrency` protocol the error should be downgraded even with strict concurrency checking to allow clients time to address the new requirement. Resolves: rdar://146027395
1 parent 7cca722 commit 6d45229

File tree

3 files changed

+37
-11
lines changed

3 files changed

+37
-11
lines changed

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6748,11 +6748,16 @@ static bool checkSendableInstanceStorage(
67486748
if (property->supportsMutation() && isolation.isUnspecified()) {
67496749
auto behavior =
67506750
SendableCheckContext(dc, check).defaultDiagnosticBehavior();
6751+
// If Sendable came from a `@preconcurrency` protocol the error
6752+
// should be downgraded even with strict concurrency checking to
6753+
// allow clients time to address the new requirement.
6754+
auto preconcurrency =
6755+
check == SendableCheck::ImpliedByPreconcurrencyProtocol;
67516756
if (behavior != DiagnosticBehavior::Ignore) {
67526757
property
67536758
->diagnose(diag::concurrent_value_class_mutable_property,
67546759
property->getName(), nominal)
6755-
.limitBehaviorUntilSwiftVersion(behavior, 6);
6760+
.limitBehaviorWithPreconcurrency(behavior, preconcurrency);
67566761
}
67576762
invalid = invalid || (behavior == DiagnosticBehavior::Unspecified);
67586763
return true;
@@ -6778,7 +6783,7 @@ static bool checkSendableInstanceStorage(
67786783
memberType, context,
67796784
/*inDerivedConformance*/ Type(), member->getLoc(),
67806785
[&](Type type, DiagnosticBehavior behavior) {
6781-
auto preconcurrency = context.preconcurrencyBehavior(type);
6786+
auto preconcurrencyBehavior = context.preconcurrencyBehavior(type);
67826787
if (isImplicitSendableCheck(check)) {
67836788
// If this is for an externally-visible conformance, fail.
67846789
if (check == SendableCheck::ImplicitForExternallyVisible) {
@@ -6788,22 +6793,29 @@ static bool checkSendableInstanceStorage(
67886793

67896794
// If we are to ignore this diagnostic, just continue.
67906795
if (behavior == DiagnosticBehavior::Ignore ||
6791-
preconcurrency == DiagnosticBehavior::Ignore)
6796+
preconcurrencyBehavior == DiagnosticBehavior::Ignore)
67926797
return true;
67936798

67946799
invalid = true;
67956800
return true;
67966801
}
67976802

6798-
if (preconcurrency)
6799-
behavior = preconcurrency.value();
6803+
// If Sendable came from a `@preconcurrency` protocol the error
6804+
// should be downgraded even with strict concurrency checking to
6805+
// allow clients time to address the new requirement.
6806+
bool fromPreconcurrencyConformance =
6807+
check == SendableCheck::ImpliedByPreconcurrencyProtocol;
6808+
6809+
if (preconcurrencyBehavior)
6810+
behavior = preconcurrencyBehavior.value();
68006811

68016812
member
68026813
->diagnose(diag::non_concurrent_type_member, type,
68036814
isa<EnumElementDecl>(member), member->getName(),
68046815
nominal)
6805-
.limitBehaviorWithPreconcurrency(behavior,
6806-
preconcurrency.has_value());
6816+
.limitBehaviorWithPreconcurrency(
6817+
behavior, fromPreconcurrencyConformance ||
6818+
preconcurrencyBehavior.has_value());
68076819
return false;
68086820
});
68096821

test/Concurrency/predates_concurrency_swift6.swift

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,14 +88,14 @@ func testCallsWithAsync() async {
8888
@preconcurrency protocol P: Sendable { }
8989
protocol Q: P { }
9090

91-
class NS { } // expected-note 3{{class 'NS' does not conform to the 'Sendable' protocol}}
91+
class NS { } // expected-note 5{{class 'NS' does not conform to the 'Sendable' protocol}}
9292

9393
struct S1: P {
94-
var ns: NS // expected-error{{stored property 'ns' of 'Sendable'-conforming struct 'S1' has non-Sendable type 'NS'}}
94+
var ns: NS // expected-warning{{stored property 'ns' of 'Sendable'-conforming struct 'S1' has non-Sendable type 'NS'}}
9595
}
9696

9797
struct S2: Q {
98-
var ns: NS // expected-error{{stored property 'ns' of 'Sendable'-conforming struct 'S2' has non-Sendable type 'NS'}}
98+
var ns: NS // expected-warning{{stored property 'ns' of 'Sendable'-conforming struct 'S2' has non-Sendable type 'NS'}}
9999
}
100100

101101
struct S3: Q, Sendable {
@@ -309,3 +309,17 @@ do {
309309
// If destination is @preconcurrency the Sendable conformance error should be downgraded
310310
d = data // expected-warning {{type 'Any' does not conform to the 'Sendable' protocol}}
311311
}
312+
313+
do {
314+
final class Mutating: P {
315+
var state: Int = 0 // expected-warning {{stored property 'state' of 'Sendable'-conforming class 'Mutating' is mutable}}
316+
}
317+
318+
struct StructWithInit: P {
319+
let prop = NS() // expected-warning {{stored property 'prop' of 'Sendable'-conforming struct 'StructWithInit' has non-Sendable type 'NS'}}
320+
}
321+
322+
enum E : P {
323+
case test(NS) // expected-warning {{associated value 'test' of 'Sendable'-conforming enum 'E' has non-Sendable type 'NS'}}
324+
}
325+
}

test/type/explicit_existential.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ protocol HasAssoc {
9090

9191
do {
9292
enum MyError: Error {
93-
case bad(Any) // expected-swift-6-error {{associated value 'bad' of 'Sendable'-conforming enum 'MyError' has non-Sendable type 'Any'}}
93+
case bad(Any) // expected-swift-6-warning {{associated value 'bad' of 'Sendable'-conforming enum 'MyError' has non-Sendable type 'Any'}}
9494
}
9595

9696
func checkIt(_ js: Any) throws {

0 commit comments

Comments
 (0)