Skip to content

Commit 10b49bc

Browse files
authored
[Executors] Ensure we treat DA older than 5.9 always as DefaultActor (#64800)
1 parent 71448d6 commit 10b49bc

11 files changed

+196
-2
lines changed

include/swift/AST/ASTContext.h

+11
Original file line numberDiff line numberDiff line change
@@ -891,6 +891,9 @@ class ASTContext final {
891891
/// Get the back-deployed availability for concurrency.
892892
AvailabilityContext getBackDeployedConcurrencyAvailability();
893893

894+
/// The the availability since when distributed actors are able to have custom executors.
895+
AvailabilityContext getConcurrencyDistributedActorWithCustomExecutorAvailability();
896+
894897
/// Get the runtime availability of support for differentiation.
895898
AvailabilityContext getDifferentiationAvailability();
896899

@@ -934,6 +937,14 @@ class ASTContext final {
934937
/// compiler for the target platform.
935938
AvailabilityContext getSwift57Availability();
936939

940+
/// Get the runtime availability of features introduced in the Swift 5.8
941+
/// compiler for the target platform.
942+
AvailabilityContext getSwift58Availability();
943+
944+
/// Get the runtime availability of features introduced in the Swift 5.9
945+
/// compiler for the target platform.
946+
AvailabilityContext getSwift59Availability();
947+
937948
// Note: Update this function if you add a new getSwiftXYAvailability above.
938949
/// Get the runtime availability for a particular version of Swift (5.0+).
939950
AvailabilityContext

include/swift/AST/Availability.h

+5
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,11 @@ class AvailabilityContext {
329329
bool isAvailableAsSPI() const {
330330
return SPI && *SPI;
331331
}
332+
333+
/// Returns a representation of this range as a string for debugging purposes.
334+
std::string getAsString() const {
335+
return "AvailabilityContext(" + OSVersion.getAsString() + (isAvailableAsSPI() ? ", spi" : "") + ")";
336+
}
332337
};
333338

334339

lib/AST/Availability.cpp

+28
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,10 @@ AvailabilityContext ASTContext::getBackDeployedConcurrencyAvailability() {
489489
return getSwift51Availability();
490490
}
491491

492+
AvailabilityContext ASTContext::getConcurrencyDistributedActorWithCustomExecutorAvailability() {
493+
return getSwift59Availability();
494+
}
495+
492496
AvailabilityContext ASTContext::getDifferentiationAvailability() {
493497
return getSwiftFutureAvailability();
494498
}
@@ -642,6 +646,28 @@ AvailabilityContext ASTContext::getSwift57Availability() {
642646
}
643647
}
644648

649+
AvailabilityContext ASTContext::getSwift58Availability() {
650+
auto target = LangOpts.Target;
651+
652+
if (target.isMacOSX()) {
653+
return AvailabilityContext(
654+
VersionRange::allGTE(llvm::VersionTuple(13, 3, 0)));
655+
} else if (target.isiOS()) {
656+
return AvailabilityContext(
657+
VersionRange::allGTE(llvm::VersionTuple(16, 4, 0)));
658+
} else if (target.isWatchOS()) {
659+
return AvailabilityContext(
660+
VersionRange::allGTE(llvm::VersionTuple(9, 4, 0)));
661+
} else {
662+
return AvailabilityContext::alwaysAvailable();
663+
}
664+
}
665+
666+
AvailabilityContext ASTContext::getSwift59Availability() {
667+
// TODO: Update Availability impl when Swift 5.9 is released
668+
return getSwiftFutureAvailability();
669+
}
670+
645671
AvailabilityContext ASTContext::getSwiftFutureAvailability() {
646672
auto target = LangOpts.Target;
647673

@@ -671,6 +697,8 @@ ASTContext::getSwift5PlusAvailability(llvm::VersionTuple swiftVersion) {
671697
case 5: return getSwift55Availability();
672698
case 6: return getSwift56Availability();
673699
case 7: return getSwift57Availability();
700+
case 8: return getSwift58Availability();
701+
case 9: return getSwift59Availability();
674702
default: break;
675703
}
676704
}

lib/SILOptimizer/Mandatory/LowerHopToActor.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,9 @@ SILValue LowerHopToActor::emitGetExecutor(SILBuilderWithScope &B,
185185
// If the actor type is a default actor, go ahead and devirtualize here.
186186
auto module = F->getModule().getSwiftModule();
187187
SILValue unmarkedExecutor;
188+
189+
// Determine if the actor is a "default actor" in which case we'll build a default
190+
// actor executor ref inline, rather than calling out to the user-provided executor function.
188191
if (isDefaultActorType(actorType, module, F->getResilienceExpansion())) {
189192
auto builtinName = ctx.getIdentifier(
190193
getBuiltinName(BuiltinValueKind::BuildDefaultActorExecutorRef));

lib/Sema/TypeCheckConcurrency.cpp

+21
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,27 @@ bool IsDefaultActorRequest::evaluate(
182182
if (!classDecl->isActor())
183183
return false;
184184

185+
// Distributed actors were not able to have custom executors until Swift 5.9,
186+
// so in order to avoid wrongly treating a resilient distributed actor from another
187+
// module as not-default we need to handle this case explicitly.
188+
if (classDecl->isDistributedActor()) {
189+
ASTContext &ctx = classDecl->getASTContext();
190+
auto customExecutorAvailability =
191+
ctx.getConcurrencyDistributedActorWithCustomExecutorAvailability();
192+
193+
auto actorAvailability = TypeChecker::overApproximateAvailabilityAtLocation(
194+
classDecl->getStartLoc(),
195+
classDecl);
196+
197+
if (!actorAvailability.isContainedIn(customExecutorAvailability)) {
198+
// Any 'distributed actor' declared with availability lower than the
199+
// introduction of custom executors for distributed actors, must be treated as default actor,
200+
// even if it were to declared the unowned executor property, as older compilers
201+
// do not have the the logic to handle that case.
202+
return true;
203+
}
204+
}
205+
185206
// If the class is resilient from the perspective of the module
186207
// module, it's not a default actor.
187208
if (classDecl->isForeign() || classDecl->isResilient(M, expansion))

test/Distributed/Runtime/distributed_actor_assume_executor.swift

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ func check(actor: MainDistributedFriend) {
3939
checkAssumeMainActor(actor: actor)
4040
}
4141

42+
@available(SwiftStdlib 5.9, *)
4243
distributed actor MainDistributedFriend {
4344
nonisolated var localUnownedExecutor: UnownedSerialExecutor? {
4445
print("get unowned executor")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
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 -Xfrontend -disable-availability-checking -parse-as-library -I %t %s %S/../Inputs/FakeDistributedActorSystems.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+
import FakeDistributedActorSystems
20+
21+
@available(SwiftStdlib 5.7, *)
22+
typealias DefaultDistributedActorSystem = LocalTestingDistributedActorSystem
23+
24+
@available(SwiftStdlib 5.7, *)
25+
distributed actor FiveSevenActor_NothingExecutor {
26+
nonisolated var localUnownedExecutor: UnownedSerialExecutor? {
27+
print("get unowned executor")
28+
return MainActor.sharedUnownedExecutor
29+
}
30+
31+
distributed func test(x: Int) async throws {
32+
print("executed: \(#function)")
33+
defer {
34+
print("done executed: \(#function)")
35+
}
36+
assumeOnMainActorExecutor {
37+
// ignore
38+
}
39+
}
40+
}
41+
42+
@available(SwiftStdlib 5.9, *)
43+
distributed actor FiveNineActor_NothingExecutor {
44+
// @available(SwiftStdlib 5.9, *) // because of `localUnownedExecutor`
45+
nonisolated var localUnownedExecutor: UnownedSerialExecutor? {
46+
print("get unowned executor")
47+
return MainActor.sharedUnownedExecutor
48+
}
49+
50+
distributed func test(x: Int) async throws {
51+
print("executed: \(#function)")
52+
defer {
53+
print("done executed: \(#function)")
54+
}
55+
assumeOnMainActorExecutor {
56+
// ignore
57+
}
58+
}
59+
}
60+
61+
@available(SwiftStdlib 5.7, *)
62+
distributed actor FiveSevenActor_FiveNineExecutor {
63+
@available(SwiftStdlib 5.9, *)
64+
nonisolated var localUnownedExecutor: UnownedSerialExecutor? {
65+
print("get unowned executor")
66+
return MainActor.sharedUnownedExecutor
67+
}
68+
69+
distributed func test(x: Int) async throws {
70+
print("executed: \(#function)")
71+
defer {
72+
print("done executed: \(#function)")
73+
}
74+
assumeOnMainActorExecutor {
75+
// ignore
76+
}
77+
}
78+
}
79+
80+
@main struct Main {
81+
static func main() async {
82+
if #available(SwiftStdlib 5.9, *) {
83+
let tests = TestSuite("DistributedActorExecutorAvailability")
84+
85+
let system = LocalTestingDistributedActorSystem()
86+
87+
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
88+
tests.test("5.7 actor, no availability executor property => no custom executor") {
89+
expectCrashLater(withMessage: "Fatal error: Incorrect actor executor assumption; Expected 'MainActor' executor.")
90+
try! await FiveSevenActor_NothingExecutor(actorSystem: system).test(x: 42)
91+
}
92+
93+
tests.test("5.9 actor, no availability executor property => custom executor") {
94+
try! await FiveNineActor_NothingExecutor(actorSystem: system).test(x: 42)
95+
}
96+
97+
tests.test("5.7 actor, 5.9 executor property => no custom executor") {
98+
expectCrashLater(withMessage: "Fatal error: Incorrect actor executor assumption; Expected 'MainActor' executor.")
99+
try! await FiveSevenActor_FiveNineExecutor(actorSystem: system).test(x: 42)
100+
}
101+
#else
102+
// On non-apple platforms the SDK comes with the toolchains,
103+
// so the feature works because we're executing in a 5.9 context already,
104+
// which otherwise could not have been compiled
105+
tests.test("non apple platform: 5.7 actor, no availability executor property => no custom executor") {
106+
try! await FiveSevenActor_NothingExecutor(actorSystem: system).test(x: 42)
107+
}
108+
109+
tests.test("non apple platform: 5.9 actor, no availability executor property => custom executor") {
110+
try! await FiveNineActor_NothingExecutor(actorSystem: system).test(x: 42)
111+
}
112+
113+
tests.test("non apple platform: 5.7 actor, 5.9 executor property => no custom executor") {
114+
try! await FiveSevenActor_FiveNineExecutor(actorSystem: system).test(x: 42)
115+
}
116+
#endif
117+
118+
await runAllTestsAsync()
119+
}
120+
}
121+
}

test/Distributed/Runtime/distributed_actor_custom_executor_basic.swift

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import FakeDistributedActorSystems
2121

2222
typealias DefaultDistributedActorSystem = FakeRoundtripActorSystem
2323

24+
@available(SwiftStdlib 5.9, *) // because conforming to the protocol... that has this field in 5.9?
2425
distributed actor Worker {
2526
nonisolated var localUnownedExecutor: UnownedSerialExecutor? {
2627
print("get unowned executor")

test/Distributed/Runtime/distributed_actor_custom_executor_from_id.swift

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import FakeDistributedActorSystems
2121

2222
typealias DefaultDistributedActorSystem = FakeRoundtripActorSystem
2323

24+
@available(SwiftStdlib 5.9, *)
2425
distributed actor Worker {
2526
nonisolated var localUnownedExecutor: UnownedSerialExecutor? {
2627
print("get unowned 'local' executor via ID")

test/Distributed/SIL/distributed_actor_initialize_nondefault.swift

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// RUN: %empty-directory(%t)
22
// 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-swift-frontend -module-name default_deinit -primary-file %s -emit-sil -verify -disable-availability-checking -I %t | %FileCheck %s --enable-var-scope --dump-input=fail
3+
// RUN: %target-swift-frontend -module-name default_deinit -primary-file %s -emit-sil -verify -disable-availability-checking -I %t | %FileCheck %s --enable-var-scope
44
// REQUIRES: concurrency
55
// REQUIRES: distributed
66

@@ -13,13 +13,14 @@ typealias DefaultDistributedActorSystem = FakeRoundtripActorSystem
1313

1414
// ==== ----------------------------------------------------------------------------------------------------------------
1515

16+
@available(SwiftStdlib 5.9, *)
1617
distributed actor MyDistActor {
1718
nonisolated var localUnownedExecutor: UnownedSerialExecutor? {
1819
return MainActor.sharedUnownedExecutor
1920
}
2021

2122
// // MyDistActor.init(actorSystem:)
22-
// CHECK: sil hidden @$s14default_deinit11MyDistActorC11actorSystemAC015FakeDistributedE7Systems0h9RoundtripeG0C_tcfc : $@convention(method) (@owned FakeRoundtripActorSystem, @owned MyDistActor) -> @owned MyDistActor
23+
// CHECK: sil hidden{{.*}} @$s14default_deinit11MyDistActorC11actorSystemAC015FakeDistributedE7Systems0h9RoundtripeG0C_tcfc : $@convention(method) (@owned FakeRoundtripActorSystem, @owned MyDistActor) -> @owned MyDistActor
2324
// CHECK-NOT: {{%[0-9]+}} = builtin "initializeDefaultActor"(%1 : $MyDistActor) : $()
2425
// CHECK: [[ACTOR_INSTANCE:%[0-9]+]] = builtin "initializeNonDefaultDistributedActor"(%1 : $MyDistActor) : $()
2526
}

utils/availability-macros.def

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ SwiftStdlib 5.6:macOS 12.3, iOS 15.4, watchOS 8.5, tvOS 15.4
3434
SwiftStdlib 5.7:macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0
3535
SwiftStdlib 5.8:macOS 13.3, iOS 16.4, watchOS 9.4, tvOS 16.4
3636
SwiftStdlib 5.9:macOS 9999, iOS 9999, watchOS 9999, tvOS 9999
37+
# TODO: Also update ASTContext::getSwift59Availability when 5.9 is released
3738

3839
# Local Variables:
3940
# mode: conf-unix

0 commit comments

Comments
 (0)