Skip to content

Commit ace1e52

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 (cherry picked from commit 6d45229)
1 parent a947450 commit ace1e52

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
@@ -6768,11 +6768,16 @@ static bool checkSendableInstanceStorage(
67686768
if (property->supportsMutation() && isolation.isUnspecified()) {
67696769
auto behavior =
67706770
SendableCheckContext(dc, check).defaultDiagnosticBehavior();
6771+
// If Sendable came from a `@preconcurrency` protocol the error
6772+
// should be downgraded even with strict concurrency checking to
6773+
// allow clients time to address the new requirement.
6774+
auto preconcurrency =
6775+
check == SendableCheck::ImpliedByPreconcurrencyProtocol;
67716776
if (behavior != DiagnosticBehavior::Ignore) {
67726777
property
67736778
->diagnose(diag::concurrent_value_class_mutable_property,
67746779
property->getName(), nominal)
6775-
.limitBehaviorUntilSwiftVersion(behavior, 6);
6780+
.limitBehaviorWithPreconcurrency(behavior, preconcurrency);
67766781
}
67776782
invalid = invalid || (behavior == DiagnosticBehavior::Unspecified);
67786783
return true;
@@ -6798,7 +6803,7 @@ static bool checkSendableInstanceStorage(
67986803
memberType, context,
67996804
/*inDerivedConformance*/ Type(), member->getLoc(),
68006805
[&](Type type, DiagnosticBehavior behavior) {
6801-
auto preconcurrency = context.preconcurrencyBehavior(type);
6806+
auto preconcurrencyBehavior = context.preconcurrencyBehavior(type);
68026807
if (isImplicitSendableCheck(check)) {
68036808
// If this is for an externally-visible conformance, fail.
68046809
if (check == SendableCheck::ImplicitForExternallyVisible) {
@@ -6808,22 +6813,29 @@ static bool checkSendableInstanceStorage(
68086813

68096814
// If we are to ignore this diagnostic, just continue.
68106815
if (behavior == DiagnosticBehavior::Ignore ||
6811-
preconcurrency == DiagnosticBehavior::Ignore)
6816+
preconcurrencyBehavior == DiagnosticBehavior::Ignore)
68126817
return true;
68136818

68146819
invalid = true;
68156820
return true;
68166821
}
68176822

6818-
if (preconcurrency)
6819-
behavior = preconcurrency.value();
6823+
// If Sendable came from a `@preconcurrency` protocol the error
6824+
// should be downgraded even with strict concurrency checking to
6825+
// allow clients time to address the new requirement.
6826+
bool fromPreconcurrencyConformance =
6827+
check == SendableCheck::ImpliedByPreconcurrencyProtocol;
6828+
6829+
if (preconcurrencyBehavior)
6830+
behavior = preconcurrencyBehavior.value();
68206831

68216832
member
68226833
->diagnose(diag::non_concurrent_type_member, type,
68236834
isa<EnumElementDecl>(member), member->getName(),
68246835
nominal)
6825-
.limitBehaviorWithPreconcurrency(behavior,
6826-
preconcurrency.has_value());
6836+
.limitBehaviorWithPreconcurrency(
6837+
behavior, fromPreconcurrencyConformance ||
6838+
preconcurrencyBehavior.has_value());
68276839
return false;
68286840
});
68296841

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)