Skip to content

[Distributed] Add more tests for implicit Codable on distributed actors #73049

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend-emit-module -emit-module-path %t/FakeDistributedActorSystems.swiftmodule -module-name FakeDistributedActorSystems -disable-availability-checking %S/../Inputs/FakeDistributedActorSystems.swift
// 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
// RUN: %target-codesign %t/a.out
// RUN: %target-run %t/a.out | %FileCheck %s --dump-input=always

// REQUIRES: executable_test
// REQUIRES: concurrency
// REQUIRES: distributed

// rdar://76038845
// UNSUPPORTED: use_os_stdlib
// UNSUPPORTED: back_deployment_runtime

// UNSUPPORTED: OS=windows-msvc

import Distributed

extension UInt8: CustomSerializationProtocol {
public func toBytes() throws -> [UInt8] {
[self]
}
public static func fromBytes(_ bytes: [UInt8]) throws -> Self {
bytes.first!
}
}

distributed actor Tester {
typealias ActorSystem = FakeCustomSerializationRoundtripActorSystem

distributed func echo(param: UInt8) -> UInt8 {
param
}
}

// ==== ------------------------------------------------------------------------

func test() async throws {
let system = FakeCustomSerializationRoundtripActorSystem()

let local = Tester(actorSystem: system)
let ref = try Tester.resolve(id: local.id, using: system)

let reply = try await ref.echo(param: 2)
// CHECK: >> remoteCall: on:main.Tester, target:main.Tester.echo(param:), invocation:FakeCustomSerializationInvocationEncoder(genericSubs: [], arguments: [2], returnType: Optional(Swift.UInt8), errorType: nil), throwing:Swift.Never, returning:Swift.UInt8

// CHECK: << remoteCall return: 2
print("reply: \(reply)")
// CHECK: reply: 2
}

@main struct Main {
static func main() async {
try! await test()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend-emit-module -emit-module-path %t/FakeDistributedActorSystems.swiftmodule -module-name FakeDistributedActorSystems -disable-availability-checking %S/../Inputs/FakeDistributedActorSystems.swift
// 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
// RUN: %target-codesign %t/a.out
// RUN: %target-run %t/a.out | %FileCheck %s --color

// REQUIRES: executable_test
// REQUIRES: concurrency
// REQUIRES: distributed

// We're using Foundation for the JSON Encoder, could be done without but good enough
// REQUIRES: objc_interop


// rdar://76038845
// UNSUPPORTED: use_os_stdlib
// UNSUPPORTED: back_deployment_runtime

import Foundation
import Distributed

final class LocalActorSystem : DistributedActorSystem {
typealias ActorID = LocalTestingActorID
typealias ResultHandler = LocalTestingInvocationResultHandler
typealias InvocationEncoder = LocalTestingInvocationEncoder
typealias InvocationDecoder = LocalTestingInvocationDecoder
typealias SerializationRequirement = any Codable

func makeInvocationEncoder() -> InvocationEncoder { LocalTestingDistributedActorSystem().makeInvocationEncoder() }

func resolve<Act>(id: ActorID, as actorType: Act.Type) throws -> Act? where Act: DistributedActor {
nil
}

func actorReady<Act>(_ actor: Act) where Act: DistributedActor, Act.ID == ActorID {
}

func resignID(_ id: ActorID) {}

func assignID<Act>(_ actorType: Act.Type) -> ActorID
where Act: DistributedActor {
.init(id: "42")
}

func remoteCall<Act, Err, Res>(on actor: Act,
target: RemoteCallTarget,
invocation: inout InvocationEncoder,
throwing: Err.Type,
returning: Res.Type) async throws -> Res
where Act: DistributedActor,
Act.ID == ActorID,
Err: Error,
Res: Codable {
let decoder = JSONDecoder()
return try! decoder.decode(Res.self, from: await fetchData())
}

func remoteCallVoid<Act, Err>(on actor: Act,
target: RemoteCallTarget,
invocation: inout InvocationEncoder,
throwing errorType: Err.Type) async throws
where Act: DistributedActor,
Act.ID == ActorID,
Err: Error {
fatalError("not implemented: \(#function)")
}

func fetchData() async -> Data {
"42".data(using: .ascii)!
}
}

distributed actor NotCodableDA<ActorSystem>
where ActorSystem: DistributedActorSystem<any Codable> {

init(actorSystem: ActorSystem) async {
self.actorSystem = actorSystem

print(try! await self.request())
}

func request() async throws -> Int {
let target = RemoteCallTarget("test.request")
var encoder = actorSystem.makeInvocationEncoder()
return try await actorSystem.remoteCall(on: self,
target: target,
invocation: &encoder,
throwing: Error.self,
returning: Int.self)
}
}

distributed actor CodableDA<ActorSystem>: Codable
where ActorSystem: DistributedActorSystem<any Codable>,
ActorSystem.ActorID: Codable {

init(actorSystem: ActorSystem) async {
self.actorSystem = actorSystem

print(try! await self.request())
}

func request() async throws -> Int {
let target = RemoteCallTarget("test.request")
var encoder = actorSystem.makeInvocationEncoder()
return try await actorSystem.remoteCall(on: self,
target: target,
invocation: &encoder,
throwing: Error.self,
returning: Int.self)
}
}

distributed actor CodableIDDA<ActorSystem>
where ActorSystem: DistributedActorSystem<any Codable>,
ActorSystem.ActorID: Codable {

init(actorSystem: ActorSystem) async {
self.actorSystem = actorSystem

print(try! await self.request())
}

func request() async throws -> Int {
let target = RemoteCallTarget("test.request")
var encoder = actorSystem.makeInvocationEncoder()
return try await actorSystem.remoteCall(on: self,
target: target,
invocation: &encoder,
throwing: Error.self,
returning: Int.self)
}
}

@main struct Main {
static func main() async throws {
if #available(SwiftStdlib 5.9, *) {
let system = LocalActorSystem()
let ncda = await NotCodableDA(actorSystem: system)
let cidda = await CodableIDDA(actorSystem: system)
let cda = await CodableDA(actorSystem: system)

try await ncda.whenLocal {
let got = try await $0.request()

// CHECK: got = 42
print("got = \(got)")
}

try await cidda.whenLocal {
let got = try await $0.request()

// CHECK: got = 42
print("got = \(got)")
}

let _: any (DistributedActor & Codable) = cda
try await cda.whenLocal {
let got = try await $0.request()

// CHECK: got = 42
print("got = \(got)")
}

// CHECK: OK
print("OK")
}
}
}
45 changes: 45 additions & 0 deletions test/Distributed/distributed_actor_implicit_codable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,48 @@ func take<A: Codable>(actor: A) {}
func test(actorSystem: FakeActorSystem) {
take(actor: DA(actorSystem: actorSystem)) // ok
}

func takeCodable<A: Codable>(actor: A) {}

func test_DA(actorSystem: FakeActorSystem) {
takeCodable(actor: DA(actorSystem: actorSystem)) // ok
}

// ==== Generic actors

distributed actor DAG<ActorSystem>
where ActorSystem: DistributedActorSystem<any Codable> {
}
func test_DAG(actorSystem: FakeActorSystem) {
takeCodable(actor: DAG<FakeActorSystem>(actorSystem: actorSystem)) // ok
}

distributed actor DAG_ID<ActorSystem>
where ActorSystem: DistributedActorSystem<any Codable>,
ID: Codable { // expected-error{{cannot find type 'ID' in scope}}
}
func test_DAG_ID(actorSystem: FakeActorSystem) {
takeCodable(actor: DAG_ID<FakeActorSystem>(actorSystem: actorSystem)) // ok
}

distributed actor DAG_ActorSystem_ActorID<ActorSystem>
where ActorSystem: DistributedActorSystem<any Codable>,
ActorSystem.ActorID: Codable {
}
func test_DAG_ActorSystem_ActorID(actorSystem: FakeActorSystem) {
takeCodable(actor: DAG_ActorSystem_ActorID<FakeActorSystem>(actorSystem: actorSystem)) // ok
}

// ==== Not codable cases

protocol SerializableButNotCodable {}

distributed actor DAG_ActorSystem_ActorID_Custom<ActorSystem>
where ActorSystem: DistributedActorSystem<any SerializableButNotCodable> {
// expected-note@-2{{requirement specified as 'ActorSystem.SerializationRequirement' == 'any SerializableButNotCodable'}}
}

func test_DAG_ActorSystem_ActorID_Custom(actorSystem: FakeActorSystem) {
takeCodable(actor: DAG_ActorSystem_ActorID_Custom<FakeActorSystem>(actorSystem: actorSystem))
// expected-error@-1{{'DAG_ActorSystem_ActorID_Custom' requires the types 'any FakeActorSystem.SerializationRequirement' (aka 'any Decodable & Encodable') and 'any SerializableButNotCodable' be equivalent}}
}