Skip to content

Commit 2a3e2d0

Browse files
authored
Merge pull request #82037 from xedin/rdar-146027395-6.2
[6.2][Concurrency] Downgrade errors to warnings when Sendable requirement is inferred from a preconcurrency protocol
2 parents fc67649 + fe2c8ef commit 2a3e2d0

8 files changed

+61
-68
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5875,8 +5875,8 @@ ERROR(non_sendable_keypath_capture,none,
58755875
(Type))
58765876
ERROR(non_concurrent_type_member,none,
58775877
"%select{stored property %2|associated value %2}1 of "
5878-
"'Sendable'-conforming %kind3 has non-Sendable type %0",
5879-
(Type, bool, DeclName, const ValueDecl *))
5878+
"'Sendable'-conforming %kind3 %select{contains|has}4 non-Sendable type %0",
5879+
(Type, bool, DeclName, const ValueDecl *, bool))
58805880
ERROR(concurrent_value_class_mutable_property,none,
58815881
"stored property %0 of 'Sendable'-conforming %kind1 is mutable",
58825882
(DeclName, const ValueDecl *))

lib/Sema/TypeCheckConcurrency.cpp

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

801-
case SendableCheck::ImpliedByStandardProtocol:
801+
case SendableCheck::ImpliedByPreconcurrencyProtocol:
802802
case SendableCheck::Implicit:
803803
case SendableCheck::ImplicitForExternallyVisible:
804804
return false;
@@ -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;
@@ -6783,57 +6788,22 @@ static bool checkSendableInstanceStorage(
67836788
}
67846789
}
67856790

6786-
// Check that the property type is Sendable.
6787-
SendableCheckContext context(dc, check);
6788-
diagnoseNonSendableTypes(
6789-
propertyType, context,
6790-
/*inDerivedConformance*/Type(), property->getLoc(),
6791-
[&](Type type, DiagnosticBehavior behavior) {
6792-
auto preconcurrency = context.preconcurrencyBehavior(type);
6793-
if (isImplicitSendableCheck(check)) {
6794-
// If this is for an externally-visible conformance, fail.
6795-
if (check == SendableCheck::ImplicitForExternallyVisible) {
6796-
invalid = true;
6797-
return true;
6798-
}
6799-
6800-
// If we are to ignore this diagnostic, just continue.
6801-
if (behavior == DiagnosticBehavior::Ignore ||
6802-
preconcurrency == DiagnosticBehavior::Ignore)
6803-
return true;
6804-
6805-
invalid = true;
6806-
return true;
6807-
}
6808-
6809-
if (preconcurrency)
6810-
behavior = preconcurrency.value();
6811-
6812-
property
6813-
->diagnose(diag::non_concurrent_type_member, propertyType,
6814-
false, property->getName(), nominal)
6815-
.limitBehaviorWithPreconcurrency(behavior,
6816-
preconcurrency.has_value());
6817-
return false;
6818-
});
6819-
6820-
if (invalid) {
6821-
// For implicit checks, bail out early if anything failed.
6822-
if (isImplicitSendableCheck(check))
6823-
return true;
6824-
}
6825-
6826-
return false;
6791+
return checkSendabilityOfMemberType(property, propertyType);
68276792
}
68286793

68296794
/// Handle an enum associated value.
68306795
bool operator()(EnumElementDecl *element, Type elementType) override {
6831-
SendableCheckContext context (dc, check);
6796+
return checkSendabilityOfMemberType(element, elementType);
6797+
}
6798+
6799+
private:
6800+
bool checkSendabilityOfMemberType(ValueDecl *member, Type memberType) {
6801+
SendableCheckContext context(dc, check);
68326802
diagnoseNonSendableTypes(
6833-
elementType, context,
6834-
/*inDerivedConformance*/Type(), element->getLoc(),
6803+
memberType, context,
6804+
/*inDerivedConformance*/ Type(), member->getLoc(),
68356805
[&](Type type, DiagnosticBehavior behavior) {
6836-
auto preconcurrency = context.preconcurrencyBehavior(type);
6806+
auto preconcurrencyBehavior = context.preconcurrencyBehavior(type);
68376807
if (isImplicitSendableCheck(check)) {
68386808
// If this is for an externally-visible conformance, fail.
68396809
if (check == SendableCheck::ImplicitForExternallyVisible) {
@@ -6843,21 +6813,29 @@ static bool checkSendableInstanceStorage(
68436813

68446814
// If we are to ignore this diagnostic, just continue.
68456815
if (behavior == DiagnosticBehavior::Ignore ||
6846-
preconcurrency == DiagnosticBehavior::Ignore)
6816+
preconcurrencyBehavior == DiagnosticBehavior::Ignore)
68476817
return true;
68486818

68496819
invalid = true;
68506820
return true;
68516821
}
68526822

6853-
if (preconcurrency)
6854-
behavior = preconcurrency.value();
6855-
6856-
element
6857-
->diagnose(diag::non_concurrent_type_member, type, true,
6858-
element->getName(), nominal)
6859-
.limitBehaviorWithPreconcurrency(behavior,
6860-
preconcurrency.has_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();
6831+
6832+
member
6833+
->diagnose(diag::non_concurrent_type_member, type,
6834+
isa<EnumElementDecl>(member), member->getName(),
6835+
nominal, type->isEqual(memberType))
6836+
.limitBehaviorWithPreconcurrency(
6837+
behavior, fromPreconcurrencyConformance ||
6838+
preconcurrencyBehavior.has_value());
68616839
return false;
68626840
});
68636841

@@ -6869,6 +6847,7 @@ static bool checkSendableInstanceStorage(
68696847

68706848
return false;
68716849
}
6850+
68726851
} visitor(nominal, dc, check);
68736852

68746853
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
@@ -352,7 +352,7 @@ enum class SendableCheck {
352352

353353
/// Sendable conformance was implied by a protocol that inherits from
354354
/// Sendable and also predates concurrency.
355-
ImpliedByStandardProtocol,
355+
ImpliedByPreconcurrencyProtocol,
356356

357357
/// Implicit conformance to Sendable.
358358
Implicit,
@@ -366,7 +366,7 @@ enum class SendableCheck {
366366
static inline bool isImplicitSendableCheck(SendableCheck check) {
367367
switch (check) {
368368
case SendableCheck::Explicit:
369-
case SendableCheck::ImpliedByStandardProtocol:
369+
case SendableCheck::ImpliedByPreconcurrencyProtocol:
370370
return false;
371371

372372
case SendableCheck::Implicit:

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6681,7 +6681,7 @@ void TypeChecker::checkConformancesInContext(IterableDeclContext *idc) {
66816681
if (!hasDeprecatedUnsafeSendable && SendableConformance) {
66826682
SendableCheck check = SendableCheck::Explicit;
66836683
if (sendableConformancePreconcurrency)
6684-
check = SendableCheck::ImpliedByStandardProtocol;
6684+
check = SendableCheck::ImpliedByPreconcurrencyProtocol;
66856685
else if (SendableConformance->getSourceKind() ==
66866686
ConformanceEntryKind::Synthesized)
66876687
check = SendableCheck::Implicit;

test/Concurrency/concurrent_value_checking.swift

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

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

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/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)