Skip to content

Commit c98d93d

Browse files
authored
Merge pull request #70909 from hborla/nonisolated-nonsendable
2 parents 0dc8fbd + 009d7d0 commit c98d93d

11 files changed

+78
-46
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5602,9 +5602,6 @@ WARNING(non_sendable_property_type,none,
56025602
WARNING(non_sendable_keypath_capture,none,
56035603
"cannot form key path that captures non-sendable type %0",
56045604
(Type))
5605-
WARNING(non_sendable_keypath_access,none,
5606-
"cannot form key path that accesses non-sendable type %0",
5607-
(Type))
56085605
ERROR(non_concurrent_type_member,none,
56095606
"%select{stored property %2|associated value %2}1 of "
56105607
"'Sendable'-conforming %kind3 has non-sendable type %0",
@@ -5668,6 +5665,10 @@ NOTE(nonisolated_mutable_storage_note,none,
56685665
"convert %0 to a 'let' constant or consider declaring it "
56695666
"'nonisolated(unsafe)' if manually managing concurrency safety",
56705667
(const VarDecl *))
5668+
ERROR(nonisolated_non_sendable,none,
5669+
"'nonisolated' can not be applied to variable with non-'Sendable' "
5670+
"type %0",
5671+
(Type))
56715672
ERROR(nonisolated_local_var,none,
56725673
"'nonisolated' can not be applied to local variables",
56735674
())

lib/SILOptimizer/Mandatory/FlowIsolation.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -497,9 +497,8 @@ static bool accessIsConcurrencySafe(ModuleDecl *module,
497497
RefElementAddrInst *inst) {
498498
VarDecl *var = inst->getField();
499499

500-
// must be accessible from nonisolated and Sendable
501-
return isLetAccessibleAnywhere(module, var)
502-
&& var->getTypeInContext()->isSendableType();
500+
// must be accessible from nonisolated.
501+
return isLetAccessibleAnywhere(module, var);
503502
}
504503

505504
/// \returns true iff the ref_element_addr instruction is only used

lib/Sema/TypeCheckAttr.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6880,6 +6880,17 @@ void AttributeChecker::visitNonisolatedAttr(NonisolatedAttr *attr) {
68806880
return;
68816881
}
68826882

6883+
// 'nonisolated' without '(unsafe)' is not allowed on non-Sendable variables.
6884+
auto type = var->getTypeInContext();
6885+
if (!attr->isUnsafe() && !type->hasError() &&
6886+
!type->isSendableType()) {
6887+
Ctx.Diags.diagnose(attr->getLocation(),
6888+
diag::nonisolated_non_sendable,
6889+
type)
6890+
.warnUntilSwiftVersion(6);
6891+
return;
6892+
}
6893+
68836894
if (auto nominal = dyn_cast<NominalTypeDecl>(dc)) {
68846895
// 'nonisolated' can not be applied to stored properties inside
68856896
// distributed actors. Attempts of nonisolated access would be

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -543,8 +543,22 @@ static bool varIsSafeAcrossActors(const ModuleDecl *fromModule,
543543
return false;
544544
}
545545

546+
bool accessWithinModule =
547+
(fromModule == var->getDeclContext()->getParentModule());
548+
549+
// If the type is not 'Sendable', it's unsafe
550+
if (!var->getTypeInContext()->isSendableType()) {
551+
// Compiler versions <= 5.10 treated this variable as nonisolated,
552+
// so downgrade async access errors in the effects checker to
553+
// warnings prior to Swift 6.
554+
if (accessWithinModule)
555+
options = ActorReferenceResult::Flags::Preconcurrency;
556+
557+
return false;
558+
}
559+
546560
// If it's actor-isolated but in the same module, then it's OK too.
547-
return (fromModule == var->getDeclContext()->getParentModule());
561+
return accessWithinModule;
548562
}
549563
}
550564

@@ -3639,34 +3653,26 @@ namespace {
36393653
LLVM_FALLTHROUGH;
36403654
}
36413655

3642-
case ActorIsolation::ActorInstance:
3643-
// If this entity is always accessible across actors, just check
3644-
// Sendable.
3656+
case ActorIsolation::ActorInstance: {
3657+
ActorReferenceResult::Options options = llvm::None;
36453658
if (isAccessibleAcrossActors(decl, isolation, getDeclContext(),
3646-
llvm::None)) {
3647-
if (diagnoseNonSendableTypes(
3648-
component.getComponentType(), getDeclContext(),
3649-
/*inDerivedConformance*/Type(),
3650-
component.getLoc(),
3651-
diag::non_sendable_keypath_access)) {
3652-
diagnosed = true;
3653-
}
3659+
options)) {
36543660
break;
36553661
}
36563662

3657-
{
3658-
auto diagnostic = ctx.Diags.diagnose(
3659-
component.getLoc(), diag::actor_isolated_keypath_component,
3660-
isolation, decl);
3663+
bool downgrade = isolation.isGlobalActor() ||
3664+
options.contains(
3665+
ActorReferenceResult::Flags::Preconcurrency);
36613666

3662-
if (isolation == ActorIsolation::ActorInstance)
3663-
diagnosed = true;
3664-
else
3665-
diagnostic.warnUntilSwiftVersion(6);
3666-
}
3667+
ctx.Diags.diagnose(
3668+
component.getLoc(), diag::actor_isolated_keypath_component,
3669+
isolation, decl)
3670+
.warnUntilSwiftVersionIf(downgrade, 6);
36673671

3672+
diagnosed = !downgrade;
36683673
break;
36693674
}
3675+
}
36703676
}
36713677

36723678
// With `InferSendableFromCaptures` feature enabled the solver is

lib/Sema/TypeCheckEffects.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1040,6 +1040,7 @@ class ApplyClassifier {
10401040

10411041
public:
10421042
ASTContext &Ctx;
1043+
DeclContext *DC = nullptr;
10431044
DeclContext *RethrowsDC = nullptr;
10441045
DeclContext *ReasyncDC = nullptr;
10451046

@@ -1083,9 +1084,12 @@ class ApplyClassifier {
10831084

10841085
if (auto *var = dyn_cast<VarDecl>(decl)) {
10851086
ActorReferenceResult::Options options = llvm::None;
1086-
// The newly-diagnosed cases are invalid regardless of the module context
1087-
// of the caller, i.e. isolated static and global 'let' variables.
1088-
auto *module = var->getDeclContext()->getParentModule();
1087+
ModuleDecl *module;
1088+
if (DC != nullptr) {
1089+
module = DC->getParentModule();
1090+
} else {
1091+
module = var->getDeclContext()->getParentModule();
1092+
}
10891093
if (!isLetAccessibleAnywhere(module, var, options)) {
10901094
return options.contains(ActorReferenceResult::Flags::Preconcurrency);
10911095
}
@@ -3064,6 +3068,7 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
30643068

30653069
ApplyClassifier getApplyClassifier() const {
30663070
ApplyClassifier classifier(Ctx);
3071+
classifier.DC = CurContext.getDeclContext();
30673072
classifier.RethrowsDC = RethrowsDC;
30683073
classifier.ReasyncDC = ReasyncDC;
30693074
return classifier;

test/Concurrency/actor_isolation.swift

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,9 @@ func checkAsyncPropertyAccess() async {
114114

115115
act.text[0] += "hello" // expected-error{{actor-isolated property 'text' can not be mutated from a non-isolated context}}
116116

117-
_ = act.point // expected-warning{{non-sendable type 'Point' in asynchronous access to actor-isolated property 'point' cannot cross actor boundary}}
117+
_ = act.point // expected-warning{{non-sendable type 'Point' in implicitly asynchronous access to actor-isolated property 'point' cannot cross actor boundary}}
118+
// expected-warning@-1 {{expression is 'async' but is not marked with 'await'}}
119+
// expected-note@-2 {{property access is 'async'}}
118120
}
119121

120122
/// ------------------------------------------------------------------
@@ -1538,11 +1540,11 @@ class OverridesNonsiolatedInit: SuperWithNonisolatedInit {
15381540
}
15391541
}
15401542

1541-
// expected-note@+1 2 {{class 'NonSendable' does not conform to the 'Sendable' protocol}}
1543+
// expected-note@+1 {{class 'NonSendable' does not conform to the 'Sendable' protocol}}
15421544
class NonSendable {}
15431545

15441546
actor ProtectNonSendable {
1545-
// expected-note@+1 {{property declared here}}
1547+
// expected-note@+1 2 {{property declared here}}
15461548
let ns = NonSendable()
15471549

15481550
init() {}
@@ -1560,7 +1562,9 @@ class ReferenceActor {
15601562
init() async {
15611563
self.a = ProtectNonSendable()
15621564

1563-
// expected-warning@+1 {{non-sendable type 'NonSendable' in asynchronous access to actor-isolated property 'ns' cannot cross actor boundary}}
1565+
// expected-warning@+3 {{non-sendable type 'NonSendable' in implicitly asynchronous access to actor-isolated property 'ns' cannot cross actor boundary}}
1566+
// expected-warning@+2 {{expression is 'async' but is not marked with 'await'}}
1567+
// expected-note@+1 {{property access is 'async'}}
15641568
_ = a.ns
15651569
}
15661570
}
@@ -1571,7 +1575,7 @@ actor AnotherActor {
15711575
init() {
15721576
self.a = ProtectNonSendable()
15731577

1574-
// expected-warning@+1 {{non-sendable type 'NonSendable' in asynchronous access to actor-isolated property 'ns' cannot cross actor boundary}}
1578+
// expected-warning@+1 {{actor-isolated property 'ns' can not be referenced from a non-isolated context}}
15751579
_ = a.ns
15761580
}
15771581
}

test/Concurrency/actor_keypath_isolation.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
// REQUIRES: concurrency
55
// REQUIRES: asserts
66

7-
class Box { // expected-note 3{{class 'Box' does not conform to the 'Sendable' protocol}}
7+
class Box {
88
let size : Int = 0
99
}
1010

@@ -48,7 +48,7 @@ func tryKeyPathsMisc(d : Door) {
4848

4949
// in combination with other key paths
5050

51-
_ = (\Door.letBox).appending(path: // expected-warning {{cannot form key path that accesses non-sendable type 'Box?'}}
51+
_ = (\Door.letBox).appending(path: // expected-warning {{cannot form key path to actor-isolated property 'letBox'; this is an error in Swift 6}}
5252
\Box?.?.size)
5353

5454
_ = (\Door.varBox).appending(path: // expected-error {{cannot form key path to actor-isolated property 'varBox'}}
@@ -62,9 +62,9 @@ func tryKeyPathsFromAsync() async {
6262
}
6363

6464
func tryNonSendable() {
65-
_ = \Door.letDict[0] // expected-warning {{cannot form key path that accesses non-sendable type '[Int : Box]'}}
65+
_ = \Door.letDict[0] // expected-warning {{cannot form key path to actor-isolated property 'letDict'; this is an error in Swift 6}}
6666
_ = \Door.varDict[0] // expected-error {{cannot form key path to actor-isolated property 'varDict'}}
67-
_ = \Door.letBox!.size // expected-warning {{cannot form key path that accesses non-sendable type 'Box?'}}
67+
_ = \Door.letBox!.size // expected-warning {{cannot form key path to actor-isolated property 'letBox'; this is an error in Swift 6}}
6868
}
6969

7070
func tryKeypaths() {

test/Concurrency/actor_keypath_isolation_swift6.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
// REQUIRES: concurrency && asserts
55

6-
class Box { // expected-note 3{{class 'Box' does not conform to the 'Sendable' protocol}}
6+
class Box {
77
let size : Int = 0
88
}
99

@@ -47,7 +47,7 @@ func tryKeyPathsMisc(d : Door) {
4747

4848
// in combination with other key paths
4949

50-
_ = (\Door.letBox).appending(path: // expected-warning {{cannot form key path that accesses non-sendable type 'Box?'}}
50+
_ = (\Door.letBox).appending(path: // expected-error {{cannot form key path to actor-isolated property 'letBox'}}
5151
\Box?.?.size)
5252

5353
_ = (\Door.varBox).appending(path: // expected-error {{cannot form key path to actor-isolated property 'varBox'}}
@@ -61,9 +61,9 @@ func tryKeyPathsFromAsync() async {
6161
}
6262

6363
func tryNonSendable() {
64-
_ = \Door.letDict[0] // expected-warning {{cannot form key path that accesses non-sendable type '[Int : Box]'}}
64+
_ = \Door.letDict[0] // expected-error {{cannot form key path to actor-isolated property 'letDict'}}
6565
_ = \Door.varDict[0] // expected-error {{cannot form key path to actor-isolated property 'varDict'}}
66-
_ = \Door.letBox!.size // expected-warning {{cannot form key path that accesses non-sendable type 'Box?'}}
66+
_ = \Door.letBox!.size // expected-error {{cannot form key path to actor-isolated property 'letBox'}}
6767
}
6868

6969
func tryKeypaths() {

test/Concurrency/concurrent_value_checking.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,9 @@ extension A1 {
8686
_ = await self.asynchronous(nil)
8787

8888
// Across to a different actor, so Sendable restriction is enforced.
89-
_ = other.localLet // expected-warning{{non-sendable type 'NotConcurrent' in asynchronous access to actor-isolated property 'localLet' cannot cross actor boundary}}
89+
_ = other.localLet // expected-warning{{non-sendable type 'NotConcurrent' in implicitly asynchronous access to actor-isolated property 'localLet' cannot cross actor boundary}}
90+
// expected-warning@-1 {{expression is 'async' but is not marked with 'await'}}
91+
// expected-note@-2 {{property access is 'async'}}
9092
_ = await other.synchronous() // expected-warning{{non-sendable type 'NotConcurrent?' returned by call to actor-isolated function cannot cross actor boundary}}
9193
_ = await other.asynchronous(nil) // expected-complete-warning{{passing argument of non-sendable type 'NotConcurrent?' into actor-isolated context may introduce data races}}
9294
}
@@ -230,7 +232,7 @@ func testKeyPaths(dict: [NC: Int], nc: NC) {
230232
// Sendable restriction on nonisolated declarations.
231233
// ----------------------------------------------------------------------
232234
actor ANI {
233-
nonisolated let nc = NC()
235+
nonisolated let nc = NC() // expected-warning {{'nonisolated' can not be applied to variable with non-'Sendable' type 'NC'; this is an error in Swift 6}}
234236
nonisolated func f() -> NC? { nil }
235237
}
236238

test/Concurrency/flow_isolation.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,19 +140,22 @@ actor ExampleFromProposal {
140140
let immutableSendable = SendableType()
141141
var mutableSendable = SendableType()
142142
let nonSendable = NonSendableType()
143+
nonisolated(unsafe) let unsafeNonSendable = NonSendableType()
143144
var nsItems: [NonSendableType] = []
144145
var sItems: [SendableType] = []
145146

146147
init() {
147148
_ = self.immutableSendable // ok
148149
_ = self.mutableSendable // ok
149150
_ = self.nonSendable // ok
151+
_ = self.unsafeNonSendable
150152

151153
f() // expected-note 2 {{after calling instance method 'f()', only non-isolated properties of 'self' can be accessed from this init}}
152154

153155
_ = self.immutableSendable // ok
154156
_ = self.mutableSendable // expected-warning {{cannot access property 'mutableSendable' here in non-isolated initializer; this is an error in Swift 6}}
155157
_ = self.nonSendable // expected-warning {{cannot access property 'nonSendable' here in non-isolated initializer; this is an error in Swift 6}}
158+
_ = self.unsafeNonSendable // ok
156159
}
157160

158161

test/Concurrency/global_variables.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// RUN: %empty-directory(%t)
22
// 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 -swift-version 6 -I %t %s %s -emit-sil -o /dev/null -verify %s
3+
// RUN: %target-swift-frontend -disable-availability-checking -parse-as-library -swift-version 6 -I %t %s -emit-sil -o /dev/null -verify %s
44

55
// REQUIRES: concurrency
66
// REQUIRES: asserts
@@ -43,6 +43,7 @@ struct TestStatics {
4343
static let immutableNonsendable = TestNonsendable() // expected-error{{static property 'immutableNonsendable' is not concurrency-safe because it is not either conforming to 'Sendable' or isolated to a global actor}}
4444
static nonisolated(unsafe) let immutableNonisolatedUnsafe = TestNonsendable()
4545
static nonisolated let immutableNonisolated = TestNonsendable() // expected-error{{static property 'immutableNonisolated' is not concurrency-safe because it is not either conforming to 'Sendable' or isolated to a global actor}}
46+
// expected-error@-1 {{'nonisolated' can not be applied to variable with non-'Sendable' type 'TestNonsendable'}}
4647
static let immutableInferredSendable = 0
4748
static var mutable = 0 // expected-error{{static property 'mutable' is not concurrency-safe because it is non-isolated global shared mutable state}}
4849
// expected-note@-1{{isolate 'mutable' to a global actor, or convert it to a 'let' constant and conform it to 'Sendable'}}

0 commit comments

Comments
 (0)