Skip to content

Commit 363f203

Browse files
committed
Make @preconcurrency suppress Sendable diagnostics more reliably.
Sendable diagnostics were firing a bit too eagerly because a suppressed Sendable diagnostic for instance storage of a struct/enum would still cause that type to not be implicitly Sendable. Additionally, a `@preconcurrency` import would not always suppress the diagnostic when there was an explicit Sendable conformance, which it should have. Fixes rdar://88363542.
1 parent 4960851 commit 363f203

File tree

4 files changed

+130
-4
lines changed

4 files changed

+130
-4
lines changed

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -719,7 +719,6 @@ DiagnosticBehavior SendableCheckContext::diagnosticBehavior(
719719
// Determine whether the type was explicitly non-Sendable.
720720
auto nominalModule = nominal->getParentModule();
721721
bool isExplicitlyNonSendable = nominalModule->isConcurrencyChecked() ||
722-
isExplicitSendableConformance() ||
723722
hasExplicitSendableConformance(nominal);
724723

725724
// Determine whether this nominal type is visible via a @preconcurrency
@@ -728,7 +727,7 @@ DiagnosticBehavior SendableCheckContext::diagnosticBehavior(
728727

729728
// When the type is explicitly non-Sendable...
730729
if (isExplicitlyNonSendable) {
731-
// @preconcurrency imports downgrade the diagnostic to a warning.
730+
// @preconcurrency imports downgrade the diagnostic to a warning in Swift 6,
732731
if (import && import->options.contains(ImportFlags::Preconcurrency)) {
733732
// FIXME: Note that this @preconcurrency import was "used".
734733
return DiagnosticBehavior::Warning;
@@ -748,7 +747,17 @@ DiagnosticBehavior SendableCheckContext::diagnosticBehavior(
748747
: DiagnosticBehavior::Ignore;
749748
}
750749

751-
return defaultDiagnosticBehavior();
750+
auto defaultBehavior = defaultDiagnosticBehavior();
751+
752+
// If we are checking an implicit Sendable conformance, don't suppress
753+
// diagnostics for declarations in the same module. We want them so make
754+
// enclosing inferred types non-Sendable.
755+
if (defaultBehavior == DiagnosticBehavior::Ignore &&
756+
nominal->getParentSourceFile() &&
757+
conformanceCheck && *conformanceCheck == SendableCheck::Implicit)
758+
return DiagnosticBehavior::Warning;
759+
760+
return defaultBehavior;
752761
}
753762

754763
/// Produce a diagnostic for a single instance of a non-Sendable type where
@@ -3890,8 +3899,10 @@ static bool checkSendableInstanceStorage(
38903899
bool operator()(VarDecl *property, Type propertyType) {
38913900
// Classes with mutable properties are not Sendable.
38923901
if (property->supportsMutation() && isa<ClassDecl>(nominal)) {
3893-
if (check == SendableCheck::Implicit)
3902+
if (check == SendableCheck::Implicit) {
3903+
invalid = true;
38943904
return true;
3905+
}
38953906

38963907
auto behavior = SendableCheckContext(
38973908
dc, check).defaultDiagnosticBehavior();
@@ -3910,6 +3921,10 @@ static bool checkSendableInstanceStorage(
39103921
propertyType, SendableCheckContext(dc, check), property->getLoc(),
39113922
[&](Type type, DiagnosticBehavior behavior) {
39123923
if (check == SendableCheck::Implicit) {
3924+
// If we are to ignore this diagnose, just continue.
3925+
if (behavior == DiagnosticBehavior::Ignore)
3926+
return false;
3927+
39133928
invalid = true;
39143929
return true;
39153930
}
@@ -3937,6 +3952,10 @@ static bool checkSendableInstanceStorage(
39373952
elementType, SendableCheckContext(dc, check), element->getLoc(),
39383953
[&](Type type, DiagnosticBehavior behavior) {
39393954
if (check == SendableCheck::Implicit) {
3955+
// If we are to ignore this diagnose, just continue.
3956+
if (behavior == DiagnosticBehavior::Ignore)
3957+
return false;
3958+
39403959
invalid = true;
39413960
return true;
39423961
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/StrictModule.swiftmodule -module-name StrictModule -warn-concurrency %S/Inputs/StrictModule.swift
3+
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/NonStrictModule.swiftmodule -module-name NonStrictModule %S/Inputs/NonStrictModule.swift
4+
// RUN: %target-typecheck-verify-swift -disable-availability-checking -I %t
5+
6+
// REQUIRES: concurrency
7+
8+
import StrictModule
9+
@preconcurrency import NonStrictModule
10+
11+
actor A {
12+
func f() -> [StrictStruct: NonStrictClass] { [:] }
13+
}
14+
15+
class NS { } // expected-note 2{{class 'NS' does not conform to the 'Sendable' protocol}}
16+
17+
struct MyType {
18+
var nsc: NonStrictClass
19+
}
20+
21+
struct MyType2: Sendable {
22+
var nsc: NonStrictClass // no warning; @preconcurrency suppressed it
23+
var ns: NS // expected-warning{{stored property 'ns' of 'Sendable'-conforming struct 'MyType2' has non-sendable type 'NS'}}
24+
}
25+
26+
struct MyType3 {
27+
var nsc: NonStrictClass
28+
}
29+
30+
func testA(ns: NS, mt: MyType, mt2: MyType2, mt3: MyType3) async {
31+
Task {
32+
print(ns) // expected-warning{{capture of 'ns' with non-sendable type 'NS' in a `@Sendable` closure}}
33+
print(mt) // no warning: MyType is Sendable because we suppressed NonStrictClass's warning
34+
print(mt2)
35+
print(mt3)
36+
}
37+
}
38+
39+
extension NonStrictStruct: @unchecked Sendable { }
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/StrictModule.swiftmodule -module-name StrictModule -warn-concurrency %S/Inputs/StrictModule.swift
3+
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/NonStrictModule.swiftmodule -module-name NonStrictModule %S/Inputs/NonStrictModule.swift
4+
// RUN: %target-typecheck-verify-swift -disable-availability-checking -I %t
5+
6+
// REQUIRES: concurrency
7+
8+
import StrictModule
9+
import NonStrictModule
10+
11+
actor A {
12+
func f() -> [StrictStruct: NonStrictClass] { [:] }
13+
}
14+
15+
class NS { } // expected-note{{class 'NS' does not conform to the 'Sendable' protocol}}
16+
17+
struct MyType {
18+
var nsc: NonStrictClass
19+
}
20+
21+
struct MyType2 { // expected-note{{consider making struct 'MyType2' conform to the 'Sendable' protocol}}
22+
var nsc: NonStrictClass
23+
var ns: NS
24+
}
25+
26+
func testA(ns: NS, mt: MyType, mt2: MyType2) async {
27+
Task {
28+
print(ns) // expected-warning{{capture of 'ns' with non-sendable type 'NS' in a `@Sendable` closure}}
29+
print(mt) // no warning: MyType is Sendable because we suppressed NonStrictClass's warning
30+
print(mt2) // expected-warning{{capture of 'mt2' with non-sendable type 'MyType2' in a `@Sendable` closure}}
31+
}
32+
}
33+
34+
extension NonStrictStruct: @unchecked Sendable { }
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/StrictModule.swiftmodule -module-name StrictModule -warn-concurrency %S/Inputs/StrictModule.swift
3+
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/NonStrictModule.swiftmodule -module-name NonStrictModule %S/Inputs/NonStrictModule.swift
4+
// RUN: %target-typecheck-verify-swift -disable-availability-checking -I %t
5+
6+
// REQUIRES: concurrency
7+
8+
import StrictModule
9+
import NonStrictModule // expected-remark{{add '@preconcurrency' to suppress 'Sendable'-related warnings from module 'NonStrictModule'}}
10+
11+
actor A {
12+
func f() -> [StrictStruct: NonStrictClass] { [:] }
13+
}
14+
15+
class NS { } // expected-note 2{{class 'NS' does not conform to the 'Sendable' protocol}}
16+
17+
struct MyType {
18+
var nsc: NonStrictClass
19+
}
20+
21+
struct MyType2: Sendable {
22+
var nsc: NonStrictClass // expected-warning{{stored property 'nsc' of 'Sendable'-conforming struct 'MyType2' has non-sendable type 'NonStrictClass'}}
23+
var ns: NS // expected-warning{{stored property 'ns' of 'Sendable'-conforming struct 'MyType2' has non-sendable type 'NS'}}
24+
}
25+
26+
func testA(ns: NS, mt: MyType, mt2: MyType2) async {
27+
Task {
28+
print(ns) // expected-warning{{capture of 'ns' with non-sendable type 'NS' in a `@Sendable` closure}}
29+
print(mt) // no warning: MyType is Sendable because we suppressed NonStrictClass's warning
30+
print(mt2)
31+
}
32+
}
33+
34+
extension NonStrictStruct: @unchecked Sendable { }

0 commit comments

Comments
 (0)