From 39c7962661b09ccc1b13440dbda8853755cebcb7 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Thu, 25 Apr 2024 22:28:16 -0700 Subject: [PATCH] [Concurrency] Only treat 'nonisolated let' properties in actors as isolated if access is from outside the module or the property type is not 'Sendable'. --- lib/Sema/TypeCheckConcurrency.cpp | 11 +++++++++-- test/Concurrency/actor_isolation.swift | 23 ++++++++++++++++++++--- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index 100116297c934..cf76d8f8cfdee 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -6465,12 +6465,19 @@ static ActorIsolation getActorIsolationForReference(ValueDecl *decl, // Fall through to treat initializers like any other declaration. } - // A 'nonisolated let' within an actor is treated as isolated from the - // perspective of the referencer. + // A 'nonisolated let' within an actor is treated as isolated if + // the access is outside the module or if the property type is not + // 'Sendable'. // // FIXME: getActorIsolation(decl) should treat these as isolated. // FIXME: Expand this out to local variables? if (auto var = dyn_cast(decl)) { + auto *fromModule = fromDC->getParentModule(); + ActorReferenceResult::Options options = std::nullopt; + if (varIsSafeAcrossActors(fromModule, var, declIsolation, options) && + var->getTypeInContext()->isSendableType()) + return ActorIsolation::forNonisolated(/*unsafe*/false); + if (var->isLet() && isStoredProperty(var) && declIsolation.isNonisolated()) { if (auto nominal = var->getDeclContext()->getSelfNominalTypeDecl()) { diff --git a/test/Concurrency/actor_isolation.swift b/test/Concurrency/actor_isolation.swift index e1493c22197b5..123155d4874f4 100644 --- a/test/Concurrency/actor_isolation.swift +++ b/test/Concurrency/actor_isolation.swift @@ -156,10 +156,11 @@ func checkIsolationValueType(_ formance: InferredFromConformance, _ anno: NoGlobalActorValueType) async { // these still do need an await in Swift 5 _ = await ext.point // expected-warning {{non-sendable type 'Point' in implicitly asynchronous access to main actor-isolated property 'point' cannot cross actor boundary}} - _ = formance.counter _ = await anno.point // expected-warning {{non-sendable type 'Point' in implicitly asynchronous access to global actor 'SomeGlobalActor'-isolated property 'point' cannot cross actor boundary}} // expected-warning@-1 {{non-sendable type 'NoGlobalActorValueType' passed in implicitly asynchronous call to global actor 'SomeGlobalActor'-isolated property 'point' cannot cross actor boundary}} - _ = anno.counter // expected-warning {{non-sendable type 'NoGlobalActorValueType' passed in call to main actor-isolated property 'counter' cannot cross actor boundary}} + + _ = formance.counter + _ = anno.counter // these will always need an await _ = await (formance as MainCounter).counter // expected-warning {{non-sendable type 'any MainCounter' passed in implicitly asynchronous call to main actor-isolated property 'counter' cannot cross actor boundary}} @@ -171,7 +172,7 @@ func checkIsolationValueType(_ formance: InferredFromConformance, } // expected-warning@+2 {{memberwise initializer for 'NoGlobalActorValueType' cannot be both nonisolated and global actor 'SomeGlobalActor'-isolated; this is an error in the Swift 6 language mode}} -// expected-note@+1 2 {{consider making struct 'NoGlobalActorValueType' conform to the 'Sendable' protocol}} +// expected-note@+1 {{consider making struct 'NoGlobalActorValueType' conform to the 'Sendable' protocol}} struct NoGlobalActorValueType { @SomeGlobalActor var point: Point // expected-note@-1 {{initializer for property 'point' is global actor 'SomeGlobalActor'-isolated}} @@ -1636,3 +1637,19 @@ nonisolated func accessAcrossActors() { // expected-warning@+1 {{main actor-isolated static property 'shared' can not be referenced from a non-isolated context; this is an error in the Swift 6 language mode}} let _ = MainActorIsolated.shared } + +@available(SwiftStdlib 5.1, *) +actor Iterator: AsyncIteratorProtocol { + init() {} + func next() throws -> Int? { nil } +} + +@available(SwiftStdlib 5.1, *) +actor SafeMutatingCall { + private let iterator: Iterator + + public init() async throws { + self.iterator = Iterator() + _ = try await self.iterator.next() + } +}