Skip to content

Commit 5a4a9a6

Browse files
authored
Merge pull request #69267 from hborla/stored-property-sendable-violation
[Concurrency] Close a hole that allowed non-`Sendable` types to cross isolation boundaries in initializers.
2 parents 2ca2792 + 6afd38f commit 5a4a9a6

File tree

4 files changed

+41
-9
lines changed

4 files changed

+41
-9
lines changed

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5838,6 +5838,27 @@ ActorReferenceResult ActorReferenceResult::forReference(
58385838
return forSameConcurrencyDomain(declIsolation);
58395839
}
58405840

5841+
// Initializing an actor isolated stored property with a value effectively
5842+
// passes that value from the init context into the actor isolated context.
5843+
// It's only okay for the value to cross isolation boundaries if the property
5844+
// type is Sendable. Note that if the init is a nonisolated actor init,
5845+
// Sendable checking is already performed on arguments at the call-site.
5846+
if ((declIsolation.isActorIsolated() && contextIsolation.isGlobalActor()) ||
5847+
declIsolation.isGlobalActor()) {
5848+
auto *init = dyn_cast<ConstructorDecl>(fromDC);
5849+
auto *decl = declRef.getDecl();
5850+
if (init && init->isDesignatedInit() && isStoredProperty(decl)) {
5851+
auto type =
5852+
fromDC->mapTypeIntoContext(declRef.getDecl()->getInterfaceType());
5853+
if (!isSendableType(fromDC->getParentModule(), type)) {
5854+
// Treat the decl isolation as 'preconcurrency' to downgrade violations
5855+
// to warnings, because violating Sendable here is accepted by the
5856+
// Swift 5.9 compiler.
5857+
return forEntersActor(declIsolation, Flags::Preconcurrency);
5858+
}
5859+
}
5860+
}
5861+
58415862
// If there is an instance and it is checked by flow isolation, treat it
58425863
// as being in the same concurrency domain.
58435864
if (actorInstance &&
@@ -5856,13 +5877,8 @@ ActorReferenceResult ActorReferenceResult::forReference(
58565877

58575878
// If there is an instance that corresponds to 'self',
58585879
// we are in a constructor or destructor, and we have a stored property of
5859-
// global-actor-qualified type, pretend we are in the same concurrency
5860-
// domain.
5861-
// FIXME: This is an odd carve-out that probably shouldn't have been allowed.
5862-
// It should at the very least be diagnosed, and either subsumed by flow
5863-
// isolation or banned outright.
5864-
// FIXME: At the very least, we should consistently use
5865-
// isActorInitOrDeInitContext here, but it only wants to think about actors.
5880+
// global-actor-qualified type, then we have problems if the stored property
5881+
// type is non-Sendable. Note that if we get here, the type must be Sendable.
58665882
if (actorInstance && actorInstance->isSelf() &&
58675883
isNonInheritedStorage(declRef.getDecl(), fromDC) &&
58685884
declIsolation.isGlobalActor() &&

test/Concurrency/actor_isolation.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -824,6 +824,7 @@ actor SomeActorWithInits {
824824
// expected-note@+1 3 {{mutation of this property is only permitted within the actor}}
825825
var mutableState: Int = 17
826826
var otherMutableState: Int
827+
// expected-note@+1 {{mutation of this property is only permitted within the actor}}
827828
let nonSendable: SomeClass
828829

829830
// Sema should not complain about referencing non-sendable members
@@ -887,7 +888,7 @@ actor SomeActorWithInits {
887888
@MainActor init(i5 x: SomeClass) {
888889
self.mutableState = 42
889890
self.otherMutableState = 17
890-
self.nonSendable = x
891+
self.nonSendable = x // expected-warning {{actor-isolated property 'nonSendable' can not be mutated from the main actor; this is an error in Swift 6}}
891892

892893
self.isolated() // expected-warning{{actor-isolated instance method 'isolated()' can not be referenced from the main actor; this is an error in Swift 6}}
893894
self.nonisolated()

test/Concurrency/global_actor_inference.swift

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,17 +324,30 @@ struct WrapperOnActor<Wrapped: Sendable> {
324324
public struct WrapperOnMainActor<Wrapped> {
325325
// Make sure inference of @MainActor on wrappedValue doesn't crash.
326326

327+
// expected-note@+1 {{mutation of this property is only permitted within the actor}}
327328
public var wrappedValue: Wrapped
328329

329330
public var accessCount: Int
330331

331332
nonisolated public init(wrappedValue: Wrapped) {
333+
// expected-warning@+1 {{main actor-isolated property 'wrappedValue' can not be mutated from a non-isolated context; this is an error in Swift 6}}
332334
self.wrappedValue = wrappedValue
333335
}
334336
}
335337

336338
@propertyWrapper
337-
public struct WrapperOnMainActor2<Wrapped> {
339+
public struct WrapperOnMainActorNonSendable<Wrapped> {
340+
// expected-note@+1 {{mutation of this property is only permitted within the actor}}
341+
@MainActor public var wrappedValue: Wrapped
342+
343+
public init(wrappedValue: Wrapped) {
344+
// expected-warning@+1 {{main actor-isolated property 'wrappedValue' can not be mutated from a non-isolated context; this is an error in Swift 6}}
345+
self.wrappedValue = wrappedValue
346+
}
347+
}
348+
349+
@propertyWrapper
350+
public struct WrapperOnMainActorSendable<Wrapped: Sendable> {
338351
@MainActor public var wrappedValue: Wrapped
339352

340353
public init(wrappedValue: Wrapped) {

test/Concurrency/global_actor_inference_swift6.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,13 @@ struct WrapperOnActor<Wrapped: Sendable> {
6969
public struct WrapperOnMainActor<Wrapped> {
7070
// Make sure inference of @MainActor on wrappedValue doesn't crash.
7171

72+
// expected-note@+1 {{mutation of this property is only permitted within the actor}}
7273
public var wrappedValue: Wrapped // expected-note {{property declared here}}
7374

7475
public var accessCount: Int
7576

7677
nonisolated public init(wrappedValue: Wrapped) {
78+
// expected-error@+1 {{main actor-isolated property 'wrappedValue' can not be mutated from a non-isolated context}}
7779
self.wrappedValue = wrappedValue
7880
}
7981
}

0 commit comments

Comments
 (0)