mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[Distributed] Implicit Codable conformance for Dist Actors
[Witness] implement dump() on witness
This commit is contained in:
@@ -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 */
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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));
|
||||||
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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!
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>"))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
|||||||
19
test/Distributed/distributed_actor_implicit_codable.swift
Normal file
19
test/Distributed/distributed_actor_implicit_codable.swift
Normal 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
|
||||||
|
}
|
||||||
@@ -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}}
|
||||||
|
|||||||
Reference in New Issue
Block a user