Skip to content

Commit af51b5f

Browse files
authored
Merge pull request swiftlang#73698 from DougGregor/preconcurrency-fixes
Various fixes for `@preconcurrency`
2 parents f055192 + 0f8c478 commit af51b5f

16 files changed

+162
-90
lines changed

include/swift/AST/DiagnosticsSIL.def

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -694,9 +694,6 @@ ERROR(isolated_after_nonisolated, none,
694694
NOTE(nonisolated_blame, none, "after %1%2 %3, "
695695
"only non-isolated properties of 'self' can be accessed from "
696696
"%select{this init|a deinit}0", (bool, StringRef, StringRef, DeclName))
697-
ERROR(non_sendable_from_deinit,none,
698-
"cannot access %1 %2 with a non-sendable type %0 from non-isolated deinit",
699-
(Type, DescriptiveDeclKind, DeclName))
700697
ERROR(isolated_property_mutation_in_nonisolated_context,none,
701698
"actor-isolated %kind0 can not be %select{referenced|mutated}1 "
702699
"from a non-isolated context",

include/swift/AST/DiagnosticsSema.def

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5520,8 +5520,8 @@ ERROR(sendable_isolated_sync_function,none,
55205520
"%0 synchronous %kind1 cannot be marked as '@Sendable'",
55215521
(ActorIsolation, const ValueDecl *))
55225522
ERROR(nonsendable_instance_method,none,
5523-
"instance methods of non-Sendable types cannot be marked as '@Sendable'",
5524-
())
5523+
"instance method of non-Sendable type %0 cannot be marked as '@Sendable'",
5524+
(Type))
55255525
ERROR(concurrent_access_of_local_capture,none,
55265526
"%select{mutation of|reference to}0 captured %kind1 in "
55275527
"concurrently-executing code",
@@ -5796,6 +5796,10 @@ ERROR(nonisolated_wrapped_property,none,
57965796
"'nonisolated' is not supported on properties with property wrappers",
57975797
())
57985798

5799+
ERROR(non_sendable_from_deinit,none,
5800+
"cannot access %1 %2 with a non-sendable type %0 from non-isolated deinit",
5801+
(Type, DescriptiveDeclKind, DeclName))
5802+
57995803
ERROR(actor_instance_property_wrapper,none,
58005804
"%0 property in property wrapper type %1 cannot be isolated to "
58015805
"the actor instance; consider 'nonisolated'",

include/swift/Sema/Concurrency.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@
2222

2323
namespace swift {
2424

25+
class DeclContext;
2526
class SourceFile;
2627
class NominalTypeDecl;
28+
class VarDecl;
2729

2830
/// If any of the imports in this source file was @preconcurrency but there were
2931
/// no diagnostics downgraded or suppressed due to that @preconcurrency, suggest
@@ -35,6 +37,13 @@ void diagnoseUnnecessaryPreconcurrencyImports(SourceFile &sf);
3537
bool hasExplicitSendableConformance(NominalTypeDecl *nominal,
3638
bool applyModuleDefault = true);
3739

40+
/// Diagnose the use of an instance property of non-sendable type from an
41+
/// nonisolated deinitializer within an actor-isolated type.
42+
///
43+
/// \returns true iff a diagnostic was emitted for this reference.
44+
bool diagnoseNonSendableFromDeinit(
45+
SourceLoc refLoc, VarDecl *var, DeclContext *dc);
46+
3847
} // namespace swift
3948

4049
#endif

lib/SILOptimizer/Mandatory/FlowIsolation.cpp

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "swift/AST/Expr.h"
1717
#include "swift/AST/ActorIsolation.h"
1818
#include "swift/AST/DiagnosticsSIL.h"
19+
#include "swift/Sema/Concurrency.h"
1920
#include "swift/SIL/ApplySite.h"
2021
#include "swift/SIL/BitDataflow.h"
2122
#include "swift/SIL/BasicBlockBits.h"
@@ -528,31 +529,17 @@ static bool onlyDeinitAccess(RefElementAddrInst *inst) {
528529
/// is happening in a deinit that uses flow-isolation.
529530
/// \returns true iff a diagnostic was emitted for this reference.
530531
static bool diagnoseNonSendableFromDeinit(RefElementAddrInst *inst) {
531-
VarDecl *var = inst->getField();
532-
Type ty = var->getTypeInContext();
533-
DeclContext *dc = inst->getFunction()->getDeclContext();
532+
auto dc = inst->getFunction()->getDeclContext();
534533

535-
// FIXME: we should emit diagnostics in other modes using:
536-
//
537-
// if (!shouldDiagnoseExistingDataRaces(dc))
538-
// return false;
539-
//
540-
// but until we decide how we want to handle isolated state from deinits,
541-
// we're going to limit the noise to complete mode for now.
534+
// For historical reasons, only diagnose this issue in strict mode.
542535
if (dc->getASTContext().LangOpts.StrictConcurrencyLevel
543-
!= StrictConcurrency::Complete)
544-
return false;
545-
546-
if (ty->isSendableType())
536+
!= StrictConcurrency::Complete)
547537
return false;
548538

549-
auto &diag = var->getASTContext().Diags;
550-
551-
diag.diagnose(inst->getLoc().getSourceLoc(), diag::non_sendable_from_deinit,
552-
ty, var->getDescriptiveKind(), var->getName())
553-
.warnUntilSwiftVersion(6);
554-
555-
return true;
539+
return swift::diagnoseNonSendableFromDeinit(
540+
inst->getLoc().getSourceLoc(),
541+
inst->getField(),
542+
dc);
556543
}
557544

558545
class OperandWorklist {

lib/Sema/TypeCheckAttr.cpp

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6938,10 +6938,13 @@ void AttributeChecker::visitSendableAttr(SendableAttr *attr) {
69386938
// Prevent Sendable Attr from being added to methods of non-sendable types
69396939
if (auto *funcDecl = dyn_cast<AbstractFunctionDecl>(D)) {
69406940
if (auto selfDecl = funcDecl->getImplicitSelfDecl()) {
6941-
if (!selfDecl->getTypeInContext()->isSendableType()) {
6942-
diagnose(attr->getLocation(), diag::nonsendable_instance_method)
6943-
.warnUntilSwiftVersion(6);
6944-
}
6941+
diagnoseIfAnyNonSendableTypes(
6942+
selfDecl->getTypeInContext(),
6943+
SendableCheckContext(funcDecl),
6944+
Type(),
6945+
SourceLoc(),
6946+
attr->getLocation(),
6947+
diag::nonsendable_instance_method);
69456948
}
69466949
}
69476950
}
@@ -6965,13 +6968,16 @@ void AttributeChecker::visitNonisolatedAttr(NonisolatedAttr *attr) {
69656968

69666969
// 'nonisolated' without '(unsafe)' is not allowed on non-Sendable variables.
69676970
auto type = var->getTypeInContext();
6968-
if (!attr->isUnsafe() && !type->hasError() &&
6969-
!type->isSendableType()) {
6970-
Ctx.Diags.diagnose(attr->getLocation(),
6971-
diag::nonisolated_non_sendable,
6972-
type)
6973-
.warnUntilSwiftVersion(6);
6974-
return;
6971+
if (!attr->isUnsafe() && !type->hasError()) {
6972+
bool diagnosed = diagnoseIfAnyNonSendableTypes(
6973+
type,
6974+
SendableCheckContext(dc),
6975+
Type(),
6976+
SourceLoc(),
6977+
attr->getLocation(),
6978+
diag::nonisolated_non_sendable);
6979+
if (diagnosed)
6980+
return;
69756981
}
69766982

69776983
if (auto nominal = dyn_cast<NominalTypeDecl>(dc)) {

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -511,7 +511,6 @@ static bool varIsSafeAcrossActors(const ModuleDecl *fromModule,
511511
return true;
512512

513513
if (!var->isLet()) {
514-
ASTContext &ctx = var->getASTContext();
515514
// A mutable storage of a value type accessed from within the module is
516515
// okay.
517516
if (dyn_cast_or_null<StructDecl>(var->getDeclContext()->getAsDecl()) &&
@@ -841,16 +840,25 @@ SendableCheckContext::preconcurrencyBehavior(Decl *decl) const {
841840
return std::nullopt;
842841

843842
if (auto *nominal = dyn_cast<NominalTypeDecl>(decl)) {
844-
// Determine whether this nominal type is visible via a @preconcurrency
845-
// import.
846-
auto import = nominal->findImport(fromDC);
847-
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();
848853

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

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

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

860868
// When the type is implicitly non-Sendable, `@preconcurrency` suppresses
861869
// diagnostics until the imported module enables Swift 6.
862-
return import->module.importedModule->isConcurrencyChecked()
870+
return importedModule->isConcurrencyChecked()
863871
? DiagnosticBehavior::Warning
864872
: DiagnosticBehavior::Ignore;
865873
}
@@ -5709,7 +5717,7 @@ static bool checkSendableInstanceStorage(
57095717
/*inDerivedConformance*/Type(), element->getLoc(),
57105718
[&](Type type, DiagnosticBehavior behavior) {
57115719
auto preconcurrency =
5712-
context.preconcurrencyBehavior(elementType->getAnyNominal());
5720+
context.preconcurrencyBehavior(type->getAnyNominal());
57135721
if (isImplicitSendableCheck(check)) {
57145722
// If this is for an externally-visible conformance, fail.
57155723
if (check == SendableCheck::ImplicitForExternallyVisible) {
@@ -5720,7 +5728,7 @@ static bool checkSendableInstanceStorage(
57205728
// If we are to ignore this diagnostic, just continue.
57215729
if (behavior == DiagnosticBehavior::Ignore ||
57225730
preconcurrency == DiagnosticBehavior::Ignore)
5723-
return false;
5731+
return true;
57245732

57255733
invalid = true;
57265734
return true;
@@ -6831,3 +6839,14 @@ ActorReferenceResult ActorReferenceResult::forReference(
68316839

68326840
return forEntersActor(declIsolation, options);
68336841
}
6842+
6843+
bool swift::diagnoseNonSendableFromDeinit(
6844+
SourceLoc refLoc, VarDecl *var, DeclContext *dc) {
6845+
return diagnoseIfAnyNonSendableTypes(var->getTypeInContext(),
6846+
SendableCheckContext(dc),
6847+
Type(),
6848+
SourceLoc(),
6849+
refLoc,
6850+
diag::non_sendable_from_deinit,
6851+
var->getDescriptiveKind(), var->getName());
6852+
}

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/concurrent_value_checking.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ func testUnsafeSendableInAsync() async {
215215
// ----------------------------------------------------------------------
216216
// Sendable restriction on key paths.
217217
// ----------------------------------------------------------------------
218-
class NC: Hashable { // expected-note 2{{class 'NC' does not conform to the 'Sendable' protocol}}
218+
class NC: Hashable { // expected-note 3{{class 'NC' does not conform to the 'Sendable' protocol}}
219219
func hash(into: inout Hasher) { }
220220
static func==(_: NC, _: NC) -> Bool { true }
221221
}

test/Concurrency/flow_isolation.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ func takeNonSendable(_ ns: NonSendableType) {}
1515
@available(SwiftStdlib 5.1, *)
1616
func takeSendable(_ s: SendableType) {}
1717

18-
class NonSendableType {
18+
class NonSendableType { // expected-note *{{class 'NonSendableType' does not conform to the 'Sendable' protocol}}
1919
var x: Int = 0
2020
func f() {}
2121
}

test/Concurrency/global_variables.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ final class TestSendable: Sendable {
2323
init() {}
2424
}
2525

26-
final class TestNonsendable { // expected-note 2{{class 'TestNonsendable' does not conform to the 'Sendable' protocol}}
26+
final class TestNonsendable { // expected-note 3{{class 'TestNonsendable' does not conform to the 'Sendable' protocol}}
2727
init() {}
2828
}
2929

test/Concurrency/predates_concurrency_import.swift

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,50 @@
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
3345

3446
let strictGlobal = StrictStruct() // expected-warning{{let 'strictGlobal' is not concurrency-safe because non-'Sendable' type 'StrictStruct' may have shared mutable state}}
3547
// expected-note@-1{{isolate 'strictGlobal' to a global actor, or conform 'StrictStruct' to 'Sendable'}}
48+
49+
extension NonStrictClass {
50+
@Sendable func f() { }
51+
}
52+
53+
extension StrictStruct {
54+
@Sendable func f() { } // expected-warning{{instance method of non-Sendable type 'StrictStruct' cannot be marked as '@Sendable'}}
55+
}
56+
57+
58+
struct HasStatics {
59+
nonisolated static let ns: NonStrictClass = NonStrictClass()
60+
61+
nonisolated static let ss: StrictStruct = StrictStruct()
62+
// expected-warning@-1{{'nonisolated' can not be applied to variable with non-'Sendable' type 'StrictStruct'}}
63+
// expected-warning@-2{{static property 'ss' is not concurrency-safe because non-'Sendable' type 'StrictStruct' may have shared mutable state}}
64+
// expected-note@-3{{isolate 'ss' to a global actor, or conform 'StrictStruct' to 'Sendable'}}
65+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/StrictModule.swiftmodule -module-name StrictModule -swift-version 6 %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+
5+
// RUN: %target-swift-frontend -I %t %s -emit-sil -o /dev/null -verify -strict-concurrency=complete
6+
7+
// REQUIRES: concurrency
8+
// REQUIRES: asserts
9+
10+
@preconcurrency import NonStrictModule
11+
@preconcurrency import StrictModule
12+
13+
@available(SwiftStdlib 5.1, *)
14+
actor ActorWithDeinit {
15+
var ns: NonStrictClass = NonStrictClass()
16+
var ss: StrictStruct = StrictStruct()
17+
18+
deinit {
19+
print(ns)
20+
print(ss) // expected-warning{{cannot access property 'ss' with a non-sendable type 'StrictStruct' from non-isolated deinit}}
21+
}
22+
}

test/Concurrency/predates_concurrency_import_swift6.swift

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/StrictModule.swiftmodule -module-name StrictModule -swift-version 6 %S/Inputs/StrictModule.swift
33
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/NonStrictModule.swiftmodule -module-name NonStrictModule %S/Inputs/NonStrictModule.swift
44

5-
// RUN: %target-swift-frontend -swift-version 6 -I %t %s %s -emit-sil -o /dev/null -verify -parse-as-library
6-
// RUN: %target-swift-frontend -swift-version 6 -I %t %s %s -emit-sil -o /dev/null -verify -enable-upcoming-feature RegionBasedIsolation -parse-as-library
5+
// RUN: %target-swift-frontend -swift-version 6 -I %t %s -emit-sil -o /dev/null -verify -parse-as-library
6+
// RUN: %target-swift-frontend -swift-version 6 -I %t %s -emit-sil -o /dev/null -verify -enable-upcoming-feature RegionBasedIsolation -parse-as-library
77

88
@preconcurrency import NonStrictModule
99
@preconcurrency import StrictModule
@@ -19,3 +19,11 @@ func test(ss: StrictStruct, ns: NonStrictClass) {
1919
let nonStrictGlobal = NonStrictClass()
2020
let strictGlobal = StrictStruct() // expected-warning{{let 'strictGlobal' is not concurrency-safe because non-'Sendable' type 'StrictStruct' may have shared mutable state}}
2121
// expected-note@-1{{isolate 'strictGlobal' to a global actor, or conform 'StrictStruct' to 'Sendable'}}
22+
23+
extension NonStrictClass {
24+
@Sendable func f() { }
25+
}
26+
27+
extension StrictStruct {
28+
@Sendable func f() { } // expected-warning{{instance method of non-Sendable type 'StrictStruct' cannot be marked as '@Sendable'}}
29+
}

0 commit comments

Comments
 (0)