[SILGen] Respect stored property's explicit global actor isolation in its initializer

Previously, when generating SIL for a stored property initializer, we would
use the enclosing context's isolation rather than the property's own explicit
isolation. For example:

    @MainActor
    struct S {
      @CustomActor var x = NS()  // initializer was incorrectly MainActor-isolated
    }

Now the initializer correctly uses the property's explicit @CustomActor isolation
instead of inheriting @MainActor from the struct.

This is implemented by checking for an explicit global actor isolation on the
VarDecl in SILDeclRef::getActorIsolation() before falling back to
getActorIsolationOfContext().
This commit is contained in:
Michael Gottesman
2026-02-16 09:36:51 -08:00
parent 9d4478bebc
commit 0a60c93ca5
2 changed files with 115 additions and 0 deletions
+8
View File
@@ -2067,5 +2067,13 @@ ActorIsolation SILDeclRef::getActorIsolation() const {
return param->getInitializerIsolation();
}
// If we have a stored property initializer for a VarDecl with an explicit
// isolation, match that explicit isolation.
if (isStoredPropertyInitializer()) {
if (auto isolation = swift::getActorIsolation(cast<VarDecl>(getDecl()));
isolation && isolation.isGlobalActor())
return isolation;
}
return getActorIsolationOfContext(getInnermostDeclContext());
}
@@ -11,6 +11,15 @@
func useValueAsync<T>(_ t: T) async {}
actor CustomActorInstance {}
@globalActor
struct CustomActor {
static let shared = CustomActorInstance()
}
class NonSendableKlass {}
/////////////////
// MARK: Tests //
/////////////////
@@ -32,3 +41,101 @@ func useValueAsync<T>(_ t: T) async {}
await useValueAsync(erased) // expected-error {{sending 'erased' risks causing data races}}
// expected-note @-1 {{sending main actor-isolated 'erased' to nonisolated global function 'useValueAsync' risks causing data races between nonisolated and main actor-isolated uses}}
}
@MainActor
struct MainActorStruct {
// CHECK: variable initialization expression of MainActorStruct.mainActorField
// CHECK-NEXT: Isolation: global_actor. type: MainActor
// CHECK: MainActorStruct.mainActorField.getter
// CHECK-NEXT: Isolation: global_actor. type: MainActor
// CHECK: MainActorStruct.mainActorField.setter
// CHECK-NEXT: Isolation: global_actor. type: MainActor
var mainActorField = NonSendableKlass()
// CHECK: variable initialization expression of MainActorStruct.customActorField
// CHECK-NEXT: Isolation: global_actor. type: CustomActor
// CHECK: MainActorStruct.customActorField.getter
// CHECK-NEXT: Isolation: global_actor. type: CustomActor
// CHECK: MainActorStruct.customActorField.setter
// CHECK-NEXT: Isolation: global_actor. type: CustomActor
@CustomActor var customActorField = NonSendableKlass()
nonisolated init() {}
}
struct NonisolatedStruct {
// CHECK: variable initialization expression of NonisolatedStruct.nonisolatedField
// CHECK-NEXT: Isolation: unspecified
// CHECK: NonisolatedStruct.nonisolatedField.getter
// CHECK-NEXT: Isolation: unspecified
// CHECK: NonisolatedStruct.nonisolatedField.setter
// CHECK-NEXT: Isolation: unspecified
var nonisolatedField = NonSendableKlass()
// CHECK: variable initialization expression of NonisolatedStruct.mainActorField
// CHECK-NEXT: Isolation: global_actor. type: MainActor
// CHECK: NonisolatedStruct.mainActorField.getter
// CHECK-NEXT: Isolation: global_actor. type: MainActor
// CHECK: NonisolatedStruct.mainActorField.setter
// CHECK-NEXT: Isolation: global_actor. type: MainActor
@MainActor var mainActorField = NonSendableKlass()
// CHECK: variable initialization expression of NonisolatedStruct.customActorField
// CHECK-NEXT: Isolation: global_actor. type: CustomActor
// CHECK: NonisolatedStruct.customActorField.getter
// CHECK-NEXT: Isolation: global_actor. type: CustomActor
// CHECK: NonisolatedStruct.customActorField.setter
// CHECK-NEXT: Isolation: global_actor. type: CustomActor
@CustomActor var customActorField = NonSendableKlass()
nonisolated init() {}
}
@MainActor
struct MainActorKlass {
// CHECK: variable initialization expression of MainActorKlass.mainActorField
// CHECK-NEXT: Isolation: global_actor. type: MainActor
// CHECK: MainActorKlass.mainActorField.getter
// CHECK-NEXT: Isolation: global_actor. type: MainActor
// CHECK: MainActorKlass.mainActorField.setter
// CHECK-NEXT: Isolation: global_actor. type: MainActor
var mainActorField = NonSendableKlass()
// CHECK: variable initialization expression of MainActorKlass.customActorField
// CHECK-NEXT: Isolation: global_actor. type: CustomActor
// CHECK: MainActorKlass.customActorField.getter
// CHECK-NEXT: Isolation: global_actor. type: CustomActor
// CHECK: MainActorKlass.customActorField.setter
// CHECK-NEXT: Isolation: global_actor. type: CustomActor
@CustomActor var customActorField = NonSendableKlass()
nonisolated init() {}
}
struct NonisolatedKlass {
// CHECK: variable initialization expression of NonisolatedKlass.nonisolatedField
// CHECK-NEXT: Isolation: unspecified
// CHECK: NonisolatedKlass.nonisolatedField.getter
// CHECK-NEXT: Isolation: unspecified
// CHECK: NonisolatedKlass.nonisolatedField.setter
// CHECK-NEXT: Isolation: unspecified
var nonisolatedField = NonSendableKlass()
// CHECK: variable initialization expression of NonisolatedKlass.mainActorField
// CHECK-NEXT: Isolation: global_actor. type: MainActor
// CHECK: NonisolatedKlass.mainActorField.getter
// CHECK-NEXT: Isolation: global_actor. type: MainActor
// CHECK: NonisolatedKlass.mainActorField.setter
// CHECK-NEXT: Isolation: global_actor. type: MainActor
@MainActor var mainActorField = NonSendableKlass()
// CHECK: variable initialization expression of NonisolatedKlass.customActorField
// CHECK-NEXT: Isolation: global_actor. type: CustomActor
// CHECK: NonisolatedKlass.customActorField.getter
// CHECK-NEXT: Isolation: global_actor. type: CustomActor
// CHECK: NonisolatedKlass.customActorField.setter
// CHECK-NEXT: Isolation: global_actor. type: CustomActor
@CustomActor var customActorField = NonSendableKlass()
nonisolated init() {}
}