Skip to content

Commit e8945ac

Browse files
committed
[Concurrency] @preconcurrency import downgrades globals concurrency errors to warnings
1 parent 4abb567 commit e8945ac

File tree

4 files changed

+64
-14
lines changed

4 files changed

+64
-14
lines changed

include/swift/AST/DiagnosticEngine.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,16 @@ namespace swift {
564564
/// emitted as a warning, but a note will still be emitted as a note.
565565
InFlightDiagnostic &limitBehavior(DiagnosticBehavior limit);
566566

567+
/// Conditionally prevent the diagnostic from behaving more severely than \p
568+
/// limit. If the condition is false, no limit is imposed.
569+
InFlightDiagnostic &limitBehaviorIf(bool shouldLimit,
570+
DiagnosticBehavior limit) {
571+
if (!shouldLimit) {
572+
return *this;
573+
}
574+
return limitBehavior(limit);
575+
}
576+
567577
/// Limit the diagnostic behavior to warning until the specified version.
568578
///
569579
/// This helps stage in fixes for stricter diagnostics as warnings

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -829,12 +829,12 @@ static bool hasExplicitSendableConformance(NominalTypeDecl *nominal,
829829
conformance.getConcrete())->isMissing());
830830
}
831831

832-
/// Find the import that makes the given nominal declaration available.
832+
/// Find the import that makes the given declaration available.
833833
static llvm::Optional<AttributedImport<ImportedModule>>
834-
findImportFor(NominalTypeDecl *nominal, const DeclContext *fromDC) {
835-
// If the nominal type is from the current module, there's no import.
836-
auto nominalModule = nominal->getParentModule();
837-
if (nominalModule == fromDC->getParentModule())
834+
findImportFor(const DeclContext *dc, const DeclContext *fromDC) {
835+
// If the type is from the current module, there's no import.
836+
auto module = dc->getParentModule();
837+
if (module == fromDC->getParentModule())
838838
return llvm::None;
839839

840840
auto fromSourceFile = fromDC->getParentSourceFile();
@@ -843,16 +843,16 @@ findImportFor(NominalTypeDecl *nominal, const DeclContext *fromDC) {
843843

844844
// Look to see if the owning module was directly imported.
845845
for (const auto &import : fromSourceFile->getImports()) {
846-
if (import.module.importedModule == nominalModule)
846+
if (import.module.importedModule == module)
847847
return import;
848848
}
849849

850850
// Now look for transitive imports.
851-
auto &importCache = nominal->getASTContext().getImportCache();
851+
auto &importCache = dc->getASTContext().getImportCache();
852852
for (const auto &import : fromSourceFile->getImports()) {
853853
auto &importSet = importCache.getImportSet(import.module.importedModule);
854854
for (const auto &transitive : importSet.getTransitiveImports()) {
855-
if (transitive.importedModule == nominalModule) {
855+
if (transitive.importedModule == module) {
856856
return import;
857857
}
858858
}
@@ -2931,7 +2931,8 @@ namespace {
29312931
///
29322932
/// \returns true if we diagnosed the entity, \c false otherwise.
29332933
bool diagnoseReferenceToUnsafeGlobal(ValueDecl *value, SourceLoc loc) {
2934-
switch (value->getASTContext().LangOpts.StrictConcurrencyLevel) {
2934+
auto &ctx = value->getASTContext();
2935+
switch (ctx.LangOpts.StrictConcurrencyLevel) {
29352936
case StrictConcurrency::Minimal:
29362937
case StrictConcurrency::Targeted:
29372938
// Never diagnose.
@@ -2954,17 +2955,32 @@ namespace {
29542955
return false;
29552956

29562957
// If it's actor-isolated, it's already been dealt with.
2957-
if (getActorIsolation(value).isActorIsolated())
2958+
const auto isolation = getActorIsolation(value);
2959+
if (isolation.isActorIsolated())
29582960
return false;
29592961

29602962
if (auto attr = value->getAttrs().getAttribute<NonisolatedAttr>();
29612963
attr && attr->isUnsafe()) {
29622964
return false;
29632965
}
29642966

2967+
const auto import =
2968+
findImportFor(var->getDeclContext(), getDeclContext());
2969+
const bool isPreconcurrencyImport =
2970+
import && import->options.contains(ImportFlags::Preconcurrency);
2971+
const auto isPreconcurrencyUnspecifiedIsolation =
2972+
isPreconcurrencyImport && isolation.isUnspecified();
29652973
ctx.Diags.diagnose(loc, diag::shared_mutable_state_access, value)
2966-
.warnUntilSwiftVersion(6);
2974+
.warnUntilSwiftVersionIf(!isPreconcurrencyUnspecifiedIsolation, 6)
2975+
.limitBehaviorIf(isPreconcurrencyImport, DiagnosticBehavior::Warning)
2976+
.limitBehaviorIf(!ctx.isSwiftVersionAtLeast(6) &&
2977+
isPreconcurrencyUnspecifiedIsolation,
2978+
DiagnosticBehavior::Ignore);
29672979
value->diagnose(diag::kind_declared_here, value->getDescriptiveKind());
2980+
if (const auto sourceFile = getDeclContext()->getParentSourceFile();
2981+
sourceFile && isPreconcurrencyImport) {
2982+
sourceFile->setImportUsedPreconcurrency(*import);
2983+
}
29682984
return true;
29692985
}
29702986

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
public struct Globals {
2+
public static let integerConstant = 0
3+
public static var integerMutable = 0
4+
5+
public static nonisolated(unsafe) let nonisolatedUnsafeIntegerConstant = 0
6+
public static nonisolated(unsafe) var nonisolatedUnsafeIntegerMutable = 0
7+
8+
@MainActor public static var actorInteger = 0
9+
10+
public init() {}
11+
}

test/Concurrency/experimental_feature_strictconcurrency.swift

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1-
// RUN: %target-swift-frontend -disable-availability-checking -parse-as-library -enable-experimental-feature StrictConcurrency -enable-experimental-feature GlobalConcurrency -swift-version 6 -emit-sil -o /dev/null -verify %s
2-
// RUN: %target-swift-frontend -disable-availability-checking -parse-as-library -enable-experimental-feature StrictConcurrency=complete -enable-experimental-feature GlobalConcurrency -swift-version 6 -emit-sil -o /dev/null -verify %s
3-
// RUN: %target-swift-frontend -disable-availability-checking -parse-as-library -enable-experimental-feature StrictConcurrency=complete -enable-experimental-feature GlobalConcurrency -swift-version 6 -emit-sil -o /dev/null -verify -verify-additional-prefix region-isolation- -enable-experimental-feature RegionBasedIsolation %s
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/GlobalVariables.swiftmodule -module-name GlobalVariables -parse-as-library -strict-concurrency=minimal -swift-version 5 %S/Inputs/GlobalVariables.swift
3+
// RUN: %target-swift-frontend -disable-availability-checking -parse-as-library -enable-experimental-feature StrictConcurrency -enable-experimental-feature GlobalConcurrency -swift-version 6 -I %t %s %s -emit-sil -o /dev/null -verify %s
4+
// RUN: %target-swift-frontend -disable-availability-checking -parse-as-library -enable-experimental-feature StrictConcurrency=complete -enable-experimental-feature GlobalConcurrency -swift-version 6 -I %t %s %s -emit-sil -o /dev/null -verify %s
5+
// RUN: %target-swift-frontend -disable-availability-checking -parse-as-library -enable-experimental-feature StrictConcurrency=complete -enable-experimental-feature GlobalConcurrency -swift-version 6 -I %t %s %s -emit-sil -o /dev/null -verify -verify-additional-prefix region-isolation- -enable-experimental-feature RegionBasedIsolation %s
46

57
// REQUIRES: concurrency
68
// REQUIRES: asserts
79

10+
@preconcurrency import GlobalVariables
11+
812
class C1 { } // expected-note{{class 'C1' does not conform to the 'Sendable' protocol}}
913
class C2 { }
1014

@@ -74,6 +78,7 @@ func f() {
7478
print(TestStatics.immutableExplicitSendable)
7579
print(TestStatics.immutableInferredSendable)
7680
print(TestStatics.mutable) // expected-error{{reference to static property 'mutable' is not concurrency-safe because it involves shared mutable state}}
81+
print(Globals.actorInteger) // expected-error{{main actor-isolated static property 'actorInteger' can not be referenced from global actor 'TestGlobalActor'}}
7782
}
7883

7984
func testLocalNonisolatedUnsafe() async {
@@ -85,6 +90,14 @@ func testLocalNonisolatedUnsafe() async {
8590
print(await task.value)
8691
}
8792

93+
func testCGlobals() { // expected-note{{add '@MainActor' to make global function 'testCGlobals()' part of global actor 'MainActor'}}
94+
let _ = Globals.integerConstant
95+
let _ = Globals.integerMutable // expected-warning{{reference to static property 'integerMutable' is not concurrency-safe because it involves shared mutable state}}
96+
let _ = Globals.nonisolatedUnsafeIntegerConstant
97+
let _ = Globals.nonisolatedUnsafeIntegerMutable
98+
let _ = Globals.actorInteger // expected-error{{main actor-isolated static property 'actorInteger' can not be referenced from a non-isolated context}}
99+
}
100+
88101
@MainActor
89102
func iterate(stream: AsyncStream<Int>) async {
90103
nonisolated(unsafe) var it = stream.makeAsyncIterator()

0 commit comments

Comments
 (0)