Skip to content

Commit 86d036d

Browse files
authored
Merge pull request #81960 from xedin/rdar-146027395
[Concurrency] Downgrade errors to warnings when `Sendable` requirement is inferred from a preconcurrency protocol
2 parents 89628ab + 3495c61 commit 86d036d

9 files changed

+62
-69
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5923,8 +5923,8 @@ ERROR(non_sendable_keypath_capture,none,
59235923
(Type))
59245924
ERROR(non_concurrent_type_member,none,
59255925
"%select{stored property %2|associated value %2}1 of "
5926-
"'Sendable'-conforming %kind3 has non-Sendable type %0",
5927-
(Type, bool, DeclName, const ValueDecl *))
5926+
"'Sendable'-conforming %kind3 %select{contains|has}4 non-Sendable type %0",
5927+
(Type, bool, DeclName, const ValueDecl *, bool))
59285928
ERROR(concurrent_value_class_mutable_property,none,
59295929
"stored property %0 of 'Sendable'-conforming %kind1 is mutable",
59305930
(DeclName, const ValueDecl *))

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 35 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -797,7 +797,7 @@ bool SendableCheckContext::warnInMinimalChecking() const {
797797
case SendableCheck::Explicit:
798798
return true;
799799

800-
case SendableCheck::ImpliedByStandardProtocol:
800+
case SendableCheck::ImpliedByPreconcurrencyProtocol:
801801
case SendableCheck::Implicit:
802802
case SendableCheck::ImplicitForExternallyVisible:
803803
return false;
@@ -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;
@@ -6763,57 +6768,22 @@ static bool checkSendableInstanceStorage(
67636768
}
67646769
}
67656770

6766-
// Check that the property type is Sendable.
6767-
SendableCheckContext context(dc, check);
6768-
diagnoseNonSendableTypes(
6769-
propertyType, context,
6770-
/*inDerivedConformance*/Type(), property->getLoc(),
6771-
[&](Type type, DiagnosticBehavior behavior) {
6772-
auto preconcurrency = context.preconcurrencyBehavior(type);
6773-
if (isImplicitSendableCheck(check)) {
6774-
// If this is for an externally-visible conformance, fail.
6775-
if (check == SendableCheck::ImplicitForExternallyVisible) {
6776-
invalid = true;
6777-
return true;
6778-
}
6779-
6780-
// If we are to ignore this diagnostic, just continue.
6781-
if (behavior == DiagnosticBehavior::Ignore ||
6782-
preconcurrency == DiagnosticBehavior::Ignore)
6783-
return true;
6784-
6785-
invalid = true;
6786-
return true;
6787-
}
6788-
6789-
if (preconcurrency)
6790-
behavior = preconcurrency.value();
6791-
6792-
property
6793-
->diagnose(diag::non_concurrent_type_member, propertyType,
6794-
false, property->getName(), nominal)
6795-
.limitBehaviorWithPreconcurrency(behavior,
6796-
preconcurrency.has_value());
6797-
return false;
6798-
});
6799-
6800-
if (invalid) {
6801-
// For implicit checks, bail out early if anything failed.
6802-
if (isImplicitSendableCheck(check))
6803-
return true;
6804-
}
6805-
6806-
return false;
6771+
return checkSendabilityOfMemberType(property, propertyType);
68076772
}
68086773

68096774
/// Handle an enum associated value.
68106775
bool operator()(EnumElementDecl *element, Type elementType) override {
6811-
SendableCheckContext context (dc, check);
6776+
return checkSendabilityOfMemberType(element, elementType);
6777+
}
6778+
6779+
private:
6780+
bool checkSendabilityOfMemberType(ValueDecl *member, Type memberType) {
6781+
SendableCheckContext context(dc, check);
68126782
diagnoseNonSendableTypes(
6813-
elementType, context,
6814-
/*inDerivedConformance*/Type(), element->getLoc(),
6783+
memberType, context,
6784+
/*inDerivedConformance*/ Type(), member->getLoc(),
68156785
[&](Type type, DiagnosticBehavior behavior) {
6816-
auto preconcurrency = context.preconcurrencyBehavior(type);
6786+
auto preconcurrencyBehavior = context.preconcurrencyBehavior(type);
68176787
if (isImplicitSendableCheck(check)) {
68186788
// If this is for an externally-visible conformance, fail.
68196789
if (check == SendableCheck::ImplicitForExternallyVisible) {
@@ -6823,21 +6793,29 @@ static bool checkSendableInstanceStorage(
68236793

68246794
// If we are to ignore this diagnostic, just continue.
68256795
if (behavior == DiagnosticBehavior::Ignore ||
6826-
preconcurrency == DiagnosticBehavior::Ignore)
6796+
preconcurrencyBehavior == DiagnosticBehavior::Ignore)
68276797
return true;
68286798

68296799
invalid = true;
68306800
return true;
68316801
}
68326802

6833-
if (preconcurrency)
6834-
behavior = preconcurrency.value();
6835-
6836-
element
6837-
->diagnose(diag::non_concurrent_type_member, type, true,
6838-
element->getName(), nominal)
6839-
.limitBehaviorWithPreconcurrency(behavior,
6840-
preconcurrency.has_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();
6811+
6812+
member
6813+
->diagnose(diag::non_concurrent_type_member, type,
6814+
isa<EnumElementDecl>(member), member->getName(),
6815+
nominal, type->isEqual(memberType))
6816+
.limitBehaviorWithPreconcurrency(
6817+
behavior, fromPreconcurrencyConformance ||
6818+
preconcurrencyBehavior.has_value());
68416819
return false;
68426820
});
68436821

@@ -6849,6 +6827,7 @@ static bool checkSendableInstanceStorage(
68496827

68506828
return false;
68516829
}
6830+
68526831
} visitor(nominal, dc, check);
68536832

68546833
return visitor.visit(nominal, dc) || visitor.invalid;

lib/Sema/TypeCheckConcurrency.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,7 @@ enum class SendableCheck {
353353

354354
/// Sendable conformance was implied by a protocol that inherits from
355355
/// Sendable and also predates concurrency.
356-
ImpliedByStandardProtocol,
356+
ImpliedByPreconcurrencyProtocol,
357357

358358
/// Implicit conformance to Sendable.
359359
Implicit,
@@ -367,7 +367,7 @@ enum class SendableCheck {
367367
static inline bool isImplicitSendableCheck(SendableCheck check) {
368368
switch (check) {
369369
case SendableCheck::Explicit:
370-
case SendableCheck::ImpliedByStandardProtocol:
370+
case SendableCheck::ImpliedByPreconcurrencyProtocol:
371371
return false;
372372

373373
case SendableCheck::Implicit:

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6688,7 +6688,7 @@ void TypeChecker::checkConformancesInContext(IterableDeclContext *idc) {
66886688
if (!hasDeprecatedUnsafeSendable && SendableConformance) {
66896689
SendableCheck check = SendableCheck::Explicit;
66906690
if (sendableConformancePreconcurrency)
6691-
check = SendableCheck::ImpliedByStandardProtocol;
6691+
check = SendableCheck::ImpliedByPreconcurrencyProtocol;
66926692
else if (SendableConformance->getSourceKind() ==
66936693
ConformanceEntryKind::Synthesized)
66946694
check = SendableCheck::Implicit;

test/Concurrency/concurrent_value_checking.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ enum E2<T> {
337337
extension E2: Sendable where T: Sendable { }
338338

339339
final class C1: Sendable {
340-
let nc: NotConcurrent? = nil // expected-warning{{stored property 'nc' of 'Sendable'-conforming class 'C1' has non-Sendable type 'NotConcurrent?'}}
340+
let nc: NotConcurrent? = nil // expected-warning{{stored property 'nc' of 'Sendable'-conforming class 'C1' contains non-Sendable type 'NotConcurrent'}}
341341
var x: Int = 0 // expected-warning{{stored property 'x' of 'Sendable'-conforming class 'C1' is mutable}}
342342
let i: Int = 0
343343
}

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/Concurrency/sendable_metatype_typecheck.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,11 +119,11 @@ class Holder: @unchecked Sendable {
119119
}
120120

121121
enum E: Sendable {
122-
case q(Q.Type, Int) // expected-warning{{associated value 'q' of 'Sendable'-conforming enum 'E' has non-Sendable type 'any Q.Type'}}
122+
case q(Q.Type, Int) // expected-warning{{associated value 'q' of 'Sendable'-conforming enum 'E' contains non-Sendable type 'any Q.Type'}}
123123
}
124124

125125
struct S: Sendable {
126-
var tuple: ([Q.Type], Int) // expected-warning{{stored property 'tuple' of 'Sendable'-conforming struct 'S' has non-Sendable type '([any Q.Type], Int)'}}
126+
var tuple: ([Q.Type], Int) // expected-warning{{stored property 'tuple' of 'Sendable'-conforming struct 'S' contains non-Sendable type 'any Q.Type'}}
127127
}
128128

129129
extension Q {

test/Concurrency/weak_ref_sendability.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ final class CheckSendability3: Sendable {
4949
}
5050

5151
final class CheckSendability4: Sendable {
52-
// expected-error@+1 {{stored property 'x' of 'Sendable'-conforming class 'CheckSendability4' has non-Sendable type 'NS?'}}
52+
// expected-error@+1 {{stored property 'x' of 'Sendable'-conforming class 'CheckSendability4' contains non-Sendable type 'NS'}}
5353
weak let x: NS? = nil
5454
}
5555

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)