[Concurrency] Allow explicit 'nonisolated' on lazy properties and properties with property wrappers if they are a member of a non-'Sendable' type.

This commit is contained in:
Sima Nerush
2025-03-17 12:51:07 -07:00
parent 8f7af45115
commit 0f7d39dae8
8 changed files with 68 additions and 62 deletions

View File

@@ -6068,12 +6068,6 @@ ERROR(nonisolated_local_var,none,
ERROR(nonisolated_actor_sync_init,none,
"'nonisolated' on an actor's synchronous initializer is invalid",
())
ERROR(nonisolated_wrapped_property,none,
"'nonisolated' is not supported on properties with property wrappers",
())
ERROR(nonisolated_lazy,none,
"'nonisolated' is not supported on lazy properties",
())
ERROR(non_sendable_from_deinit,none,
"cannot access %kind1 with a non-Sendable type %0 from nonisolated "

View File

@@ -7787,31 +7787,50 @@ void AttributeChecker::visitNonisolatedAttr(NonisolatedAttr *attr) {
if (auto var = dyn_cast<VarDecl>(D)) {
// stored properties have limitations as to when they can be nonisolated.
auto type = var->getTypeInContext();
if (var->hasStorage()) {
if (var->hasStorage() || var->hasAttachedPropertyWrapper() ||
var->getAttrs().hasAttribute<LazyAttr>()) {
{
// A stored property can be 'nonisolated' if it is a 'Sendable' member
// of a 'Sendable' value type.
// The above rule does not apply to lazy properties and properties with
// property wrappers, because backing storage is a stored
// 'var' that is part of the internal state of the actor which could
// only be accessed in actor's isolation context.
bool canBeNonisolated = false;
if (auto nominal = dc->getSelfStructDecl()) {
if (nominal->getDeclaredTypeInContext()->isSendableType() &&
if (var->hasStorage() &&
nominal->getDeclaredTypeInContext()->isSendableType() &&
!var->isStatic() && type->isSendableType()) {
canBeNonisolated = true;
}
}
// Additionally, a stored property of a non-'Sendable' type can be
// explicitly marked 'nonisolated'.
if (auto parentDecl = dc->getDeclaredTypeInContext())
if (!parentDecl->isSendableType()) {
canBeNonisolated = true;
}
// Additionally, a stored property of a non-'Sendable' type can be
// explicitly marked 'nonisolated'.
if (auto parentDecl = dc->getDeclaredTypeInContext())
if (!parentDecl->isSendableType()) {
canBeNonisolated = true;
}
// Otherwise, this stored property has to be qualified as 'unsafe'.
if (var->supportsMutation() && !attr->isUnsafe() && !canBeNonisolated) {
diagnoseAndRemoveAttr(attr, diag::nonisolated_mutable_storage)
if (var->hasAttachedPropertyWrapper()) {
diagnoseAndRemoveAttr(attr, diag::nonisolated_mutable_storage)
.warnUntilSwiftVersionIf(attr->isImplicit(), 6)
.fixItInsertAfter(attr->getRange().End, "(unsafe)");
var->diagnose(diag::nonisolated_mutable_storage_note, var);
return;
return;
} else if (var->getAttrs().hasAttribute<LazyAttr>()) {
diagnoseAndRemoveAttr(attr, diag::nonisolated_mutable_storage)
.warnUntilSwiftVersion(6)
.fixItInsertAfter(attr->getRange().End, "(unsafe)");
return;
} else {
diagnoseAndRemoveAttr(attr, diag::nonisolated_mutable_storage)
.fixItInsertAfter(attr->getRange().End, "(unsafe)");
if (var->hasStorage())
var->diagnose(diag::nonisolated_mutable_storage_note, var);
return;
}
}
// 'nonisolated' without '(unsafe)' is not allowed on non-Sendable
@@ -7877,22 +7896,6 @@ void AttributeChecker::visitNonisolatedAttr(NonisolatedAttr *attr) {
}
}
// Using 'nonisolated' with lazy properties and wrapped properties is
// unsupported, because backing storage is a stored 'var' that is part
// of the internal state of the actor which could only be accessed in
// actor's isolation context.
if (var->hasAttachedPropertyWrapper()) {
diagnoseAndRemoveAttr(attr, diag::nonisolated_wrapped_property)
.warnUntilSwiftVersionIf(attr->isImplicit(), 6);
return;
}
if (var->getAttrs().hasAttribute<LazyAttr>()) {
diagnose(attr->getLocation(), diag::nonisolated_lazy)
.warnUntilSwiftVersion(6);
return;
}
// nonisolated can not be applied to local properties unless qualified as
// 'unsafe'.
if (dc->isLocalContext() && !attr->isUnsafe()) {

View File

@@ -813,31 +813,29 @@ actor LazyActor {
lazy var l25: Int = { [unowned self] in self.l }()
nonisolated lazy var l31: Int = { v }()
// expected-warning@-1 {{'nonisolated' is not supported on lazy properties; this is an error in the Swift 6 language mode}}
// expected-warning@-1 {{'nonisolated' cannot be applied to mutable stored properties; this is an error in the Swift 6 language mode}}
// expected-warning@-2 {{actor-isolated default value in a nonisolated context; this is an error in the Swift 6 language mode}}
nonisolated lazy var l32: Int = v
// expected-warning@-1 {{'nonisolated' is not supported on lazy properties; this is an error in the Swift 6 language mode}}
// expected-warning@-2 {{actor-isolated default value in a nonisolated context; this is an error in the Swift 6 language mode}}
// expected-warning@-1 {{'nonisolated' cannot be applied to mutable stored properties; this is an error in the Swift 6 language mode}}
nonisolated lazy var l33: Int = { self.v }()
// expected-warning@-1 {{'nonisolated' is not supported on lazy properties; this is an error in the Swift 6 language mode}}
// expected-warning@-1 {{'nonisolated' cannot be applied to mutable stored properties; this is an error in the Swift 6 language mode}}
// expected-warning@-2 {{actor-isolated default value in a nonisolated context; this is an error in the Swift 6 language mode}}
nonisolated lazy var l34: Int = self.v
// expected-warning@-1 {{'nonisolated' is not supported on lazy properties; this is an error in the Swift 6 language mode}}
// expected-warning@-2 {{actor-isolated default value in a nonisolated context; this is an error in the Swift 6 language mode}}
// expected-warning@-1 {{'nonisolated' cannot be applied to mutable stored properties; this is an error in the Swift 6 language mode}}
nonisolated lazy var l35: Int = { [unowned self] in self.v }()
// expected-warning@-1 {{'nonisolated' is not supported on lazy properties; this is an error in the Swift 6 language mode}}
// expected-warning@-1 {{'nonisolated' cannot be applied to mutable stored properties; this is an error in the Swift 6 language mode}}
// expected-warning@-2 {{actor-isolated default value in a nonisolated context; this is an error in the Swift 6 language mode}}
nonisolated lazy var l41: Int = { l }()
// expected-warning@-1 {{'nonisolated' is not supported on lazy properties; this is an error in the Swift 6 language mode}}
// expected-warning@-1 {{'nonisolated' cannot be applied to mutable stored properties; this is an error in the Swift 6 language mode}}
nonisolated lazy var l42: Int = l
// expected-warning@-1 {{'nonisolated' is not supported on lazy properties; this is an error in the Swift 6 language mode}}
// expected-warning@-1 {{'nonisolated' cannot be applied to mutable stored properties; this is an error in the Swift 6 language mode}}
nonisolated lazy var l43: Int = { self.l }()
// expected-warning@-1 {{'nonisolated' is not supported on lazy properties; this is an error in the Swift 6 language mode}}
// expected-warning@-1 {{'nonisolated' cannot be applied to mutable stored properties; this is an error in the Swift 6 language mode}}
nonisolated lazy var l44: Int = self.l
// expected-warning@-1 {{'nonisolated' is not supported on lazy properties; this is an error in the Swift 6 language mode}}
// expected-warning@-1 {{'nonisolated' cannot be applied to mutable stored properties; this is an error in the Swift 6 language mode}}
nonisolated lazy var l45: Int = { [unowned self] in self.l }()
// expected-warning@-1 {{'nonisolated' is not supported on lazy properties; this is an error in the Swift 6 language mode}}
// expected-warning@-1 {{'nonisolated' cannot be applied to mutable stored properties; this is an error in the Swift 6 language mode}}
}
// Infer global actors from context only for instance members.

View File

@@ -65,5 +65,7 @@ actor Pumpkin: NSObject {}
actor Bad {
@objc nonisolated lazy var invalid = 0
// expected-warning@-1 {{'nonisolated' is not supported on lazy properties; this is an error in the Swift 6 language mode}}
// expected-warning@-1 {{'nonisolated' cannot be applied to mutable stored properties; this is an error in the Swift 6 language mode}}
// expected-error@-2 {{actor-isolated setter for property 'invalid' cannot be @objc}}
// expected-error@-3 {{actor-isolated getter for property 'invalid' cannot be @objc}}
}

View File

@@ -400,8 +400,7 @@ struct HasWrapperOnActor {
synced = 17
}
@WrapperActor var actorSynced: Int = 0 // expected-warning{{'nonisolated' is not supported on properties with property wrappers}}
@WrapperActor var actorSynced: Int = 0 // expected-warning {{'nonisolated' cannot be applied to mutable stored properties}}
func testActorSynced() {
_ = actorSynced
_ = $actorSynced
@@ -499,9 +498,8 @@ struct SimplePropertyWrapper {
@MainActor
class WrappedContainsNonisolatedAttr {
@SimplePropertyWrapper nonisolated var value
// expected-error@-1 {{'nonisolated' is not supported on properties with property wrappers}}
// expected-note@-2 {{property declared here}}
@SimplePropertyWrapper nonisolated var value
// expected-note@-1 {{property declared here}}
nonisolated func test() {
_ = value

View File

@@ -115,7 +115,7 @@ struct HasWrapperOnActor {
synced = 17
}
@WrapperActor var actorSynced: Int = 0 // expected-error{{'nonisolated' is not supported on properties with property wrappers}}
@WrapperActor var actorSynced: Int = 0 // expected-error {{'nonisolated' cannot be applied to mutable stored properties}}
func testActorSynced() {
_ = actorSynced

View File

@@ -36,8 +36,8 @@ struct ImplicitlySendable {
nonisolated var d = 0
// never okay
nonisolated lazy var e = 0 // expected-error {{'nonisolated' is not supported on lazy properties}}
@P nonisolated var f = 0 // expected-error {{'nonisolated' is not supported on properties with property wrappers}}
nonisolated lazy var e = 0 // expected-error {{'nonisolated' cannot be applied to mutable stored properties}}
@P nonisolated var f = 0 // expected-error {{'nonisolated' cannot be applied to mutable stored properties}}
}
struct ImplicitlyNonSendable {
@@ -48,9 +48,9 @@ struct ImplicitlyNonSendable {
nonisolated var c: Int { 0 }
nonisolated var d = 0
// never okay
nonisolated lazy var e = 0 // expected-error {{'nonisolated' is not supported on lazy properties}}
@P nonisolated var f = 0 // expected-error {{'nonisolated' is not supported on properties with property wrappers}}
// okay, the type is non-'Sendable'
nonisolated lazy var e = 0
@P nonisolated var f = 0
}
public struct PublicSendable: Sendable {
@@ -60,8 +60,8 @@ public struct PublicSendable: Sendable {
nonisolated var d = 0
// never okay
nonisolated lazy var e = 0 // expected-error {{'nonisolated' is not supported on lazy properties}}
@P nonisolated var f = 0 // expected-error {{'nonisolated' is not supported on properties with property wrappers}}
nonisolated lazy var e = 0 // expected-error {{'nonisolated' cannot be applied to mutable stored properties}}
@P nonisolated var f = 0 // expected-error {{'nonisolated' cannot be applied to mutable stored properties}}
}
public struct PublicNonSendable {
@@ -70,9 +70,9 @@ public struct PublicNonSendable {
nonisolated var c: Int { 0 }
nonisolated var d = 0
// never okay
nonisolated lazy var e = 0 // expected-error {{'nonisolated' is not supported on lazy properties}}
@P nonisolated var f = 0 // expected-error {{'nonisolated' is not supported on properties with property wrappers}}
// okay, the type is non-'Sendable'
nonisolated lazy var e = 0
@P nonisolated var f = 0
}
@@ -96,6 +96,8 @@ nonisolated struct StructRemovesGlobalActor: GloballyIsolated {
@MainActor struct S {
var value: NonSendable // globally-isolated
@P nonisolated var x = 0 // expected-error {{'nonisolated' cannot be applied to mutable stored properties}}
nonisolated lazy var y = 1 // expected-error {{'nonisolated' cannot be applied to mutable stored properties}}
struct Nested {} // 'Nested' is not @MainActor-isolated
}
@@ -104,6 +106,8 @@ nonisolated struct StructRemovesGlobalActor: GloballyIsolated {
nonisolated struct S1: GloballyIsolated {
var x: NonSendable
@P nonisolated var y = 0 // okay
nonisolated lazy var z = 1 // okay
func f() {
// expected-error@+1 {{call to main actor-isolated global function 'requireMain()' in a synchronous nonisolated context}}
requireMain()

View File

@@ -10,6 +10,10 @@ import FakeDistributedActorSystems
@available(SwiftStdlib 5.5, *)
typealias DefaultDistributedActorSystem = FakeActorSystem
@propertyWrapper struct P {
var wrappedValue = 0
}
distributed actor DA {
let local: Int = 42
@@ -49,6 +53,9 @@ distributed actor DA {
fatalError()
}
nonisolated lazy var a = 0 // expected-error {{'nonisolated' cannot be applied to mutable stored properties}}
@P nonisolated var b = 0 // expected-error {{'nonisolated' cannot be applied to mutable stored properties}}
}
func invalidIsolatedCall<DA: DistributedActor> (