Skip to content

Commit c916c0a

Browse files
authored
[Distributed] decode init for DistributedActor (#38998)
* Revert "Revert "Merge pull request #38938 from drexin/wip-dist-resolve" (#38994)" This reverts commit f6ae9f3. * [Distributed] decode init for DistributedActor
1 parent 7e001ec commit c916c0a

File tree

4 files changed

+254
-6
lines changed

4 files changed

+254
-6
lines changed

lib/AST/ASTContext.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2943,6 +2943,29 @@ AnyFunctionType::Param swift::computeSelfParam(AbstractFunctionDecl *AFD,
29432943
// inout self.
29442944
if (!containerTy->hasReferenceSemantics())
29452945
selfAccess = SelfAccessKind::Mutating;
2946+
2947+
// FIXME(distributed): pending swift-evolution, allow `self =` in class
2948+
// inits in general.
2949+
// See also: https://github.com/apple/swift/pull/19151 general impl
2950+
if (Ctx.LangOpts.EnableExperimentalDistributed) {
2951+
auto ext = dyn_cast<ExtensionDecl>(AFD->getDeclContext());
2952+
auto distProto =
2953+
Ctx.getProtocol(KnownProtocolKind::DistributedActor);
2954+
if (ext && ext->getExtendedNominal() &&
2955+
ext->getExtendedNominal()->getInterfaceType()
2956+
->isEqual(distProto->getInterfaceType())) {
2957+
auto name = CD->getName();
2958+
auto params = name.getArgumentNames();
2959+
if (params.size() == 1 && params[0] == Ctx.Id_from) {
2960+
// FIXME(distributed): this is a workaround to allow init(from:) to
2961+
// be implemented in AST by allowing the self to be mutable in the
2962+
// decoding initializer. This should become a general Swift
2963+
// feature, allowing this in all classes:
2964+
// https://forums.swift.org/t/allow-self-x-in-class-convenience-initializers/15924
2965+
selfAccess = SelfAccessKind::Mutating;
2966+
}
2967+
}
2968+
}
29462969
} else {
29472970
// allocating constructors have metatype 'self'.
29482971
isStatic = true;

stdlib/public/Distributed/ActorTransport.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public protocol ActorTransport: Sendable {
4848
///
4949
/// Detecting liveness of such remote actors shall be offered / by transport libraries
5050
/// by other means, such as "watching an actor for termination" or similar.
51-
func resolve<Act>(_ identity: AnyActorIdentity, as actorType: Act.Type) throws -> Act? // TODO(distributed): make just optional
51+
func resolve<Act>(_ identity: AnyActorIdentity, as actorType: Act.Type) throws -> Act?
5252
where Act: DistributedActor
5353

5454
// ==== ---------------------------------------------------------------------

stdlib/public/Distributed/DistributedActor.swift

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,16 +93,21 @@ extension DistributedActor {
9393
// ==== Codable conformance ----------------------------------------------------
9494

9595
extension CodingUserInfoKey {
96-
@available(SwiftStdlib 5.5, *)
97-
static let actorTransportKey = CodingUserInfoKey(rawValue: "$dist_act_transport")!
96+
@available(SwiftStdlib 5.5, *)
97+
public static let actorTransportKey = CodingUserInfoKey(rawValue: "$dist_act_transport")!
9898
}
9999

100100
@available(SwiftStdlib 5.5, *)
101101
extension DistributedActor {
102102
nonisolated public init(from decoder: Decoder) throws {
103-
// let id = try AnyActorIdentity(from: decoder)
104-
// self = try Self.resolve(id, using: transport) // FIXME: This is going to be solved by the init() work!!!!
105-
fatalError("\(#function) is not implemented yet for distributed actors'")
103+
guard let transport = decoder.userInfo[.actorTransportKey] as? ActorTransport else {
104+
throw DistributedActorCodingError(message:
105+
"Missing ActorTransport (for key .actorTransportKey) " +
106+
"in Decoder.userInfo, while decoding \(Self.self).")
107+
}
108+
109+
let id: AnyActorIdentity = try transport.decodeIdentity(from: decoder)
110+
self = try Self.resolve(id, using: transport)
106111
}
107112

108113
nonisolated public func encode(to encoder: Encoder) throws {
@@ -121,12 +126,14 @@ public protocol ActorIdentity: Sendable, Hashable, Codable {}
121126

122127
@available(SwiftStdlib 5.5, *)
123128
public struct AnyActorIdentity: ActorIdentity, @unchecked Sendable, CustomStringConvertible {
129+
public let underlying: Any
124130
@usableFromInline let _hashInto: (inout Hasher) -> ()
125131
@usableFromInline let _equalTo: (Any) -> Bool
126132
@usableFromInline let _encodeTo: (Encoder) throws -> ()
127133
@usableFromInline let _description: () -> String
128134

129135
public init<ID>(_ identity: ID) where ID: ActorIdentity {
136+
self.underlying = identity
130137
_hashInto = { hasher in identity
131138
.hash(into: &hasher)
132139
}
Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-distributed -parse-as-library) | %FileCheck %s
2+
3+
// REQUIRES: executable_test
4+
// REQUIRES: concurrency
5+
// REQUIRES: distributed
6+
7+
// UNSUPPORTED: use_os_stdlib
8+
// UNSUPPORTED: back_deployment_runtime
9+
10+
import _Distributed
11+
12+
@available(SwiftStdlib 5.5, *)
13+
distributed actor DA: CustomStringConvertible {
14+
nonisolated var description: String {
15+
"DA(\(self.id))"
16+
}
17+
}
18+
19+
// ==== Fake Transport ---------------------------------------------------------
20+
21+
@available(SwiftStdlib 5.5, *)
22+
struct ActorAddress: ActorIdentity {
23+
let address: String
24+
init(parse address : String) {
25+
self.address = address
26+
}
27+
28+
// Explicit implementations to make our TestEncoder/Decoder simpler
29+
init(from decoder: Decoder) throws {
30+
let container = try decoder.singleValueContainer()
31+
self.address = try container.decode(String.self)
32+
print("decode ActorAddress -> \(self)")
33+
}
34+
35+
func encode(to encoder: Encoder) throws {
36+
print("encode \(self)")
37+
var container = encoder.singleValueContainer()
38+
try container.encode(self.address)
39+
}
40+
}
41+
42+
@available(SwiftStdlib 5.5, *)
43+
struct FakeTransport: ActorTransport {
44+
func decodeIdentity(from decoder: Decoder) throws -> AnyActorIdentity {
45+
print("FakeTransport.decodeIdentity from:\(decoder)")
46+
let address = try ActorAddress(from: decoder)
47+
return AnyActorIdentity(address)
48+
}
49+
50+
func resolve<Act>(_ identity: AnyActorIdentity, as actorType: Act.Type) throws -> Act?
51+
where Act: DistributedActor {
52+
print("resolve type:\(actorType), address:\(identity)")
53+
return nil
54+
}
55+
56+
func assignIdentity<Act>(_ actorType: Act.Type) -> AnyActorIdentity
57+
where Act: DistributedActor {
58+
let address = ActorAddress(parse: "xxx")
59+
print("assign type:\(actorType), address:\(address)")
60+
return .init(address)
61+
}
62+
63+
public func actorReady<Act>(_ actor: Act) where Act: DistributedActor {
64+
print("ready actor:\(actor), address:\(actor.id)")
65+
}
66+
67+
func resignIdentity(_ identity: AnyActorIdentity) {
68+
print("resign address:\(identity)")
69+
}
70+
}
71+
72+
// ==== Test Coding ------------------------------------------------------------
73+
74+
@available(SwiftStdlib 5.5, *)
75+
class TestEncoder: Encoder {
76+
var codingPath: [CodingKey]
77+
var userInfo: [CodingUserInfoKey: Any]
78+
79+
var data: String? = nil
80+
81+
init(transport: ActorTransport) {
82+
self.codingPath = []
83+
self.userInfo = [.actorTransportKey: transport]
84+
}
85+
86+
func container<Key>(keyedBy type: Key.Type) -> KeyedEncodingContainer<Key> {
87+
fatalError("Not implemented: \(#function)")
88+
}
89+
90+
func unkeyedContainer() -> UnkeyedEncodingContainer {
91+
fatalError("Not implemented: \(#function)")
92+
}
93+
94+
func singleValueContainer() -> SingleValueEncodingContainer {
95+
TestSingleValueEncodingContainer(parent: self)
96+
}
97+
98+
class TestSingleValueEncodingContainer: SingleValueEncodingContainer {
99+
let parent: TestEncoder
100+
init(parent: TestEncoder) {
101+
self.parent = parent
102+
}
103+
104+
var codingPath: [CodingKey] = []
105+
106+
func encodeNil() throws { fatalError("Not implemented: \(#function)") }
107+
func encode(_ value: Bool) throws { fatalError("Not implemented: \(#function)") }
108+
func encode(_ value: String) throws {
109+
110+
}
111+
func encode(_ value: Double) throws { fatalError("Not implemented: \(#function)") }
112+
func encode(_ value: Float) throws { fatalError("Not implemented: \(#function)") }
113+
func encode(_ value: Int) throws { fatalError("Not implemented: \(#function)") }
114+
func encode(_ value: Int8) throws { fatalError("Not implemented: \(#function)") }
115+
func encode(_ value: Int16) throws { fatalError("Not implemented: \(#function)") }
116+
func encode(_ value: Int32) throws { fatalError("Not implemented: \(#function)") }
117+
func encode(_ value: Int64) throws { fatalError("Not implemented: \(#function)") }
118+
func encode(_ value: UInt) throws { fatalError("Not implemented: \(#function)") }
119+
func encode(_ value: UInt8) throws { fatalError("Not implemented: \(#function)") }
120+
func encode(_ value: UInt16) throws { fatalError("Not implemented: \(#function)") }
121+
func encode(_ value: UInt32) throws { fatalError("Not implemented: \(#function)") }
122+
func encode(_ value: UInt64) throws { fatalError("Not implemented: \(#function)") }
123+
func encode<T: Encodable>(_ value: T) throws {
124+
print("encode: \(value)")
125+
if let identity = value as? AnyActorIdentity {
126+
self.parent.data =
127+
(identity.underlying as! ActorAddress).address
128+
}
129+
}
130+
}
131+
132+
func encode<Act: DistributedActor>(_ actor: Act) throws -> String {
133+
try actor.encode(to: self)
134+
return self.data!
135+
}
136+
}
137+
138+
@available(SwiftStdlib 5.5, *)
139+
class TestDecoder: Decoder {
140+
let encoder: TestEncoder
141+
let data: String
142+
143+
init(encoder: TestEncoder, transport: ActorTransport, data: String) {
144+
self.encoder = encoder
145+
self.userInfo = [.actorTransportKey: transport]
146+
self.data = data
147+
}
148+
149+
var codingPath: [CodingKey] = []
150+
var userInfo: [CodingUserInfoKey : Any]
151+
152+
func container<Key>(keyedBy type: Key.Type) throws -> KeyedDecodingContainer<Key> where Key : CodingKey {
153+
fatalError("Not implemented: \(#function)")
154+
}
155+
func unkeyedContainer() throws -> UnkeyedDecodingContainer {
156+
fatalError("Not implemented: \(#function)")
157+
}
158+
func singleValueContainer() throws -> SingleValueDecodingContainer {
159+
TestSingleValueDecodingContainer(parent: self)
160+
}
161+
162+
class TestSingleValueDecodingContainer: SingleValueDecodingContainer {
163+
let parent: TestDecoder
164+
init(parent: TestDecoder) {
165+
self.parent = parent
166+
}
167+
168+
var codingPath: [CodingKey] = []
169+
func decodeNil() -> Bool { fatalError("Not implemented: \(#function)") }
170+
func decode(_ type: Bool.Type) throws -> Bool { fatalError("Not implemented: \(#function)") }
171+
func decode(_ type: String.Type) throws -> String {
172+
print("decode String -> \(self.parent.data)")
173+
return self.parent.data
174+
}
175+
func decode(_ type: Double.Type) throws -> Double { fatalError("Not implemented: \(#function)") }
176+
func decode(_ type: Float.Type) throws -> Float { fatalError("Not implemented: \(#function)") }
177+
func decode(_ type: Int.Type) throws -> Int { fatalError("Not implemented: \(#function)") }
178+
func decode(_ type: Int8.Type) throws -> Int8 { fatalError("Not implemented: \(#function)") }
179+
func decode(_ type: Int16.Type) throws -> Int16 { fatalError("Not implemented: \(#function)") }
180+
func decode(_ type: Int32.Type) throws -> Int32 { fatalError("Not implemented: \(#function)") }
181+
func decode(_ type: Int64.Type) throws -> Int64 { fatalError("Not implemented: \(#function)") }
182+
func decode(_ type: UInt.Type) throws -> UInt { fatalError("Not implemented: \(#function)") }
183+
func decode(_ type: UInt8.Type) throws -> UInt8 { fatalError("Not implemented: \(#function)") }
184+
func decode(_ type: UInt16.Type) throws -> UInt16 { fatalError("Not implemented: \(#function)") }
185+
func decode(_ type: UInt32.Type) throws -> UInt32 { fatalError("Not implemented: \(#function)") }
186+
func decode(_ type: UInt64.Type) throws -> UInt64 { fatalError("Not implemented: \(#function)") }
187+
func decode<T>(_ type: T.Type) throws -> T where T : Decodable { fatalError("Not implemented: \(#function)") }
188+
}
189+
}
190+
191+
// ==== Execute ----------------------------------------------------------------
192+
193+
@available(SwiftStdlib 5.5, *)
194+
func test() {
195+
let transport = FakeTransport()
196+
197+
// CHECK: assign type:DA, address:ActorAddress(address: "xxx")
198+
let da = DA(transport: transport)
199+
200+
// CHECK: encode: AnyActorIdentity(ActorAddress(address: "xxx"))
201+
// CHECK: FakeTransport.decodeIdentity from:main.TestDecoder
202+
let encoder = TestEncoder(transport: transport)
203+
let data = try! encoder.encode(da)
204+
205+
// CHECK: decode String -> xxx
206+
// CHECK: decode ActorAddress -> ActorAddress(address: "xxx")
207+
let da2 = try! DA(from: TestDecoder(encoder: encoder, transport: transport, data: data))
208+
209+
// CHECK: decoded da2: DA(AnyActorIdentity(ActorAddress(address: "xxx")))
210+
print("decoded da2: \(da2)")
211+
}
212+
213+
@available(SwiftStdlib 5.5, *)
214+
@main struct Main {
215+
static func main() async {
216+
test()
217+
}
218+
}

0 commit comments

Comments
 (0)