Skip to content

Commit 6bd354a

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 a33eaf6 commit 6bd354a

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
@@ -714,7 +714,6 @@ DiagnosticBehavior SendableCheckContext::diagnosticBehavior(
714714
// Determine whether the type was explicitly non-Sendable.
715715
auto nominalModule = nominal->getParentModule();
716716
bool isExplicitlyNonSendable = nominalModule->isConcurrencyChecked() ||
717-
isExplicitSendableConformance() ||
718717
hasExplicitSendableConformance(nominal);
719718

720719
// Determine whether this nominal type is visible via a @preconcurrency
@@ -723,7 +722,7 @@ DiagnosticBehavior SendableCheckContext::diagnosticBehavior(
723722

724723
// When the type is explicitly non-Sendable...
725724
if (isExplicitlyNonSendable) {
726-
// @preconcurrency imports downgrade the diagnostic to a warning.
725+
// @preconcurrency imports downgrade the diagnostic to a warning in Swift 6,
727726
if (import && import->options.contains(ImportFlags::Preconcurrency)) {
728727
// FIXME: Note that this @preconcurrency import was "used".
729728
return DiagnosticBehavior::Warning;
@@ -743,7 +742,17 @@ DiagnosticBehavior SendableCheckContext::diagnosticBehavior(
743742
: DiagnosticBehavior::Ignore;
744743
}
745744

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

749758
/// Produce a diagnostic for a single instance of a non-Sendable type where
@@ -3871,8 +3880,10 @@ static bool checkSendableInstanceStorage(
38713880
bool operator()(VarDecl *property, Type propertyType) {
38723881
// Classes with mutable properties are not Sendable.
38733882
if (property->supportsMutation() && isa<ClassDecl>(nominal)) {
3874-
if (check == SendableCheck::Implicit)
3883+
if (check == SendableCheck::Implicit) {
3884+
invalid = true;
38753885
return true;
3886+
}
38763887

38773888
auto behavior = SendableCheckContext(
38783889
dc, check).defaultDiagnosticBehavior();
@@ -3891,6 +3902,10 @@ static bool checkSendableInstanceStorage(
38913902
propertyType, SendableCheckContext(dc, check), property->getLoc(),
38923903
[&](Type type, DiagnosticBehavior behavior) {
38933904
if (check == SendableCheck::Implicit) {
3905+
// If we are to ignore this diagnose, just continue.
3906+
if (behavior == DiagnosticBehavior::Ignore)
3907+
return false;
3908+
38943909
invalid = true;
38953910
return true;
38963911
}
@@ -3918,6 +3933,10 @@ static bool checkSendableInstanceStorage(
39183933
elementType, SendableCheckContext(dc, check), element->getLoc(),
39193934
[&](Type type, DiagnosticBehavior behavior) {
39203935
if (check == SendableCheck::Implicit) {
3936+
// If we are to ignore this diagnose, just continue.
3937+
if (behavior == DiagnosticBehavior::Ignore)
3938+
return false;
3939+
39213940
invalid = true;
39223941
return true;
39233942
}
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)