[Distributed] Implicit Codable conformance for Dist Actors

[Witness] implement dump() on witness
This commit is contained in:
Konrad `ktoso` Malawski
2022-03-09 00:20:04 +09:00
parent d3702bacbb
commit 6d502fc042
19 changed files with 432 additions and 44 deletions

View File

@@ -106,6 +106,7 @@ getDistributedSerializationRequirements(
llvm::SmallPtrSet<ProtocolDecl *, 2> llvm::SmallPtrSet<ProtocolDecl *, 2>
extractDistributedSerializationRequirements( extractDistributedSerializationRequirements(
ASTContext &C, ArrayRef<Requirement> allRequirements); ASTContext &C, ArrayRef<Requirement> allRequirements);
} }
#endif /* SWIFT_DECL_TYPECHECKDISTRIBUTED_H */ #endif /* SWIFT_DECL_TYPECHECKDISTRIBUTED_H */

View File

@@ -1039,6 +1039,30 @@ public:
bool isCached() const { return true; } bool isCached() const { return true; }
}; };
/// Retrieve the implicit conformance for the given distributed actor type to
/// the Codable protocol protocol.
///
/// Similar to 'GetImplicitSendableRequest'.
class GetDistributedActorImplicitCodableRequest :
public SimpleRequest<GetDistributedActorImplicitCodableRequest,
NormalProtocolConformance *(NominalTypeDecl *, KnownProtocolKind),
RequestFlags::Cached> {
public:
using SimpleRequest::SimpleRequest;
private:
friend SimpleRequest;
NormalProtocolConformance *evaluate(
Evaluator &evaluator,
NominalTypeDecl *nominal,
KnownProtocolKind protoKind) const;
public:
// Caching
bool isCached() const { return true; }
};
/// Obtain the 'remoteCall' function of a 'DistributedActorSystem'. /// Obtain the 'remoteCall' function of a 'DistributedActorSystem'.
class GetDistributedActorSystemRemoteCallFunctionRequest : class GetDistributedActorSystemRemoteCallFunctionRequest :
public SimpleRequest<GetDistributedActorSystemRemoteCallFunctionRequest, public SimpleRequest<GetDistributedActorSystemRemoteCallFunctionRequest,

View File

@@ -107,6 +107,9 @@ SWIFT_REQUEST(TypeChecker, IsDefaultActorRequest,
Cached, NoLocationInfo) Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, IsDistributedActorRequest, bool(NominalTypeDecl *), SWIFT_REQUEST(TypeChecker, IsDistributedActorRequest, bool(NominalTypeDecl *),
Cached, NoLocationInfo) Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, GetDistributedActorImplicitCodableRequest,
NormalProtocolConformance *(NominalTypeDecl *, KnownProtocolKind),
Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, GetDistributedActorSystemRemoteCallFunctionRequest, SWIFT_REQUEST(TypeChecker, GetDistributedActorSystemRemoteCallFunctionRequest,
AbstractFunctionDecl *(NominalTypeDecl *, bool), AbstractFunctionDecl *(NominalTypeDecl *, bool),
Cached, NoLocationInfo) Cached, NoLocationInfo)

View File

@@ -102,7 +102,7 @@ Type swift::getDistributedActorSystemType(NominalTypeDecl *actor) {
auto DA = C.getDistributedActorDecl(); auto DA = C.getDistributedActorDecl();
if (!DA) if (!DA)
return ErrorType::get(C); return ErrorType::get(C); // FIXME(distributed): just use Type()
// Dig out the actor system type. // Dig out the actor system type.
auto module = actor->getParentModule(); auto module = actor->getParentModule();

View File

@@ -1046,8 +1046,20 @@ ModuleDecl::lookupExistentialConformance(Type type, ProtocolDecl *protocol) {
} }
/// Whether we should create missing conformances to the given protocol. /// Whether we should create missing conformances to the given protocol.
static bool shouldCreateMissingConformances(ProtocolDecl *proto) { static bool shouldCreateMissingConformances(Type type, ProtocolDecl *proto) {
return proto->isSpecificProtocol(KnownProtocolKind::Sendable); // Sendable may be able to be synthesized.
if (proto->isSpecificProtocol(KnownProtocolKind::Sendable)) {
return true;
}
// A 'distributed actor' may have to create missing Codable conformances.
if (auto nominal = dyn_cast_or_null<ClassDecl>(type->getAnyNominal())) {
return nominal->isDistributedActor() &&
(proto->isSpecificProtocol(swift::KnownProtocolKind::Decodable) ||
proto->isSpecificProtocol(swift::KnownProtocolKind::Encodable));
}
return false;
} }
ProtocolConformanceRef ProtocolConformanceRef::forMissingOrInvalid( ProtocolConformanceRef ProtocolConformanceRef::forMissingOrInvalid(
@@ -1055,7 +1067,7 @@ ProtocolConformanceRef ProtocolConformanceRef::forMissingOrInvalid(
// Introduce "missing" conformances when appropriate, so that type checking // Introduce "missing" conformances when appropriate, so that type checking
// (and even code generation) can continue. // (and even code generation) can continue.
ASTContext &ctx = proto->getASTContext(); ASTContext &ctx = proto->getASTContext();
if (shouldCreateMissingConformances(proto)) { if (shouldCreateMissingConformances(type, proto)) {
return ProtocolConformanceRef( return ProtocolConformanceRef(
ctx.getBuiltinConformance( ctx.getBuiltinConformance(
type, proto, GenericSignature(), { }, type, proto, GenericSignature(), { },
@@ -1087,7 +1099,7 @@ ProtocolConformanceRef ModuleDecl::lookupConformance(Type type,
// If we aren't supposed to allow missing conformances through for this // If we aren't supposed to allow missing conformances through for this
// protocol, replace the result with an "invalid" result. // protocol, replace the result with an "invalid" result.
if (!allowMissing && if (!allowMissing &&
shouldCreateMissingConformances(protocol) && shouldCreateMissingConformances(type, protocol) &&
result.hasMissingConformance(this)) result.hasMissingConformance(this))
return ProtocolConformanceRef::forInvalid(); return ProtocolConformanceRef::forInvalid();
@@ -1307,18 +1319,40 @@ LookupConformanceInModuleRequest::evaluate(
// Find the (unspecialized) conformance. // Find the (unspecialized) conformance.
SmallVector<ProtocolConformance *, 2> conformances; SmallVector<ProtocolConformance *, 2> conformances;
if (!nominal->lookupConformance(protocol, conformances)) { if (!nominal->lookupConformance(protocol, conformances)) {
if (!protocol->isSpecificProtocol(KnownProtocolKind::Sendable)) if (protocol->isSpecificProtocol(KnownProtocolKind::Sendable)) {
return ProtocolConformanceRef::forMissingOrInvalid(type, protocol); // Try to infer Sendable conformance.
GetImplicitSendableRequest cvRequest{nominal};
if (auto conformance = evaluateOrDefault(
ctx.evaluator, cvRequest, nullptr)) {
conformances.clear();
conformances.push_back(conformance);
} else {
return ProtocolConformanceRef::forMissingOrInvalid(type, protocol);
}
} else if (protocol->isSpecificProtocol(KnownProtocolKind::Encodable) ||
protocol->isSpecificProtocol(KnownProtocolKind::Decodable)) {
if (nominal->isDistributedActor()) {
auto protoKind =
protocol->isSpecificProtocol(KnownProtocolKind::Encodable)
? KnownProtocolKind::Encodable
: KnownProtocolKind::Decodable;
auto request = GetDistributedActorImplicitCodableRequest{
nominal, protoKind};
// Try to infer Sendable conformance. if (auto conformance =
GetImplicitSendableRequest cvRequest{nominal}; evaluateOrDefault(ctx.evaluator, request, nullptr)) {
if (auto conformance = evaluateOrDefault( conformances.clear();
ctx.evaluator, cvRequest, nullptr)) { conformances.push_back(conformance);
conformances.clear(); } else {
conformances.push_back(conformance); return ProtocolConformanceRef::forMissingOrInvalid(type, protocol);
} else { }
return ProtocolConformanceRef::forMissingOrInvalid(type, protocol); } else {
return ProtocolConformanceRef::forMissingOrInvalid(type, protocol);
}
} }
// Was unable to infer the missing conformance.
return ProtocolConformanceRef::forMissingOrInvalid(type, protocol);
} }
assert(!conformances.empty()); assert(!conformances.empty());

View File

@@ -19,6 +19,7 @@
#include "swift/AST/ASTContext.h" #include "swift/AST/ASTContext.h"
#include "swift/AST/Availability.h" #include "swift/AST/Availability.h"
#include "swift/AST/Decl.h" #include "swift/AST/Decl.h"
#include "swift/AST/DistributedDecl.h"
#include "swift/AST/FileUnit.h" #include "swift/AST/FileUnit.h"
#include "swift/AST/GenericEnvironment.h" #include "swift/AST/GenericEnvironment.h"
#include "swift/AST/LazyResolver.h" #include "swift/AST/LazyResolver.h"
@@ -63,7 +64,12 @@ Witness::Witness(ValueDecl *decl, SubstitutionMap substitutions,
void Witness::dump() const { dump(llvm::errs()); } void Witness::dump() const { dump(llvm::errs()); }
void Witness::dump(llvm::raw_ostream &out) const { void Witness::dump(llvm::raw_ostream &out) const {
// FIXME: Implement! out << "Witness: ";
if (auto decl = this->getDecl()) {
decl->print(out);
} else {
out << "<no decl>\n";
}
} }
ProtocolConformanceRef::ProtocolConformanceRef(ProtocolDecl *protocol, ProtocolConformanceRef::ProtocolConformanceRef(ProtocolDecl *protocol,
@@ -1293,10 +1299,11 @@ void NominalTypeDecl::prepareConformanceTable() const {
// Actor classes conform to the actor protocol. // Actor classes conform to the actor protocol.
if (auto classDecl = dyn_cast<ClassDecl>(mutableThis)) { if (auto classDecl = dyn_cast<ClassDecl>(mutableThis)) {
if (classDecl->isDistributedActor()) if (classDecl->isDistributedActor()) {
addSynthesized(KnownProtocolKind::DistributedActor); addSynthesized(KnownProtocolKind::DistributedActor);
else if (classDecl->isActor()) } else if (classDecl->isActor()) {
addSynthesized(KnownProtocolKind::Actor); addSynthesized(KnownProtocolKind::Actor);
}
} }
// Global actors conform to the GlobalActor protocol. // Global actors conform to the GlobalActor protocol.
@@ -1367,26 +1374,29 @@ IterableDeclContext::getLocalProtocols(ConformanceLookupKind lookupKind) const {
return result; return result;
} }
/// Find a synthesized Sendable conformance in this declaration context,
/// if there is one.
static ProtocolConformance *findSynthesizedSendableConformance(
const DeclContext *dc) {
auto nominal = dc->getSelfNominalTypeDecl();
if (!nominal)
return nullptr;
if (isa<ProtocolDecl>(nominal))
/// Find a synthesized conformance in this declaration context, if there is one.
static ProtocolConformance *
findSynthesizedConformance(
const DeclContext *dc,
KnownProtocolKind protoKind) {
auto nominal = dc->getSelfNominalTypeDecl();
// Perform some common checks
if (!nominal)
return nullptr; return nullptr;
if (dc->getParentModule() != nominal->getParentModule()) if (dc->getParentModule() != nominal->getParentModule())
return nullptr; return nullptr;
auto cvProto = nominal->getASTContext().getProtocol( auto &C = nominal->getASTContext();
KnownProtocolKind::Sendable); auto cvProto = C.getProtocol(protoKind);
if (!cvProto) if (!cvProto)
return nullptr; return nullptr;
auto conformance = dc->getParentModule()->lookupConformance( auto module = dc->getParentModule();
auto conformance = module->lookupConformance(
nominal->getDeclaredInterfaceType(), cvProto); nominal->getDeclaredInterfaceType(), cvProto);
if (!conformance || !conformance.isConcrete()) if (!conformance || !conformance.isConcrete())
return nullptr; return nullptr;
@@ -1405,6 +1415,43 @@ static ProtocolConformance *findSynthesizedSendableConformance(
return normal; return normal;
} }
/// Find any synthesized conformances for given decl context.
///
/// Some protocol conformances can be synthesized by the compiler,
/// for those, we need to add them to "local conformances" because otherwise
/// we'd get missing symbols while attempting to use these.
static SmallVector<ProtocolConformance *, 2> findSynthesizedConformances(
const DeclContext *dc) {
auto nominal = dc->getSelfNominalTypeDecl();
if (!nominal)
return {};
// Try to find specific conformances
SmallVector<ProtocolConformance *, 2> result;
// Sendable may be synthesized for concrete types
if (!isa<ProtocolDecl>(nominal)) {
if (auto sendable =
findSynthesizedConformance(dc, KnownProtocolKind::Sendable)) {
result.push_back(sendable);
}
}
/// Distributed actors can synthesize Encodable/Decodable, so look for those
if (nominal->isDistributedActor()) {
if (auto conformance =
findSynthesizedConformance(dc, KnownProtocolKind::Encodable)) {
result.push_back(conformance);
}
if (auto conformance =
findSynthesizedConformance(dc, KnownProtocolKind::Decodable)) {
result.push_back(conformance);
}
}
return result;
}
std::vector<ProtocolConformance *> std::vector<ProtocolConformance *>
LookupAllConformancesInContextRequest::evaluate( LookupAllConformancesInContextRequest::evaluate(
Evaluator &eval, const IterableDeclContext *IDC) const { Evaluator &eval, const IterableDeclContext *IDC) const {
@@ -1485,8 +1532,9 @@ IterableDeclContext::getLocalConformances(ConformanceLookupKind lookupKind)
// Look for a Sendable conformance globally. If it is synthesized // Look for a Sendable conformance globally. If it is synthesized
// and matches this declaration context, use it. // and matches this declaration context, use it.
auto dc = getAsGenericContext(); auto dc = getAsGenericContext();
if (auto conformance = findSynthesizedSendableConformance(dc)) for (auto conformance : findSynthesizedConformances(dc)) {
result.push_back(conformance); result.push_back(conformance);
}
break; break;
} }

View File

@@ -836,8 +836,9 @@ void SILGenFunction::emitClassConstructorInitializer(ConstructorDecl *ctor) {
} }
// Distributed actor initializers implicitly initialize their transport and id // Distributed actor initializers implicitly initialize their transport and id
if (isDesignatedDistActorInit) if (isDesignatedDistActorInit) {
emitDistActorImplicitPropertyInits(ctor, selfArg); emitDistributedActorImplicitPropertyInits(ctor, selfArg);
}
// Prepare the end of initializer location. // Prepare the end of initializer location.
SILLocation endOfInitLoc = RegularLocation(ctor); SILLocation endOfInitLoc = RegularLocation(ctor);

View File

@@ -223,7 +223,7 @@ public:
}; };
} // end anonymous namespace } // end anonymous namespace
void SILGenFunction::emitDistActorImplicitPropertyInits( void SILGenFunction::emitDistributedActorImplicitPropertyInits(
ConstructorDecl *ctor, ManagedValue selfArg) { ConstructorDecl *ctor, ManagedValue selfArg) {
// Only designated initializers should perform this initialization. // Only designated initializers should perform this initialization.
assert(ctor->isDesignatedInit()); assert(ctor->isDesignatedInit());

View File

@@ -2048,7 +2048,7 @@ public:
/// Initializes the implicit stored properties of a distributed actor that correspond to /// Initializes the implicit stored properties of a distributed actor that correspond to
/// its transport and identity. /// its transport and identity.
void emitDistActorImplicitPropertyInits( void emitDistributedActorImplicitPropertyInits(
ConstructorDecl *ctor, ManagedValue selfArg); ConstructorDecl *ctor, ManagedValue selfArg);
/// Given a function representing a distributed actor factory, emits the /// Given a function representing a distributed actor factory, emits the

View File

@@ -300,12 +300,13 @@ static ConstructorDecl *createImplicitConstructor(NominalTypeDecl *decl,
if (swift::ensureDistributedModuleLoaded(decl)) { if (swift::ensureDistributedModuleLoaded(decl)) {
// copy access level of distributed actor init from the nominal decl // copy access level of distributed actor init from the nominal decl
accessLevel = decl->getEffectiveAccess(); accessLevel = decl->getEffectiveAccess();
auto systemTy = getDistributedActorSystemType(classDecl);
// Create the parameter. // Create the parameter.
auto *arg = new (ctx) ParamDecl(SourceLoc(), Loc, ctx.Id_system, Loc, auto *arg = new (ctx) ParamDecl(SourceLoc(), Loc, ctx.Id_system, Loc,
ctx.Id_system, decl); ctx.Id_system, decl);
arg->setSpecifier(ParamSpecifier::Default); arg->setSpecifier(ParamSpecifier::Default);
arg->setInterfaceType(getDistributedActorSystemType(classDecl)); arg->setInterfaceType(systemTy);
arg->setImplicit(); arg->setImplicit();
params.push_back(arg); params.push_back(arg);

View File

@@ -21,6 +21,7 @@
#include "swift/AST/Initializer.h" #include "swift/AST/Initializer.h"
#include "swift/AST/ParameterList.h" #include "swift/AST/ParameterList.h"
#include "swift/AST/TypeCheckRequests.h" #include "swift/AST/TypeCheckRequests.h"
#include "swift/AST/NameLookupRequests.h"
#include "swift/AST/ASTMangler.h" #include "swift/AST/ASTMangler.h"
#include "swift/AST/DistributedDecl.h" #include "swift/AST/DistributedDecl.h"
#include "swift/Basic/Defer.h" #include "swift/Basic/Defer.h"
@@ -29,6 +30,7 @@
#include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringExtras.h"
#include "DerivedConformances.h" #include "DerivedConformances.h"
using namespace swift; using namespace swift;
@@ -552,6 +554,75 @@ static FuncDecl *createDistributedThunkFunction(FuncDecl *func) {
return thunk; return thunk;
} }
/******************************************************************************/
/*********************** CODABLE CONFORMANCE **********************************/
/******************************************************************************/
static NormalProtocolConformance*
addDistributedActorCodableConformance(
ClassDecl *actor, ProtocolDecl *proto) {
assert(proto->isSpecificProtocol(swift::KnownProtocolKind::Decodable) ||
proto->isSpecificProtocol(swift::KnownProtocolKind::Encodable));
auto &C = actor->getASTContext();
auto DC = actor->getDeclContext();
auto module = actor->getParentModule();
// === Only Distributed actors can gain this implicit conformance
if (!actor->isDistributedActor()) {
return nullptr;
}
// === Does the actor explicitly conform to the protocol already?
auto explicitConformance =
module->lookupConformance(actor->getInterfaceType(), proto);
if (!explicitConformance.isInvalid()) {
// ok, it was conformed explicitly -- let's not synthesize;
return nullptr;
}
// Check whether we can infer conformance at all.
if (auto *file = dyn_cast<FileUnit>(actor->getModuleScopeContext())) {
switch (file->getKind()) {
case FileUnitKind::Source:
// Check what kind of source file we have.
if (auto sourceFile = actor->getParentSourceFile()) {
switch (sourceFile->Kind) {
case SourceFileKind::Interface:
return nullptr;
case SourceFileKind::Library:
case SourceFileKind::Main:
case SourceFileKind::SIL:
break;
}
}
break;
case FileUnitKind::Builtin:
case FileUnitKind::SerializedAST:
case FileUnitKind::Synthesized:
// Explicitly-handled modules don't infer Sendable conformances.
return nullptr;
case FileUnitKind::ClangModule:
case FileUnitKind::DWARFModule:
// Infer conformances for imported modules.
break;
}
} else {
return nullptr;
}
auto conformance = C.getConformance(actor->getDeclaredInterfaceType(), proto,
actor->getLoc(), /*dc=*/actor,
ProtocolConformanceState::Incomplete,
/*isUnchecked=*/false);
conformance->setSourceKindAndImplyingConformance(
ConformanceEntryKind::Synthesized, nullptr);
actor->registerProtocolConformance(conformance, /*synthesized=*/true);
return conformance;
}
/******************************************************************************/ /******************************************************************************/
/*********************** SYNTHESIS ENTRY POINTS *******************************/ /*********************** SYNTHESIS ENTRY POINTS *******************************/
/******************************************************************************/ /******************************************************************************/
@@ -621,7 +692,6 @@ VarDecl *GetDistributedActorSystemPropertyRequest::evaluate(
auto module = nominal->getParentModule(); auto module = nominal->getParentModule();
auto DAS = C.getDistributedActorSystemDecl(); auto DAS = C.getDistributedActorSystemDecl();
auto f = DAS->lookupDirect(C.Id_makeInvocationEncoder);
// not via `ensureDistributedModuleLoaded` to avoid generating a warning, // not via `ensureDistributedModuleLoaded` to avoid generating a warning,
// we won't be emitting the offending decl after all. // we won't be emitting the offending decl after all.
@@ -662,3 +732,28 @@ VarDecl *GetDistributedActorSystemPropertyRequest::evaluate(
return nullptr; return nullptr;
} }
NormalProtocolConformance
*GetDistributedActorImplicitCodableRequest::evaluate(
Evaluator &evaluator,
NominalTypeDecl *nominal,
KnownProtocolKind protoKind) const {
assert(nominal->isDistributedActor());
assert(protoKind == KnownProtocolKind::Encodable ||
protoKind == KnownProtocolKind::Decodable);
auto &C = nominal->getASTContext();
// not via `ensureDistributedModuleLoaded` to avoid generating a warning,
// we won't be emitting the offending decl after all.
if (!C.getLoadedModule(C.Id_Distributed))
return nullptr;
auto classDecl = dyn_cast<ClassDecl>(nominal);
if (!classDecl) {
// we only synthesize the conformance for concrete actors
return nullptr;
}
return addDistributedActorCodableConformance(classDecl,
C.getProtocol(protoKind));
}

View File

@@ -23,11 +23,25 @@ import _Concurrency
/// ///
/// The 'DistributedActor' protocol provides the core functionality of any /// The 'DistributedActor' protocol provides the core functionality of any
/// distributed actor. /// distributed actor.
///
/// ## Implicit `Codable` conformance
/// If created with an actor system whose `ActorID` is `Codable`, the
/// compiler will synthesize code for the concrete distributed actor to conform
/// to `Codable` as well. This is necessary to support distributed calls where
/// the `SerializationRequirement` is `Codable` and thus users may want to pass
/// actors as arguments to remote calls.
///
/// The synthesized implementations use a single `SingleValueContainer` to
/// encode/decode the `self.id` property of the actor. The `Decoder` required
/// `init(from:)` is implemented by retrieving an actor system from the
/// decoders' `userInfo`, effectively like this:
/// `decoder.userInfo[.actorSystemKey] as? ActorSystem`. The obtained actor
/// system is then used to `resolve(id:using:)` the decoded ID.
///
/// Use the `CodingUserInfoKey.actorSystemKey` to provide the necessary
/// actor system for the decoding initializer when decoding a distributed actor.
@available(SwiftStdlib 5.7, *) @available(SwiftStdlib 5.7, *)
public protocol DistributedActor: public protocol DistributedActor: AnyActor, Identifiable, Hashable
AnyActor,
Identifiable,
Hashable, Codable
where ID == ActorSystem.ActorID { where ID == ActorSystem.ActorID {
/// The type of transport used to communicate with actors of this type. /// The type of transport used to communicate with actors of this type.
@@ -92,7 +106,7 @@ extension CodingUserInfoKey {
} }
@available(SwiftStdlib 5.7, *) @available(SwiftStdlib 5.7, *)
extension DistributedActor { extension DistributedActor /*: implicitly Decodable */ where Self.ID: Decodable {
nonisolated public init(from decoder: Decoder) throws { nonisolated public init(from decoder: Decoder) throws {
guard let system = decoder.userInfo[.actorSystemKey] as? ActorSystem else { guard let system = decoder.userInfo[.actorSystemKey] as? ActorSystem else {
throw DistributedActorCodingError(message: throw DistributedActorCodingError(message:
@@ -103,7 +117,10 @@ extension DistributedActor {
let id: ID = try Self.ID(from: decoder) let id: ID = try Self.ID(from: decoder)
self = try Self.resolve(id: id, using: system) self = try Self.resolve(id: id, using: system)
} }
}
@available(SwiftStdlib 5.7, *)
extension DistributedActor /*: implicitly Encodable */ where Self.ID: Encodable {
nonisolated public func encode(to encoder: Encoder) throws { nonisolated public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer() var container = encoder.singleValueContainer()
try container.encode(self.id) try container.encode(self.id)

View File

@@ -15,7 +15,7 @@ import _Concurrency
@available(SwiftStdlib 5.7, *) @available(SwiftStdlib 5.7, *)
public protocol DistributedActorSystem: Sendable { public protocol DistributedActorSystem: Sendable {
/// The identity used by actors that communicate via this transport /// The identity used by actors that communicate via this transport
associatedtype ActorID: Sendable & Hashable & Codable // TODO(distributed): make Codable conditional here associatedtype ActorID: Sendable & Hashable
associatedtype InvocationEncoder: DistributedTargetInvocationEncoder associatedtype InvocationEncoder: DistributedTargetInvocationEncoder
associatedtype InvocationDecoder: DistributedTargetInvocationDecoder associatedtype InvocationDecoder: DistributedTargetInvocationDecoder

View File

@@ -367,3 +367,88 @@ fileprivate struct StringsSingleValueEncoding: SingleValueEncodingContainer {
try value.encode(to: stringsEncoding) try value.encode(to: stringsEncoding)
} }
} }
public final class FakeDecoder: Decoder {
public var codingPath: [CodingKey] = []
public var userInfo: [CodingUserInfoKey: Any] = [:]
var string: String = ""
public func decode<T>(_ string: String, as type: T.Type) throws -> T where T: Decodable {
self.string = string
return try type.init(from: self)
}
public func container<Key>(
keyedBy type: Key.Type
) throws -> KeyedDecodingContainer<Key> {
fatalError("\(#function) not implemented")
}
public func unkeyedContainer() throws -> UnkeyedDecodingContainer {
fatalError("\(#function) not implemented")
}
public func singleValueContainer() throws -> SingleValueDecodingContainer {
return FakeSingleValueDecodingContainer(string: self.string)
}
}
public final class FakeSingleValueDecodingContainer: SingleValueDecodingContainer {
public var codingPath: [CodingKey] { [] }
let string: String
init(string: String) {
self.string = string
}
public func decodeNil() -> Bool {
fatalError("\(#function) not implemented")
}
public func decode(_ type: Bool.Type) throws -> Bool {
fatalError("\(#function) not implemented")
}
public func decode(_ type: String.Type) throws -> String {
String(self.string.split(separator: ";").first!)
}
public func decode(_ type: Double.Type) throws -> Double {
fatalError("\(#function) not implemented")
}
public func decode(_ type: Float.Type) throws -> Float {
fatalError("\(#function) not implemented")
}
public func decode(_ type: Int.Type) throws -> Int {
fatalError("\(#function) not implemented")
}
public func decode(_ type: Int8.Type) throws -> Int8 {
fatalError("\(#function) not implemented")
}
public func decode(_ type: Int16.Type) throws -> Int16 {
fatalError("\(#function) not implemented")
}
public func decode(_ type: Int32.Type) throws -> Int32 {
fatalError("\(#function) not implemented")
}
public func decode(_ type: Int64.Type) throws -> Int64 {
fatalError("\(#function) not implemented")
}
public func decode(_ type: UInt.Type) throws -> UInt {
fatalError("\(#function) not implemented")
}
public func decode(_ type: UInt8.Type) throws -> UInt8 {
fatalError("\(#function) not implemented")
}
public func decode(_ type: UInt16.Type) throws -> UInt16 {
fatalError("\(#function) not implemented")
}
public func decode(_ type: UInt32.Type) throws -> UInt32 {
fatalError("\(#function) not implemented")
}
public func decode(_ type: UInt64.Type) throws -> UInt64 {
fatalError("\(#function) not implemented")
}
public func decode<T>(_ type: T.Type) throws -> T where T : Decodable {
fatalError("\(#function) not implemented")
}
}

View File

@@ -179,7 +179,7 @@ class TestEncoder: Encoder {
} }
} }
func encode<Act: DistributedActor>(_ actor: Act) throws -> String { func encode<Act: DistributedActor>(_ actor: Act) throws -> String where Act.ID: Codable {
try actor.encode(to: self) try actor.encode(to: self)
return self.data! return self.data!
} }

View File

@@ -0,0 +1,54 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend-emit-module -emit-module-path %t/FakeCodableForDistributedTests.swiftmodule -module-name FakeCodableForDistributedTests -disable-availability-checking %S/../Inputs/FakeCodableForDistributedTests.swift
// RUN: %target-swift-frontend-emit-module -emit-module-path %t/FakeDistributedActorSystems.swiftmodule -module-name FakeDistributedActorSystems -disable-availability-checking %S/../Inputs/FakeDistributedActorSystems.swift
// XXX: %target-build-swift -emit-silgen -module-name main -Xfrontend -enable-experimental-distributed -Xfrontend -disable-availability-checking -j2 -parse-as-library -I %t %s %S/../Inputs/FakeCodableForDistributedTests.swift %S/../Inputs/FakeDistributedActorSystems.swift
// RUN: %target-build-swift -module-name main -Xfrontend -enable-experimental-distributed -Xfrontend -disable-availability-checking -j2 -parse-as-library -I %t %s %S/../Inputs/FakeCodableForDistributedTests.swift %S/../Inputs/FakeDistributedActorSystems.swift -o %t/a.out
// RUN: %target-run %t/a.out | %FileCheck %s --color
// REQUIRES: executable_test
// REQUIRES: concurrency
// REQUIRES: distributed
// rdar://76038845
// UNSUPPORTED: use_os_stdlib
// UNSUPPORTED: back_deployment_runtime
import _Distributed
import FakeDistributedActorSystems
import FakeCodableForDistributedTests
distributed actor Worker: CustomStringConvertible {
nonisolated var description: Swift.String {
"Worker(\(id))"
}
}
typealias DefaultDistributedActorSystem = FakeRoundtripActorSystem
// ==== Execute ----------------------------------------------------------------
@main struct Main {
static func main() async {
let system = DefaultDistributedActorSystem()
let actor = Worker(system: system)
// CHECK: assign id: ActorAddress(address: "<unique-id>") for Worker
// compilation check:
let _: Encodable = actor
let _: Decodable = actor
// round trip check:
let json = FakeEncoder()
let encoded = try! json.encode(actor)
print("encoded = \(encoded)")
// CHECK: encoded = <unique-id>;
let decoder = FakeDecoder()
decoder.userInfo[.actorSystemKey] = system
let back = try! decoder.decode(encoded, as: Worker.self)
// CHECK: | resolve ActorAddress(address: "<unique-id>")
print("back = \(back)")
// CHECK: back = Worker(ActorAddress(address: "<unique-id>"))
}
}

View File

@@ -113,7 +113,6 @@ distributed actor Greeter {
} }
} }
// ==== Fake Transport --------------------------------------------------------- // ==== Fake Transport ---------------------------------------------------------
struct ActorAddress: Sendable, Hashable, Codable { struct ActorAddress: Sendable, Hashable, Codable {
let address: String let address: String

View File

@@ -0,0 +1,19 @@
// 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-swift-frontend -typecheck -verify -enable-experimental-distributed -disable-availability-checking -I %t 2>&1 %s
// REQUIRES: concurrency
// REQUIRES: distributed
import _Distributed
import FakeDistributedActorSystems
typealias DefaultDistributedActorSystem = FakeActorSystem
distributed actor DA {
}
func take<A: Codable>(actor: A) {}
func test(actorSystem: FakeActorSystem) {
take(actor: DA(system: actorSystem)) // ok
}

View File

@@ -22,6 +22,9 @@ distributed actor D2 {
distributed actor D3 { distributed actor D3 {
// expected-error@-1{{type 'D3' does not conform to protocol 'Identifiable'}} // expected-error@-1{{type 'D3' does not conform to protocol 'Identifiable'}}
// expected-error@-2{{type 'D3' does not conform to protocol 'DistributedActor'}} // expected-error@-2{{type 'D3' does not conform to protocol 'DistributedActor'}}
// Codable synthesis also will fail since the ID mismatch:
// expected-error@-4{{type 'D3' does not conform to protocol 'Decodable'}}
// expected-error@-5{{type 'D3' does not conform to protocol 'Encodable'}}
var id: Int { 0 } var id: Int { 0 }
// expected-error@-1{{property 'id' cannot be defined explicitly, as it conflicts with distributed actor synthesized stored property}} // expected-error@-1{{property 'id' cannot be defined explicitly, as it conflicts with distributed actor synthesized stored property}}
@@ -35,6 +38,10 @@ distributed actor D4 {
// expected-error@-1{{actor 'D4' has no initializers}} // expected-error@-1{{actor 'D4' has no initializers}}
// expected-error@-2{{type 'D4' does not conform to protocol 'DistributedActor'}} // expected-error@-2{{type 'D4' does not conform to protocol 'DistributedActor'}}
// expected-error@-3{{type 'D4' does not conform to protocol 'Identifiable'}} // expected-error@-3{{type 'D4' does not conform to protocol 'Identifiable'}}
// Codable synthesis also will fail since the ID errors:
// expected-error@-5{{type 'D4' does not conform to protocol 'Decodable'}}
// expected-error@-6{{type 'D4' does not conform to protocol 'Encodable'}}
let actorSystem: String let actorSystem: String
// expected-error@-1{{invalid redeclaration of synthesized property 'actorSystem'}} // expected-error@-1{{invalid redeclaration of synthesized property 'actorSystem'}}
// expected-error@-2{{property 'actorSystem' cannot be defined explicitly, as it conflicts with distributed actor synthesized stored property}} // expected-error@-2{{property 'actorSystem' cannot be defined explicitly, as it conflicts with distributed actor synthesized stored property}}