Skip to content

[Distributed] Move property synthesis to DerivedConformanceDistributedActor #39046

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 6 commits into from
Aug 26, 2021
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
154 changes: 0 additions & 154 deletions lib/Sema/CodeSynthesisDistributedActor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,158 +30,6 @@
#include "DerivedConformances.h"
using namespace swift;

/******************************************************************************/
/******************************* RESOLVE FUNCTION *****************************/
/******************************************************************************/

/// Synthesizes the
///
/// \verbatim
/// static resolve(_ address: ActorAddress,
/// using transport: ActorTransport) throws -> Self {
/// <filled in by SILGenDistributed>
/// }
/// \endverbatim
///
/// factory function in the AST, with an empty body. Its body is
/// expected to be filled-in during SILGen.
// TODO(distributed): move this synthesis to DerivedConformance style
static void addFactoryResolveFunction(ClassDecl *decl) {
assert(decl->isDistributedActor());
auto &C = decl->getASTContext();

auto mkParam = [&](Identifier argName, Identifier paramName, Type ty) -> ParamDecl* {
auto *param = new (C) ParamDecl(SourceLoc(),
SourceLoc(), argName,
SourceLoc(), paramName, decl);
param->setImplicit();
param->setSpecifier(ParamSpecifier::Default);
param->setInterfaceType(ty);
return param;
};

auto addressType = C.getAnyActorIdentityDecl()->getDeclaredInterfaceType();
auto transportType = C.getActorTransportDecl()->getDeclaredInterfaceType();

// (_ identity: AnyActorIdentity, using transport: ActorTransport)
auto *params = ParameterList::create(
C,
/*LParenLoc=*/SourceLoc(),
/*params=*/{ mkParam(Identifier(), C.Id_identity, addressType),
mkParam(C.Id_using, C.Id_transport, transportType)
},
/*RParenLoc=*/SourceLoc()
);

// Func name: resolve(_:using:)
DeclName name(C, C.Id_resolve, params);

// Expected type: (Self) -> (AnyActorIdentity, ActorTransport) throws -> (Self)
auto *factoryDecl =
FuncDecl::createImplicit(C, StaticSpellingKind::KeywordStatic,
name, SourceLoc(),
/*async=*/false,
/*throws=*/true,
/*genericParams=*/nullptr,
params,
/*returnType*/decl->getDeclaredInterfaceType(),
decl);

factoryDecl->setDistributedActorFactory(); // TODO(distributed): should we mark this specifically as the resolve factory?
factoryDecl->copyFormalAccessFrom(decl, /*sourceIsParentContext=*/true);

decl->addMember(factoryDecl);
}

/******************************************************************************/
/******************************** PROPERTIES **********************************/
/******************************************************************************/

// TODO: deduplicate with 'declareDerivedProperty' from DerivedConformance...
std::pair<VarDecl *, PatternBindingDecl *>
createStoredProperty(ClassDecl *classDecl, ASTContext &ctx,
VarDecl::Introducer introducer, Identifier name,
Type propertyInterfaceType, Type propertyContextType,
bool isStatic, bool isFinal) {
auto parentDC = classDecl;

VarDecl *propDecl = new (ctx)
VarDecl(/*IsStatic*/ isStatic, introducer,
SourceLoc(), name, parentDC);
propDecl->setImplicit();
propDecl->setSynthesized();
propDecl->copyFormalAccessFrom(classDecl, /*sourceIsParentContext*/ true);
propDecl->setInterfaceType(propertyInterfaceType);

Pattern *propPat = NamedPattern::createImplicit(ctx, propDecl);
propPat->setType(propertyContextType);

propPat = TypedPattern::createImplicit(ctx, propPat, propertyContextType);
propPat->setType(propertyContextType);

auto *pbDecl = PatternBindingDecl::createImplicit(
ctx, StaticSpellingKind::None, propPat, /*InitExpr*/ nullptr,
parentDC);
return {propDecl, pbDecl};
}

/// Adds the following, fairly special, properties to each distributed actor:
/// - actorTransport
/// - id
// TODO(distributed): move this synthesis to DerivedConformance style
static void addImplicitDistributedActorStoredProperties(ClassDecl *decl) {
assert(decl->isDistributedActor());

auto &C = decl->getASTContext();

// ```
// @_distributedActorIndependent
// let id: AnyActorIdentity
// ```
{
auto propertyType = C.getAnyActorIdentityDecl()->getDeclaredInterfaceType();

VarDecl *propDecl;
PatternBindingDecl *pbDecl;
std::tie(propDecl, pbDecl) = createStoredProperty(
decl, C,
VarDecl::Introducer::Let, C.Id_id,
propertyType, propertyType,
/*isStatic=*/false, /*isFinal=*/true);

// mark as @_distributedActorIndependent, allowing access to it from everywhere
propDecl->getAttrs().add(
new (C) DistributedActorIndependentAttr(/*IsImplicit=*/true));

decl->addMember(propDecl);
decl->addMember(pbDecl);
}

// ```
// @_distributedActorIndependent
// let actorTransport: ActorTransport
// ```
// (no need for @actorIndependent because it is an immutable let)
{
auto propertyType = C.getActorTransportDecl()->getDeclaredInterfaceType();

VarDecl *propDecl;
PatternBindingDecl *pbDecl;
std::tie(propDecl, pbDecl) = createStoredProperty(
decl, C,
VarDecl::Introducer::Let, C.Id_actorTransport,
propertyType, propertyType,
/*isStatic=*/false, /*isFinal=*/true);

// mark as @_distributedActorIndependent, allowing access to it from everywhere
propDecl->getAttrs().add(
new (C) DistributedActorIndependentAttr(/*IsImplicit=*/true));

decl->addMember(propDecl);
decl->addMember(pbDecl);
}
}

/******************************************************************************/
/*************************** _REMOTE_ FUNCTIONS *******************************/
/******************************************************************************/
Expand Down Expand Up @@ -354,7 +202,5 @@ void swift::addImplicitDistributedActorMembersToClass(ClassDecl *decl) {
if (!swift::ensureDistributedModuleLoaded(decl))
return;

addFactoryResolveFunction(decl);
addImplicitDistributedActorStoredProperties(decl);
addImplicitRemoteActorFunctions(decl);
}
143 changes: 143 additions & 0 deletions lib/Sema/DerivedConformanceDistributedActor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,151 @@ bool DerivedConformance::canDeriveDistributedActor(
auto classDecl = dyn_cast<ClassDecl>(nominal);
return classDecl && classDecl->isDistributedActor() && dc == nominal;
}

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

/******************************************************************************/
/******************************* RESOLVE FUNCTION *****************************/
/******************************************************************************/

/// Synthesizes the
///
/// \verbatim
/// static resolve(_ address: ActorAddress,
/// using transport: ActorTransport) throws -> Self {
/// <filled in by SILGenDistributed>
/// }
/// \endverbatim
///
/// factory function in the AST, with an empty body. Its body is
/// expected to be filled-in during SILGen.
// TODO(distributed): move this synthesis to DerivedConformance style
static FuncDecl *deriveDistributedActor_resolve(DerivedConformance &derived) {
auto decl = dyn_cast<ClassDecl>(derived.Nominal);
assert(decl->isDistributedActor());
auto &C = decl->getASTContext();

auto mkParam = [&](Identifier argName, Identifier paramName, Type ty) -> ParamDecl* {
auto *param = new (C) ParamDecl(SourceLoc(),
SourceLoc(), argName,
SourceLoc(), paramName, decl);
param->setImplicit();
param->setSpecifier(ParamSpecifier::Default);
param->setInterfaceType(ty);
return param;
};

auto addressType = C.getAnyActorIdentityDecl()->getDeclaredInterfaceType();
auto transportType = C.getActorTransportDecl()->getDeclaredInterfaceType();

// (_ identity: AnyActorIdentity, using transport: ActorTransport)
auto *params = ParameterList::create(
C,
/*LParenLoc=*/SourceLoc(),
/*params=*/{ mkParam(Identifier(), C.Id_identity, addressType),
mkParam(C.Id_using, C.Id_transport, transportType)
},
/*RParenLoc=*/SourceLoc()
);

// Func name: resolve(_:using:)
DeclName name(C, C.Id_resolve, params);

// Expected type: (Self) -> (AnyActorIdentity, ActorTransport) throws -> (Self)
auto *factoryDecl =
FuncDecl::createImplicit(C, StaticSpellingKind::KeywordStatic,
name, SourceLoc(),
/*async=*/false,
/*throws=*/true,
/*genericParams=*/nullptr,
params,
/*returnType*/decl->getDeclaredInterfaceType(),
decl);

factoryDecl->setDistributedActorFactory(); // TODO(distributed): should we mark this specifically as the resolve factory?
factoryDecl->copyFormalAccessFrom(decl, /*sourceIsParentContext=*/true);

derived.addMembersToConformanceContext({factoryDecl});
return factoryDecl;
}

/******************************************************************************/
/******************************* PROPERTIES ***********************************/
/******************************************************************************/

static ValueDecl *deriveDistributedActor_id(DerivedConformance &derived) {
assert(derived.Nominal->isDistributedActor());
auto &C = derived.Context;

// ```
// @_distributedActorIndependent
// let id: AnyActorIdentity
// ```
auto propertyType = C.getAnyActorIdentityDecl()->getDeclaredInterfaceType();

VarDecl *propDecl;
PatternBindingDecl *pbDecl;
std::tie(propDecl, pbDecl) = derived.declareDerivedProperty(
C.Id_id,
propertyType, propertyType,
/*isStatic=*/false, /*isFinal=*/true);

propDecl->setIntroducer(VarDecl::Introducer::Let);

// mark as @_distributedActorIndependent, allowing access to it from everywhere
propDecl->getAttrs().add(
new (C) DistributedActorIndependentAttr(/*IsImplicit=*/true));

derived.addMembersToConformanceContext({ propDecl, pbDecl });
return propDecl;
}

static ValueDecl *deriveDistributedActor_actorTransport(
DerivedConformance &derived) {
assert(derived.Nominal->isDistributedActor());
auto &C = derived.Context;

// ```
// @_distributedActorIndependent
// let actorTransport: ActorTransport
// ```
// (no need for @actorIndependent because it is an immutable let)
auto propertyType = C.getActorTransportDecl()->getDeclaredInterfaceType();

VarDecl *propDecl;
PatternBindingDecl *pbDecl;
std::tie(propDecl, pbDecl) = derived.declareDerivedProperty(
C.Id_actorTransport,
propertyType, propertyType,
/*isStatic=*/false, /*isFinal=*/true);

propDecl->setIntroducer(VarDecl::Introducer::Let);

// mark as @_distributedActorIndependent, allowing access to it from everywhere
propDecl->getAttrs().add(
new (C) DistributedActorIndependentAttr(/*IsImplicit=*/true));

derived.addMembersToConformanceContext({ propDecl, pbDecl });
return propDecl;
}

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

ValueDecl *DerivedConformance::deriveDistributedActor(ValueDecl *requirement) {
if (auto var = dyn_cast<VarDecl>(requirement)) {
if (var->getName() == Context.Id_id)
return deriveDistributedActor_id(*this);

if (var->getName() == Context.Id_actorTransport)
return deriveDistributedActor_actorTransport(*this);
}

if (auto func = dyn_cast<FuncDecl>(requirement)) {
// just a simple name check is enough here,
// if we are invoked here we know for sure it is for the "right" function
if (func->getName().getBaseName() == Context.Id_resolve)
return deriveDistributedActor_resolve(*this);
}

return nullptr;
}
19 changes: 19 additions & 0 deletions lib/Sema/DerivedConformances.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,14 @@ ValueDecl *DerivedConformance::getDerivableRequirement(NominalTypeDecl *nominal,
if (name.isSimpleName(ctx.Id_unownedExecutor))
return getRequirement(KnownProtocolKind::Actor);

// DistributedActor.id
if(name.isSimpleName(ctx.Id_id))
return getRequirement(KnownProtocolKind::DistributedActor);

// DistributedActor.actorTransport
if(name.isSimpleName(ctx.Id_actorTransport))
return getRequirement(KnownProtocolKind::DistributedActor);

return nullptr;
}

Expand Down Expand Up @@ -351,6 +359,17 @@ ValueDecl *DerivedConformance::getDerivableRequirement(NominalTypeDecl *nominal,
return getRequirement(KnownProtocolKind::Hashable);
}

// static DistributedActor.resolve(_:using:)
if (name.isCompoundName() && name.getBaseName() == ctx.Id_resolve &&
func->isStatic()) {
auto argumentNames = name.getArgumentNames();
if (argumentNames.size() == 2 &&
argumentNames[0] == Identifier() &&
argumentNames[1] == ctx.Id_using) {
return getRequirement(KnownProtocolKind::DistributedActor);
}
}

return nullptr;
}

Expand Down
5 changes: 5 additions & 0 deletions lib/Sema/TypeCheckDeclPrimary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2930,6 +2930,11 @@ class DeclChecker : public DeclVisitor<DeclChecker> {

TypeChecker::checkDeclAttributes(ED);

if (nominal->isDistributedActor()) {
auto decl = dyn_cast<ClassDecl>(nominal);
TypeChecker::checkDistributedActor(decl);
}

for (Decl *Member : ED->getMembers())
visit(Member);

Expand Down
11 changes: 7 additions & 4 deletions lib/Sema/TypeCheckDistributed.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,11 @@ void swift::checkDistributedActorProperties(const ClassDecl *decl) {

for (auto member : decl->getMembers()) {
if (auto prop = dyn_cast<VarDecl>(member)) {
if (prop->isSynthesized())
continue;

auto id = prop->getName();
if (id == C.Id_actorTransport ||
id == C.Id_id) {
if (id == C.Id_actorTransport || id == C.Id_id) {
prop->diagnose(diag::distributed_actor_user_defined_special_property,
id);
}
Expand Down Expand Up @@ -213,6 +215,9 @@ void swift::checkDistributedActorConstructor(const ClassDecl *decl, ConstructorD
// ==== ------------------------------------------------------------------------

void TypeChecker::checkDistributedActor(ClassDecl *decl) {
if (!decl)
return;

// ==== Ensure the _Distributed module is available,
// without it there's no reason to check the decl in more detail anyway.
if (!swift::ensureDistributedModuleLoaded(decl))
Expand All @@ -235,7 +240,5 @@ void TypeChecker::checkDistributedActor(ClassDecl *decl) {
// --- Synthesize properties
// TODO: those could technically move to DerivedConformance style
swift::addImplicitDistributedActorMembersToClass(decl);

// ==== Functions
}

Loading