Skip to content

Commit 7f7ca53

Browse files
committed
[Distributed] Handle #isolation param in DA as known to not cross isolation
The isolation checker was assuming that one can only be isolated to a specific var, but that's not true for distributed actors -- because the default parameter emitted by #isolation is a method call -- converting the self into an any Actor. We must handle this in isolation checker in order to avoid thinking we're crossing isolation boundaries and making methods implicitly async etc, when we're actually not. resolves rdar://131874709
1 parent 346bd23 commit 7f7ca53

File tree

3 files changed

+84
-1
lines changed

3 files changed

+84
-1
lines changed

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "TypeCheckType.h"
2222
#include "swift/Strings.h"
2323
#include "swift/AST/ASTWalker.h"
24+
#include "swift/AST/DistributedDecl.h"
2425
#include "swift/AST/GenericEnvironment.h"
2526
#include "swift/AST/ImportCache.h"
2627
#include "swift/AST/Initializer.h"
@@ -6880,6 +6881,27 @@ VarDecl *swift::getReferencedParamOrCapture(
68806881
if (isa<CurrentContextIsolationExpr>(expr))
68816882
return getCurrentIsolatedVar();
68826883

6884+
// Distributed:
6885+
// If we're referring to a member, it may be the special 'self.asLocalActor'
6886+
// of an actor that is the result of #isolation of distributed actors.
6887+
// the result of the value should be considered equal to the "self" isolation
6888+
// as it only transforms the DistributedActor self to an "any Actor" self
6889+
// used for isolation purposes.
6890+
if (auto memberRef = dyn_cast<MemberRefExpr>(expr)) {
6891+
if (auto refDecl = expr->getReferencedDecl(/*stopAtParenExpr=*/true)) {
6892+
if (auto decl = refDecl.getDecl()) {
6893+
auto module = decl->getDeclContext()->getParentModule();
6894+
auto AsLocalActorDecl =
6895+
getDistributedActorAsLocalActorComputedProperty(module);
6896+
6897+
if (decl == AsLocalActorDecl) {
6898+
return getCurrentIsolatedVar();
6899+
}
6900+
}
6901+
}
6902+
}
6903+
6904+
68836905
return nullptr;
68846906
}
68856907

test/Distributed/Inputs/FakeDistributedActorSystems.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -548,7 +548,7 @@ extension ActorAddress {
548548
return bytes
549549
}
550550
func fromBytes(_ bytes: [UInt8]) throws -> ActorAddress {
551-
let address = String(cString: bytes)
551+
let address = String(decoding: bytes, as: UTF8.self)
552552
return Self.init(parse: address)
553553
}
554554
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend-emit-module -emit-module-path %t/FakeDistributedActorSystems.swiftmodule -module-name FakeDistributedActorSystems -disable-availability-checking %S/../Inputs/FakeDistributedActorSystems.swift
3+
// RUN: %target-build-swift -parse-as-library -swift-version 6 -target %target-swift-abi-5.9-triple -I %t %s %S/../Inputs/FakeDistributedActorSystems.swift %S/../Inputs/CustomSerialExecutorAvailability.swift -o %t/a.out
4+
// RUN: %target-codesign %t/a.out
5+
// RUN: %target-run %t/a.out
6+
7+
// REQUIRES: executable_test
8+
// REQUIRES: concurrency
9+
// REQUIRES: distributed
10+
// REQUIRES: concurrency_runtime
11+
// UNSUPPORTED: back_deployment_runtime
12+
13+
// UNSUPPORTED: back_deploy_concurrency
14+
// UNSUPPORTED: use_os_stdlib
15+
// UNSUPPORTED: freestanding
16+
17+
//import StdlibUnittest
18+
import Distributed
19+
20+
@available(SwiftStdlib 5.9, *)
21+
distributed
22+
actor Tester {
23+
24+
typealias ActorSystem = LocalTestingDistributedActorSystem
25+
26+
distributed
27+
func check() async {
28+
pass()
29+
passOptional() // should work, but fails to infer that #isolation is the same as "self"
30+
31+
pass(isolatedToActor: self.asLocalActor)
32+
passOptional(isolatedToActor: self.asLocalActor)
33+
34+
// Not supported: the parameter must be exactly the 'self.asLocalActor'
35+
// as otherwise we don't know where the value came from and if it's really
36+
// self we're calling from; We'd need more sophisticated analysis to make it work.
37+
// let myself = self.asLocalActor
38+
// pass(isolatedToActor: myself) // same
39+
}
40+
}
41+
42+
@available(SwiftStdlib 5.9, *)
43+
func passOptional(isolatedToActor: isolated (any Actor)? = #isolation) {
44+
isolatedToActor!.preconditionIsolated("Expected to be executing on actor \(isolatedToActor!)")
45+
}
46+
47+
@available(SwiftStdlib 5.9, *)
48+
func pass(isolatedToActor: isolated (any Actor) = #isolation) {
49+
isolatedToActor.preconditionIsolated("Expected to be executing on actor \(isolatedToActor)")
50+
}
51+
52+
@main struct Main {
53+
static func main() async {
54+
if #available(SwiftStdlib 5.9, *) {
55+
let system = LocalTestingDistributedActorSystem()
56+
let tester = Tester(actorSystem: system)
57+
58+
try! await tester.check()
59+
}
60+
}
61+
}

0 commit comments

Comments
 (0)