Skip to content

Commit e53cd7d

Browse files
authored
[Distributed] Handle #isolation param in DA as known to not cross isolation (#75327)
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 04b15ba commit e53cd7d

File tree

4 files changed

+85
-1
lines changed

4 files changed

+85
-1
lines changed

lib/AST/DistributedDecl.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ VarDecl *
106106
swift::getDistributedActorAsLocalActorComputedProperty(ModuleDecl *module) {
107107
auto &C = module->getASTContext();
108108
auto DA = C.getDistributedActorDecl();
109+
if (!DA)
110+
return nullptr;
109111
auto extension = findDistributedActorAsActorExtension(DA);
110112

111113
if (!extension)

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"
@@ -6904,6 +6905,27 @@ VarDecl *swift::getReferencedParamOrCapture(
69046905
if (isa<CurrentContextIsolationExpr>(expr))
69056906
return getCurrentIsolatedVar();
69066907

6908+
// Distributed:
6909+
// If we're referring to a member, it may be the special 'self.asLocalActor'
6910+
// of an actor that is the result of #isolation of distributed actors.
6911+
// the result of the value should be considered equal to the "self" isolation
6912+
// as it only transforms the DistributedActor self to an "any Actor" self
6913+
// used for isolation purposes.
6914+
if (auto memberRef = dyn_cast<MemberRefExpr>(expr)) {
6915+
if (auto refDecl = expr->getReferencedDecl(/*stopAtParenExpr=*/true)) {
6916+
if (auto decl = refDecl.getDecl()) {
6917+
auto module = decl->getDeclContext()->getParentModule();
6918+
auto AsLocalActorDecl =
6919+
getDistributedActorAsLocalActorComputedProperty(module);
6920+
6921+
if (decl == AsLocalActorDecl) {
6922+
return getCurrentIsolatedVar();
6923+
}
6924+
}
6925+
}
6926+
}
6927+
6928+
69076929
return nullptr;
69086930
}
69096931

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: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
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 actor Tester {
22+
23+
typealias ActorSystem = LocalTestingDistributedActorSystem
24+
25+
distributed func check() async {
26+
// should correctly infer not to be crossing isolation boundary:
27+
pass()
28+
passOptional()
29+
30+
pass(isolatedToActor: self.asLocalActor)
31+
passOptional(isolatedToActor: self.asLocalActor)
32+
33+
// Not supported: the parameter must be exactly the 'self.asLocalActor'
34+
// as otherwise we don't know where the value came from and if it's really
35+
// self we're calling from; We'd need more sophisticated analysis to make it work.
36+
// let myself = self.asLocalActor
37+
// pass(isolatedToActor: myself)
38+
}
39+
}
40+
41+
@available(SwiftStdlib 5.9, *)
42+
func passOptional(isolatedToActor: isolated (any Actor)? = #isolation) {
43+
isolatedToActor!.preconditionIsolated("Expected to be executing on actor \(isolatedToActor!)")
44+
}
45+
46+
@available(SwiftStdlib 5.9, *)
47+
func pass(isolatedToActor: isolated (any Actor) = #isolation) {
48+
isolatedToActor.preconditionIsolated("Expected to be executing on actor \(isolatedToActor)")
49+
}
50+
51+
@main struct Main {
52+
static func main() async {
53+
if #available(SwiftStdlib 5.9, *) {
54+
let system = LocalTestingDistributedActorSystem()
55+
let tester = Tester(actorSystem: system)
56+
57+
try! await tester.check()
58+
}
59+
}
60+
}

0 commit comments

Comments
 (0)