Skip to content

Commit 5f669bb

Browse files
committed
[Distributed] handle generic actors and Codable better
1 parent 675441d commit 5f669bb

File tree

3 files changed

+236
-11
lines changed

3 files changed

+236
-11
lines changed

lib/Sema/CodeSynthesisDistributedActor.cpp

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,24 +12,25 @@
1212

1313
#include "TypeCheckDistributed.h"
1414

15-
#include "TypeChecker.h"
15+
#include "DerivedConformances.h"
1616
#include "TypeCheckType.h"
17+
#include "TypeChecker.h"
18+
#include "swift/AST/ASTMangler.h"
1719
#include "swift/AST/ASTPrinter.h"
1820
#include "swift/AST/Availability.h"
21+
#include "swift/AST/DistributedDecl.h"
22+
#include "swift/AST/ExistentialLayout.h"
1923
#include "swift/AST/Expr.h"
2024
#include "swift/AST/GenericEnvironment.h"
2125
#include "swift/AST/Initializer.h"
26+
#include "swift/AST/NameLookupRequests.h"
2227
#include "swift/AST/ParameterList.h"
2328
#include "swift/AST/TypeCheckRequests.h"
24-
#include "swift/AST/NameLookupRequests.h"
25-
#include "swift/AST/ASTMangler.h"
26-
#include "swift/AST/DistributedDecl.h"
2729
#include "swift/Basic/Defer.h"
2830
#include "swift/ClangImporter/ClangModule.h"
2931
#include "swift/Sema/ConstraintSystem.h"
3032
#include "llvm/ADT/SmallString.h"
3133
#include "llvm/ADT/StringExtras.h"
32-
#include "DerivedConformances.h"
3334

3435
using namespace swift;
3536

@@ -753,8 +754,6 @@ static FuncDecl *createDistributedThunkFunction(FuncDecl *func) {
753754
static NormalProtocolConformance*
754755
addDistributedActorCodableConformance(
755756
ClassDecl *actor, ProtocolDecl *proto) {
756-
assert(proto->isSpecificProtocol(swift::KnownProtocolKind::Decodable) ||
757-
proto->isSpecificProtocol(swift::KnownProtocolKind::Encodable));
758757
auto &C = actor->getASTContext();
759758
auto module = actor->getParentModule();
760759

@@ -763,6 +762,30 @@ addDistributedActorCodableConformance(
763762
return nullptr;
764763
}
765764

765+
if (actor->isGeneric()) {
766+
auto idTy = C.getAssociatedTypeOfDistributedSystemOfActor(actor, C.Id_ActorID);
767+
if (idTy->hasError()) {
768+
return nullptr;
769+
}
770+
auto encodableConf = module->lookupConformance(
771+
idTy, C.getProtocol(swift::KnownProtocolKind::Encodable),
772+
/*allowMissing=*/true);
773+
auto decodableConf = module->lookupConformance(
774+
idTy, C.getProtocol(swift::KnownProtocolKind::Decodable),
775+
/*allowMissing=*/true);
776+
777+
// the system's ID is not codable, thus the actor isn't as well -- don't add the conformance.
778+
if (encodableConf.isInvalid()) {
779+
return nullptr;
780+
}
781+
if (decodableConf.isInvalid()) {
782+
return nullptr;
783+
}
784+
}
785+
786+
assert(proto->isSpecificProtocol(swift::KnownProtocolKind::Decodable) ||
787+
proto->isSpecificProtocol(swift::KnownProtocolKind::Encodable));
788+
766789
// === Does the actor explicitly conform to the protocol already?
767790
auto explicitConformance =
768791
module->lookupConformance(actor->getInterfaceType(), proto);
@@ -958,7 +981,8 @@ VarDecl *GetDistributedActorSystemPropertyRequest::evaluate(
958981
return addImplicitDistributedActorActorSystemProperty(classDecl);
959982
}
960983

961-
NormalProtocolConformance *GetDistributedActorImplicitCodableRequest::evaluate(
984+
NormalProtocolConformance *
985+
GetDistributedActorImplicitCodableRequest::evaluate(
962986
Evaluator &evaluator, NominalTypeDecl *nominal,
963987
KnownProtocolKind protoKind) const {
964988
assert(nominal->isDistributedActor());
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
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 -module-name main -Xfrontend -disable-availability-checking -j2 -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 | %FileCheck %s --color
6+
7+
// REQUIRES: executable_test
8+
// REQUIRES: concurrency
9+
// REQUIRES: distributed
10+
11+
// rdar://76038845
12+
// UNSUPPORTED: use_os_stdlib
13+
// UNSUPPORTED: back_deployment_runtime
14+
15+
import Foundation
16+
import Distributed
17+
18+
final class LocalActorSystem : DistributedActorSystem {
19+
typealias ActorID = LocalTestingActorID
20+
typealias ResultHandler = LocalTestingInvocationResultHandler
21+
typealias InvocationEncoder = LocalTestingInvocationEncoder
22+
typealias InvocationDecoder = LocalTestingInvocationDecoder
23+
typealias SerializationRequirement = any Codable
24+
25+
func makeInvocationEncoder() -> InvocationEncoder { LocalTestingDistributedActorSystem().makeInvocationEncoder() }
26+
27+
func resolve<Act>(id: ActorID, as actorType: Act.Type) throws -> Act? where Act: DistributedActor {
28+
nil
29+
}
30+
31+
func actorReady<Act>(_ actor: Act) where Act: DistributedActor, Act.ID == ActorID {
32+
}
33+
34+
func resignID(_ id: ActorID) {}
35+
36+
func assignID<Act>(_ actorType: Act.Type) -> ActorID
37+
where Act: DistributedActor {
38+
.init(id: "42")
39+
}
40+
41+
func remoteCall<Act, Err, Res>(on actor: Act,
42+
target: RemoteCallTarget,
43+
invocation: inout InvocationEncoder,
44+
throwing: Err.Type,
45+
returning: Res.Type) async throws -> Res
46+
where Act: DistributedActor,
47+
Act.ID == ActorID,
48+
Err: Error,
49+
Res: Codable {
50+
let decoder = JSONDecoder()
51+
return try! decoder.decode(Res.self, from: await fetchData())
52+
}
53+
54+
func remoteCallVoid<Act, Err>(on actor: Act,
55+
target: RemoteCallTarget,
56+
invocation: inout InvocationEncoder,
57+
throwing errorType: Err.Type) async throws
58+
where Act: DistributedActor,
59+
Act.ID == ActorID,
60+
Err: Error {
61+
fatalError("not implemented: \(#function)")
62+
}
63+
64+
func fetchData() async -> Data {
65+
"42".data(using: .ascii)!
66+
}
67+
}
68+
69+
distributed actor NotCodableDA<ActorSystem>
70+
where ActorSystem: DistributedActorSystem<any Codable> {
71+
72+
init(actorSystem: ActorSystem) async {
73+
self.actorSystem = actorSystem
74+
75+
print(try! await self.request())
76+
}
77+
78+
func request() async throws -> Int {
79+
let target = RemoteCallTarget("test.request")
80+
var encoder = actorSystem.makeInvocationEncoder()
81+
return try await actorSystem.remoteCall(on: self,
82+
target: target,
83+
invocation: &encoder,
84+
throwing: Error.self,
85+
returning: Int.self)
86+
}
87+
}
88+
89+
distributed actor CodableDA<ActorSystem>: Codable
90+
where ActorSystem: DistributedActorSystem<any Codable>,
91+
ActorSystem.ActorID: Codable {
92+
93+
init(actorSystem: ActorSystem) async {
94+
self.actorSystem = actorSystem
95+
96+
print(try! await self.request())
97+
}
98+
99+
func request() async throws -> Int {
100+
let target = RemoteCallTarget("test.request")
101+
var encoder = actorSystem.makeInvocationEncoder()
102+
return try await actorSystem.remoteCall(on: self,
103+
target: target,
104+
invocation: &encoder,
105+
throwing: Error.self,
106+
returning: Int.self)
107+
}
108+
}
109+
110+
distributed actor CodableIDDA<ActorSystem>
111+
where ActorSystem: DistributedActorSystem<any Codable>,
112+
ActorSystem.ActorID: Codable {
113+
114+
init(actorSystem: ActorSystem) async {
115+
self.actorSystem = actorSystem
116+
117+
print(try! await self.request())
118+
}
119+
120+
func request() async throws -> Int {
121+
let target = RemoteCallTarget("test.request")
122+
var encoder = actorSystem.makeInvocationEncoder()
123+
return try await actorSystem.remoteCall(on: self,
124+
target: target,
125+
invocation: &encoder,
126+
throwing: Error.self,
127+
returning: Int.self)
128+
}
129+
}
130+
131+
@main struct Main {
132+
static func main() async throws {
133+
if #available(SwiftStdlib 5.9, *) {
134+
let system = LocalActorSystem()
135+
let ncda = await NotCodableDA(actorSystem: system)
136+
let cidda = await CodableIDDA(actorSystem: system)
137+
let cda = await CodableDA(actorSystem: system)
138+
139+
try await ncda.whenLocal {
140+
let got = try await $0.request()
141+
142+
// CHECK: got = 42
143+
print("got = \(got)")
144+
}
145+
146+
try await cidda.whenLocal {
147+
let got = try await $0.request()
148+
149+
// CHECK: got = 42
150+
print("got = \(got)")
151+
}
152+
153+
let c: any (DistributedActor & Codable) = cda
154+
try await cda.whenLocal {
155+
let got = try await $0.request()
156+
157+
// CHECK: got = 42
158+
print("got = \(got)")
159+
}
160+
161+
// CHECK: OK
162+
print("OK")
163+
}
164+
}
165+
}

test/Distributed/distributed_actor_implicit_codable.swift

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,47 @@ import FakeDistributedActorSystems
99

1010
typealias DefaultDistributedActorSystem = FakeActorSystem
1111

12+
func takeCodable<A: Codable>(actor: A) {}
13+
14+
// ====
15+
1216
distributed actor DA {
1317
}
1418

15-
func take<A: Codable>(actor: A) {}
19+
func test_DA(actorSystem: FakeActorSystem) {
20+
takeCodable(actor: DA(actorSystem: actorSystem)) // ok
21+
}
22+
23+
// ==== Generic actors
24+
25+
distributed actor DAG<ActorSystem> where ActorSystem: DistributedActorSystem<any Codable> {
26+
}
27+
func test_DAG(actorSystem: FakeActorSystem) {
28+
takeCodable(actor: DAG<FakeActorSystem>(actorSystem: actorSystem)) // ok
29+
}
30+
31+
distributed actor DAG_ID<ActorSystem> where ActorSystem: DistributedActorSystem<any Codable>,
32+
ID: Codable { // expected-error{{cannot find type 'ID' in scope}}
33+
}
34+
func test_DAG_ID(actorSystem: FakeActorSystem) {
35+
takeCodable(actor: DAG_ID<FakeActorSystem>(actorSystem: actorSystem)) // ok
36+
}
37+
38+
distributed actor DAG_ActorSystem_ActorID<ActorSystem> where ActorSystem: DistributedActorSystem<any Codable>,
39+
ActorSystem.ActorID: Codable {
40+
}
41+
func test_DAG_ActorSystem_ActorID(actorSystem: FakeActorSystem) {
42+
takeCodable(actor: DAG_ActorSystem_ActorID<FakeActorSystem>(actorSystem: actorSystem)) // ok
43+
}
44+
45+
// ==== Not codable cases
46+
47+
protocol SerializableButNotCodable {}
1648

17-
func test(actorSystem: FakeActorSystem) {
18-
take(actor: DA(actorSystem: actorSystem)) // ok
49+
distributed actor DAG_ActorSystem_ActorID_Custom<ActorSystem>
50+
where ActorSystem: DistributedActorSystem<any SerializableButNotCodable>,
51+
ActorSystem.ActorID: SerializableButNotCodable {
1952
}
53+
func test_DAG_ActorSystem_ActorID_Custom(actorSystem: FakeActorSystem) {
54+
takeCodable(actor: DAG_ActorSystem_ActorID<FakeActorSystem>(actorSystem: actorSystem)) // bad
55+
}

0 commit comments

Comments
 (0)