Skip to content

Commit d62b565

Browse files
committed
Respect @preconcurrency on nominal types uniformly
When determining whether a non-sendable type is a nominal type that is affected by `@preconcurrency`, be sure to look at the specific type even when it's part of larger type, e.g., the `NS` in `NS?`. This makes `@preconcurrency` work properly through structural types like arrays, optionals, and dictionaries. While here, also check whether the nominal declaration itself has been marked as `@preconcurrency`. Fixes rdar://125081249.
1 parent c326fd3 commit d62b565

File tree

4 files changed

+49
-33
lines changed

4 files changed

+49
-33
lines changed

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -840,16 +840,25 @@ SendableCheckContext::preconcurrencyBehavior(Decl *decl) const {
840840
return std::nullopt;
841841

842842
if (auto *nominal = dyn_cast<NominalTypeDecl>(decl)) {
843-
// Determine whether this nominal type is visible via a @preconcurrency
844-
// import.
845-
auto import = nominal->findImport(fromDC);
846-
auto sourceFile = fromDC->getParentSourceFile();
843+
ModuleDecl *importedModule = nullptr;
844+
if (nominal->getAttrs().hasAttribute<PreconcurrencyAttr>()) {
845+
// If the declaration itself has the @preconcurrency attribute,
846+
// respect it.
847+
importedModule = nominal->getParentModule();
848+
} else {
849+
// Determine whether this nominal type is visible via a @preconcurrency
850+
// import.
851+
auto import = nominal->findImport(fromDC);
852+
auto sourceFile = fromDC->getParentSourceFile();
847853

848-
if (!import || !import->options.contains(ImportFlags::Preconcurrency))
849-
return std::nullopt;
854+
if (!import || !import->options.contains(ImportFlags::Preconcurrency))
855+
return std::nullopt;
850856

851-
if (sourceFile)
852-
sourceFile->setImportUsedPreconcurrency(*import);
857+
if (sourceFile)
858+
sourceFile->setImportUsedPreconcurrency(*import);
859+
860+
importedModule = import->module.importedModule;
861+
}
853862

854863
// When the type is explicitly non-Sendable, @preconcurrency imports
855864
// downgrade the diagnostic to a warning in Swift 6.
@@ -858,7 +867,7 @@ SendableCheckContext::preconcurrencyBehavior(Decl *decl) const {
858867

859868
// When the type is implicitly non-Sendable, `@preconcurrency` suppresses
860869
// diagnostics until the imported module enables Swift 6.
861-
return import->module.importedModule->isConcurrencyChecked()
870+
return importedModule->isConcurrencyChecked()
862871
? DiagnosticBehavior::Warning
863872
: DiagnosticBehavior::Ignore;
864873
}
@@ -5708,7 +5717,7 @@ static bool checkSendableInstanceStorage(
57085717
/*inDerivedConformance*/Type(), element->getLoc(),
57095718
[&](Type type, DiagnosticBehavior behavior) {
57105719
auto preconcurrency =
5711-
context.preconcurrencyBehavior(elementType->getAnyNominal());
5720+
context.preconcurrencyBehavior(type->getAnyNominal());
57125721
if (isImplicitSendableCheck(check)) {
57135722
// If this is for an externally-visible conformance, fail.
57145723
if (check == SendableCheck::ImplicitForExternallyVisible) {
@@ -5719,7 +5728,7 @@ static bool checkSendableInstanceStorage(
57195728
// If we are to ignore this diagnostic, just continue.
57205729
if (behavior == DiagnosticBehavior::Ignore ||
57215730
preconcurrency == DiagnosticBehavior::Ignore)
5722-
return false;
5731+
return true;
57235732

57245733
invalid = true;
57255734
return true;

lib/Sema/TypeCheckConcurrency.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -441,7 +441,7 @@ bool diagnoseNonSendableTypes(
441441
type, fromContext, derivedConformance, typeLoc,
442442
[&](Type specificType, DiagnosticBehavior behavior) {
443443
auto preconcurrency =
444-
fromContext.preconcurrencyBehavior(type->getAnyNominal());
444+
fromContext.preconcurrencyBehavior(specificType->getAnyNominal());
445445

446446
ctx.Diags.diagnose(diagnoseLoc, diag, type, diagArgs...)
447447
.limitBehaviorUntilSwiftVersion(behavior, 6)
@@ -477,7 +477,7 @@ bool diagnoseIfAnyNonSendableTypes(
477477
type, fromContext, derivedConformance, typeLoc,
478478
[&](Type specificType, DiagnosticBehavior behavior) {
479479
auto preconcurrency =
480-
fromContext.preconcurrencyBehavior(type->getAnyNominal());
480+
fromContext.preconcurrencyBehavior(specificType->getAnyNominal());
481481

482482
if (behavior == DiagnosticBehavior::Ignore ||
483483
preconcurrency == DiagnosticBehavior::Ignore)

test/Concurrency/predates_concurrency_import.swift

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,29 @@
1616
@preconcurrency import OtherActors
1717
// expected-warning@-1{{'@preconcurrency' attribute on module 'OtherActors' has no effect}}{{1-17=}}
1818

19+
@preconcurrency
20+
class MyPredatesConcurrencyClass { }
21+
22+
enum EnumWithPredatesConcurrencyValue {
23+
case stored(MyPredatesConcurrencyClass)
24+
}
25+
1926
func acceptSendable<T: Sendable>(_: T) { }
2027

2128
@available(SwiftStdlib 5.1, *)
2229
func test(
2330
ss: StrictStruct, ns: NonStrictClass, oma: OtherModuleActor,
24-
ssc: SomeSendableClass
31+
ssOpt: StrictStruct?, nsOpt: NonStrictClass?
32+
ssc: SomeSendableClass,
33+
mpcc: MyPredatesConcurrencyClass
2534
) async {
2635
acceptSendable(ss) // expected-warning{{type 'StrictStruct' does not conform to the 'Sendable' protocol}}
2736
acceptSendable(ns) // silence issue entirely
37+
acceptSendable(ssOpt) // expected-warning{{type 'StrictStruct?' does not conform to the 'Sendable' protocol}}
38+
acceptSendable(nsOpt) // silence issue entirely
2839
acceptSendable(oma) // okay
2940
acceptSendable(ssc) // okay
41+
acceptSendable(mpcc)
3042
}
3143

3244
let nonStrictGlobal = NonStrictClass() // no warning

test/Concurrency/transfernonsendable_preconcurrency.swift

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,6 @@
66
// A swift 5 module /with/ concurrency checking
77
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/PreconcurrencyChecked.swiftmodule -module-name PreconcurrencyChecked %S/Inputs/transfernonsendable_preconcurrency_checked.swift -disable-availability-checking -swift-version 5 -strict-concurrency=complete
88

9-
// Test swift 5 with strict concurrency with region isolation disabled
10-
// RUN: %target-swift-frontend -swift-version 5 %s -emit-sil -o /dev/null -verify -verify-additional-prefix swift-5-no-tns- -parse-as-library -I %t -strict-concurrency=complete -disable-availability-checking -disable-region-based-isolation-with-strict-concurrency
11-
129
// Test swift 5 with strict concurrency
1310
// RUN: %target-swift-frontend -swift-version 5 %s -emit-sil -o /dev/null -verify -verify-additional-prefix swift-5- -parse-as-library -I %t -strict-concurrency=complete -disable-availability-checking
1411

@@ -26,7 +23,7 @@
2623
////////////////////////
2724

2825
@preconcurrency import PreconcurrencyUnchecked
29-
import PreconcurrencyChecked // expected-swift-5-no-tns-warning {{add '@preconcurrency' to suppress 'Sendable'-related warnings from module 'PreconcurrencyChecked'}}
26+
import PreconcurrencyChecked
3027

3128
typealias PreCUncheckedNonSendableKlass = PreconcurrencyUnchecked.NonSendableKlass
3229
typealias PreCUncheckedExplicitlyNonSendableKlass = PreconcurrencyUnchecked.ExplicitlyNonSendableKlass
@@ -62,11 +59,10 @@ func testPreconcurrencyImplicitlyNonSendable() async {
6259
func testPreconcurrencyExplicitlyNonSendable() async {
6360
let x = PreCUncheckedExplicitlyNonSendableKlass()
6461
await transferToMain(x)
65-
// expected-swift-5-no-tns-warning @-1 {{passing argument of non-sendable type 'PreCUncheckedExplicitlyNonSendableKlass' (aka 'ExplicitlyNonSendableKlass') into main actor-isolated context may introduce data races}}
66-
// expected-swift-5-warning @-2 {{sending 'x' risks causing data races}}
67-
// expected-swift-5-note @-3 {{sending 'x' to main actor-isolated global function 'transferToMain' risks causing data races between main actor-isolated and local nonisolated uses}}
68-
// expected-swift-6-warning @-4 {{sending 'x' risks causing data races}}
69-
// expected-swift-6-note @-5 {{sending 'x' to main actor-isolated global function 'transferToMain' risks causing data races between main actor-isolated and local nonisolated uses}}
62+
// expected-swift-5-warning @-1 {{sending 'x' risks causing data races}}
63+
// expected-swift-5-note @-2 {{sending 'x' to main actor-isolated global function 'transferToMain' risks causing data races between main actor-isolated and local nonisolated uses}}
64+
// expected-swift-6-warning @-3 {{sending 'x' risks causing data races}}
65+
// expected-swift-6-note @-4 {{sending 'x' to main actor-isolated global function 'transferToMain' risks causing data races between main actor-isolated and local nonisolated uses}}
7066
useValue(x)
7167
// expected-swift-5-note @-1 {{access can happen concurrently}}
7268
// expected-swift-6-note @-2 {{access can happen concurrently}}
@@ -75,7 +71,7 @@ func testPreconcurrencyExplicitlyNonSendable() async {
7571
// In swift 5 this is a warning and in swift 6 this is an error.
7672
func testNormal() async {
7773
let x = PostCUncheckedNonSendableKlass()
78-
await transferToMain(x) // expected-swift-5-no-tns-warning {{passing argument of non-sendable type 'PostCUncheckedNonSendableKlass' (aka 'NonSendableKlass') into main actor-isolated context may introduce data races}}
74+
await transferToMain(x)
7975
// expected-swift-5-warning @-1 {{sending 'x' risks causing data races}}
8076
// expected-swift-6-error @-2 {{sending 'x' risks causing data races}}
8177
// expected-swift-5-note @-3 {{sending 'x' to main actor-isolated global function 'transferToMain' risks causing data races between main actor-isolated and local nonisolated uses}}
@@ -90,11 +86,10 @@ func testOnlyErrorOnExactValue() async {
9086
// We would squelch this if we transferred it directly. Also we error even
9187
// though we use x later.
9288
await transferToMain(y)
93-
// expected-swift-5-no-tns-warning @-1 2{{passing argument of non-sendable type '(PreCUncheckedNonSendableKlass, PreCUncheckedNonSendableKlass)' (aka '(NonSendableKlass, NonSendableKlass)') into main actor-isolated context may introduce data races}}
94-
// expected-swift-5-warning @-2 {{sending 'y' risks causing data races}}
95-
// expected-swift-5-note @-3 {{sending 'y' to main actor-isolated global function 'transferToMain' risks causing data races between main actor-isolated and local nonisolated uses}}
96-
// expected-swift-6-error @-4 {{sending 'y' risks causing data races}}
97-
// expected-swift-6-note @-5 {{sending 'y' to main actor-isolated global function 'transferToMain' risks causing data races between main actor-isolated and local nonisolated uses}}
89+
// expected-swift-5-warning @-1 {{sending 'y' risks causing data races}}
90+
// expected-swift-5-note @-2 {{sending 'y' to main actor-isolated global function 'transferToMain' risks causing data races between main actor-isolated and local nonisolated uses}}
91+
// expected-swift-6-error @-3 {{sending 'y' risks causing data races}}
92+
// expected-swift-6-note @-4 {{sending 'y' to main actor-isolated global function 'transferToMain' risks causing data races between main actor-isolated and local nonisolated uses}}
9893
useValue(x)
9994
// expected-swift-5-note @-1 {{access can happen concurrently}}
10095
// expected-swift-6-note @-2 {{access can happen concurrently}}
@@ -117,15 +112,15 @@ func testNeverTransfer(_ x: PreCUncheckedNonSendableKlass) async {
117112
}
118113

119114
func testNeverTransferExplicit(_ x: PreCUncheckedExplicitlyNonSendableKlass) async {
120-
await transferToMain(x) // expected-swift-5-no-tns-warning {{passing argument of non-sendable type 'PreCUncheckedExplicitlyNonSendableKlass' (aka 'ExplicitlyNonSendableKlass') into main actor-isolated context may introduce data races}}
115+
await transferToMain(x)
121116
// expected-swift-5-warning @-1 {{sending 'x' risks causing data races}}
122117
// expected-swift-5-note @-2 {{sending task-isolated 'x' to main actor-isolated global function 'transferToMain' risks causing data races between main actor-isolated and task-isolated uses}}
123118
// expected-swift-6-warning @-3 {{sending 'x' risks causing data races}}
124119
// expected-swift-6-note @-4 {{sending task-isolated 'x' to main actor-isolated global function 'transferToMain' risks causing data races between main actor-isolated and task-isolated uses}}
125120
}
126121

127122
func testNeverTransferNormal(_ x: PostCUncheckedNonSendableKlass) async {
128-
await transferToMain(x) // expected-swift-5-no-tns-warning {{passing argument of non-sendable type 'PostCUncheckedNonSendableKlass' (aka 'NonSendableKlass') into main actor-isolated context may introduce data races}}
123+
await transferToMain(x)
129124
// expected-swift-5-warning @-1 {{sending 'x' risks causing data races}}
130125
// expected-swift-5-note @-2 {{sending task-isolated 'x' to main actor-isolated global function 'transferToMain' risks causing data races between main actor-isolated and task-isolated uses}}
131126
// expected-swift-6-error @-3 {{sending 'x' risks causing data races}}
@@ -134,7 +129,7 @@ func testNeverTransferNormal(_ x: PostCUncheckedNonSendableKlass) async {
134129

135130
// Inexact match => normal behavior.
136131
func testNeverTransferInexactMatch(_ x: (PreCUncheckedNonSendableKlass, PreCUncheckedNonSendableKlass)) async {
137-
await transferToMain(x) // expected-swift-5-no-tns-warning 2{{passing argument of non-sendable type '(PreCUncheckedNonSendableKlass, PreCUncheckedNonSendableKlass)' (aka '(NonSendableKlass, NonSendableKlass)') into main actor-isolated context may introduce data races}}
132+
await transferToMain(x)
138133
// expected-swift-5-warning @-1 {{sending 'x' risks causing data races}}
139134
// expected-swift-5-note @-2 {{sending task-isolated 'x' to main actor-isolated global function 'transferToMain' risks causing data races between main actor-isolated and task-isolated uses}}
140135
// expected-swift-6-error @-3 {{sending 'x' risks causing data races}}
@@ -143,7 +138,7 @@ func testNeverTransferInexactMatch(_ x: (PreCUncheckedNonSendableKlass, PreCUnch
143138

144139
// Inexact match => normal behavior.
145140
func testNeverTransferInexactMatchExplicit(_ x: (PreCUncheckedExplicitlyNonSendableKlass, PreCUncheckedExplicitlyNonSendableKlass)) async {
146-
await transferToMain(x) // expected-swift-5-no-tns-warning {{passing argument of non-sendable type '(PreCUncheckedExplicitlyNonSendableKlass, PreCUncheckedExplicitlyNonSendableKlass)' (aka '(ExplicitlyNonSendableKlass, ExplicitlyNonSendableKlass)') into main actor-isolated context may introduce data races}}
141+
await transferToMain(x)
147142
// expected-swift-5-warning @-1 {{sending 'x' risks causing data races}}
148143
// expected-swift-5-note @-2 {{sending task-isolated 'x' to main actor-isolated global function 'transferToMain' risks causing data races between main actor-isolated and task-isolated uses}}
149144
// expected-swift-6-error @-3 {{sending 'x' risks causing data races}}

0 commit comments

Comments
 (0)