Skip to content

Commit

Permalink
Merge pull request swiftlang#70285 from xedin/keypath-with-global-actors
Browse files Browse the repository at this point in the history
[Concurrency] Key paths and global actors
  • Loading branch information
xedin committed Dec 8, 2023
2 parents d36173a + e561ac4 commit b0d4ba0
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 21 deletions.
4 changes: 2 additions & 2 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -5410,8 +5410,8 @@ ERROR(concurrent_access_local,none,
"use of local %kind0 in concurrently-executing code",
(const ValueDecl *))
ERROR(actor_isolated_keypath_component,none,
"cannot form key path to%select{| distributed}0 actor-isolated %kind1",
(bool, const ValueDecl *))
"cannot form key path to %0 %kind1",
(ActorIsolation, const ValueDecl *))
ERROR(effectful_keypath_component,none,
"cannot form key path to %0 with 'throws' or 'async'",
(DescriptiveDeclKind))
Expand Down
19 changes: 19 additions & 0 deletions lib/Sema/ConstraintSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7494,6 +7494,11 @@ ConstraintSystem::inferKeyPathLiteralCapability(KeyPathExpr *keyPath) {
auto *sendable = Context.getProtocol(KnownProtocolKind::Sendable);

for (const auto &arg : *args) {
// No need to check more or delay since we already known
// that the type is not Sendable.
if (!isSendable)
break;

auto argTy = simplifyType(getType(arg.getExpr()));

// Sendability cannot be determined until the argument
Expand Down Expand Up @@ -7546,6 +7551,20 @@ ConstraintSystem::inferKeyPathLiteralCapability(KeyPathExpr *keyPath) {
if (!storage)
return fail();

switch (getActorIsolation(storage)) {
case ActorIsolation::Unspecified:
case ActorIsolation::Nonisolated:
case ActorIsolation::NonisolatedUnsafe:
break;

// A reference to an actor isolated state make key path non-Sendable.
case ActorIsolation::ActorInstance:
case ActorIsolation::GlobalActor:
case ActorIsolation::GlobalActorUnsafe:
isSendable = false;
break;
}

if (isReadOnlyKeyPathComponent(storage, component.getLoc())) {
mutability = KeyPathMutability::ReadOnly;
continue;
Expand Down
31 changes: 20 additions & 11 deletions lib/Sema/TypeCheckConcurrency.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3355,8 +3355,8 @@ namespace {
for (const auto &component : keyPath->getComponents()) {
// The decl referred to by the path component cannot be within an actor.
if (component.hasDeclRef()) {
auto concDecl = component.getDeclRef();
auto decl = concDecl.getDecl();
auto declRef = component.getDeclRef();
auto decl = declRef.getDecl();
auto isolation = getActorIsolationForReference(
decl, getDeclContext());
switch (isolation) {
Expand All @@ -3366,13 +3366,16 @@ namespace {
break;

case ActorIsolation::GlobalActor:
case ActorIsolation::GlobalActorUnsafe:
// Disable global actor checking for now.
if (isolation.isGlobalActor() &&
!ctx.LangOpts.isSwiftVersionAtLeast(6))
case ActorIsolation::GlobalActorUnsafe: {
auto result = ActorReferenceResult::forReference(
declRef, component.getLoc(), getDeclContext(),
kindOfUsage(decl, keyPath));

if (result == ActorReferenceResult::SameConcurrencyDomain)
break;

LLVM_FALLTHROUGH;
}

case ActorIsolation::ActorInstance:
// If this entity is always accessible across actors, just check
Expand All @@ -3388,11 +3391,17 @@ namespace {
break;
}

ctx.Diags.diagnose(component.getLoc(),
diag::actor_isolated_keypath_component,
isolation.isDistributedActor(),
decl);
diagnosed = true;
{
auto diagnostic = ctx.Diags.diagnose(
component.getLoc(), diag::actor_isolated_keypath_component,
isolation, decl);

if (isolation == ActorIsolation::ActorInstance)
diagnosed = true;
else
diagnostic.warnUntilSwiftVersion(6);
}

break;
}
}
Expand Down
8 changes: 4 additions & 4 deletions test/Concurrency/actor_keypath_isolation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func tryKeyPathsMisc(d : Door) {

func tryKeyPathsFromAsync() async {
_ = \Door.unsafeGlobActor_immutable
_ = \Door.unsafeGlobActor_mutable // okay for now
_ = \Door.unsafeGlobActor_mutable // expected-warning {{cannot form key path to main actor-isolated property 'unsafeGlobActor_mutable'; this is an error in Swift 6}}
}

func tryNonSendable() {
Expand All @@ -69,7 +69,7 @@ func tryNonSendable() {

func tryKeypaths() {
_ = \Door.unsafeGlobActor_immutable
_ = \Door.unsafeGlobActor_mutable // okay for now
_ = \Door.unsafeGlobActor_mutable // expected-warning {{cannot form key path to main actor-isolated property 'unsafeGlobActor_mutable'; this is an error in Swift 6}}

_ = \Door.immutable
_ = \Door.globActor_immutable
Expand All @@ -84,7 +84,7 @@ func tryKeypaths() {
let _ : PartialKeyPath<Door> = \.mutable // expected-error{{cannot form key path to actor-isolated property 'mutable'}}
let _ : AnyKeyPath = \Door.mutable // expected-error{{cannot form key path to actor-isolated property 'mutable'}}

_ = \Door.globActor_mutable // okay for now
_ = \Door.globActor_mutable // expected-warning {{cannot form key path to main actor-isolated property 'globActor_mutable'; this is an error in Swift 6}}
_ = \Door.[0] // expected-error{{cannot form key path to actor-isolated subscript 'subscript(_:)'}}
_ = \Door.["hello"] // okay for now
_ = \Door.["hello"] // expected-warning {{cannot form key path to main actor-isolated subscript 'subscript(_:)'; this is an error in Swift 6}}
}
8 changes: 4 additions & 4 deletions test/Concurrency/actor_keypath_isolation_swift6.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func tryKeyPathsMisc(d : Door) {

func tryKeyPathsFromAsync() async {
_ = \Door.unsafeGlobActor_immutable
_ = \Door.unsafeGlobActor_mutable // expected-error {{cannot form key path to actor-isolated property 'unsafeGlobActor_mutable'}}
_ = \Door.unsafeGlobActor_mutable // expected-error {{cannot form key path to main actor-isolated property 'unsafeGlobActor_mutable'}}
}

func tryNonSendable() {
Expand All @@ -68,7 +68,7 @@ func tryNonSendable() {

func tryKeypaths() {
_ = \Door.unsafeGlobActor_immutable
_ = \Door.unsafeGlobActor_mutable // expected-error {{cannot form key path to actor-isolated property 'unsafeGlobActor_mutable'}}
_ = \Door.unsafeGlobActor_mutable // expected-error {{cannot form key path to main actor-isolated property 'unsafeGlobActor_mutable'}}

_ = \Door.immutable
_ = \Door.globActor_immutable
Expand All @@ -83,7 +83,7 @@ func tryKeypaths() {
let _ : PartialKeyPath<Door> = \.mutable // expected-error{{cannot form key path to actor-isolated property 'mutable'}}
let _ : AnyKeyPath = \Door.mutable // expected-error{{cannot form key path to actor-isolated property 'mutable'}}

_ = \Door.globActor_mutable // expected-error{{cannot form key path to actor-isolated property 'globActor_mutable'}}
_ = \Door.globActor_mutable // expected-error{{cannot form key path to main actor-isolated property 'globActor_mutable'}}
_ = \Door.[0] // expected-error{{cannot form key path to actor-isolated subscript 'subscript(_:)'}}
_ = \Door.["hello"] // expected-error {{cannot form key path to actor-isolated subscript 'subscript(_:)'}}
_ = \Door.["hello"] // expected-error {{cannot form key path to main actor-isolated subscript 'subscript(_:)'}}
}
61 changes: 61 additions & 0 deletions test/Concurrency/sendable_keypaths.swift
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,64 @@ do {

_ = Test(obj: "Hello").utf8.count // Ok
}

// Global actor isolated properties.
func testGlobalActorIsolatedReferences() {
@MainActor struct Isolated {
var data: Int = 42
subscript(v: Int) -> Bool { false }
}

let dataKP = \Isolated.data
// expected-warning@-1 {{cannot form key path to main actor-isolated property 'data'; this is an error in Swift 6}}
let subscriptKP = \Isolated.[42]
// expected-warning@-1 {{cannot form key path to main actor-isolated subscript 'subscript(_:)'; this is an error in Swift 6}}

let _: KeyPath<Isolated, Int> & Sendable = dataKP
// expected-warning@-1 {{type 'WritableKeyPath<Isolated, Int>' does not conform to the 'Sendable' protocol}}
let _: KeyPath<Isolated, Bool> & Sendable = subscriptKP
// expected-warning@-1 {{type 'KeyPath<Isolated, Bool>' does not conform to the 'Sendable' protocol}}

func testNonIsolated() {
_ = \Isolated.data
// expected-warning@-1 {{cannot form key path to main actor-isolated property 'data'; this is an error in Swift 6}}
}

@MainActor func testIsolated() {
_ = \Isolated.data // Ok
}
}

@available(SwiftStdlib 5.1, *)
actor SomeActor {
}

@available(SwiftStdlib 5.1, *)
@globalActor
actor GlobalActor {
static let shared: SomeActor = SomeActor()
}

@available(SwiftStdlib 5.1, *)
func testReferencesToDifferentGlobalActorIsolatedMembers() {
struct Info {
@MainActor var name: String { "" }
}

struct Isolated {
@GlobalActor var info: Info { Info() }
}

@MainActor func testIsolatedToMain() {
_ = \Info.name // Ok
_ = \Isolated.info.name
// expected-warning@-1 {{cannot form key path to global actor 'GlobalActor'-isolated property 'info'; this is an error in Swift 6}}
}

@GlobalActor func testIsolatedToCustom() {
_ = \Info.name // Ok
// expected-warning@-1 {{cannot form key path to main actor-isolated property 'name'; this is an error in Swift 6}}
_ = \Isolated.info.name
// expected-warning@-1 {{cannot form key path to main actor-isolated property 'name'; this is an error in Swift 6}}
}
}

0 comments on commit b0d4ba0

Please sign in to comment.