Skip to content

[Distributed] inject transport.resign into deinits #38129

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 2 commits into from
Jul 1, 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
1 change: 1 addition & 0 deletions include/swift/AST/KnownIdentifiers.def
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ IDENTIFIER(actorTransport)
IDENTIFIER(actorType)
IDENTIFIER(actorReady)
IDENTIFIER(assignAddress)
IDENTIFIER(resignAddress)
IDENTIFIER(resolve)
IDENTIFIER(address)
IDENTIFIER(actorAddress)
Expand Down
5 changes: 4 additions & 1 deletion include/swift/AST/KnownProtocols.def
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@
BUILTIN_EXPRESSIBLE_BY_LITERAL_PROTOCOL_WITH_NAME(name, "_" #name)

PROTOCOL(Actor)
PROTOCOL(DistributedActor)
PROTOCOL(Sequence)
PROTOCOL(IteratorProtocol)
PROTOCOL(RawRepresentable)
Expand Down Expand Up @@ -95,6 +94,10 @@ PROTOCOL(StringInterpolationProtocol)
PROTOCOL(AdditiveArithmetic)
PROTOCOL(Differentiable)

// Distributed Actors
PROTOCOL(DistributedActor)
PROTOCOL(ActorTransport)

PROTOCOL(AsyncSequence)
PROTOCOL(AsyncIteratorProtocol)

Expand Down
1 change: 1 addition & 0 deletions lib/AST/NameLookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1414,6 +1414,7 @@ NominalTypeDecl::lookupDirectRemoteFunc(AbstractFunctionDecl *func) {
auto remoteFuncId = C.getIdentifier("_remote_" + localFuncName);

auto remoteFuncDecls = selfTyDecl->lookupDirect(DeclName(remoteFuncId));

if (remoteFuncDecls.empty())
return nullptr;

Expand Down
1 change: 1 addition & 0 deletions lib/IRGen/GenMeta.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5165,6 +5165,7 @@ SpecialProtocol irgen::getSpecialProtocolID(ProtocolDecl *P) {
case KnownProtocolKind::Differentiable:
case KnownProtocolKind::FloatingPoint:
case KnownProtocolKind::Actor:
case KnownProtocolKind::ActorTransport:
case KnownProtocolKind::DistributedActor:
case KnownProtocolKind::SerialExecutor:
case KnownProtocolKind::Sendable:
Expand Down
11 changes: 4 additions & 7 deletions lib/SILGen/SILGenBridging.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2045,7 +2045,8 @@ void SILGenFunction::emitDistributedThunk(SILDeclRef thunk) {
B.createCondBranch(loc, isRemoteResultUnwrapped, isRemoteBB, isLocalBB);
}

// if __isRemoteActor(self) {
// // if __isRemoteActor(self)
// {
// return try await self._remote_X(...)
// }
{
Expand All @@ -2054,9 +2055,6 @@ void SILGenFunction::emitDistributedThunk(SILDeclRef thunk) {
auto *selfTyDecl = FunctionDC->getParent()->getSelfNominalTypeDecl();
assert(selfTyDecl && "distributed function declared outside of actor");

// auto selfMetatype = getLoweredType(selfTyDecl->getInterfaceType());
// SILValue metatypeValue = B.createMetatype(loc, selfMetatype);

auto remoteFnDecl = selfTyDecl->lookupDirectRemoteFunc(fd);
assert(remoteFnDecl && "Could not find _remote_<dist_func_name> function");
auto remoteFnRef = SILDeclRef(remoteFnDecl);
Expand All @@ -2068,13 +2066,12 @@ void SILGenFunction::emitDistributedThunk(SILDeclRef thunk) {
auto subs = F.getForwardingSubstitutionMap();

SmallVector<SILValue, 8> remoteParams(params);
// remoteParams.emplace_back(metatypeValue);

// B.createTryApply(loc, remoteFn, subs, params, remoteReturnBB, remoteErrorBB);
B.createTryApply(loc, remoteFn, subs, remoteParams, remoteReturnBB, remoteErrorBB);
}

// else {
// // else
// {
// return (try)? (await)? self.X(...)
// }
{
Expand Down
115 changes: 115 additions & 0 deletions lib/SILGen/SILGenDestructor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
//===----------------------------------------------------------------------===//

#include "SILGenFunction.h"
#include "SILGenFunctionBuilder.h"
#include "RValue.h"
#include "ArgumentScope.h"
#include "swift/AST/GenericSignature.h"
Expand Down Expand Up @@ -75,6 +76,13 @@ void SILGenFunction::emitDestroyingDestructor(DestructorDecl *dd) {
resultSelfValue = selfValue;
}


if (cd->isDistributedActor()) {
SILBasicBlock *continueBB = createBasicBlock();
injectDistributedActorDestructorLifecycleCall(dd, selfValue, continueBB);
B.emitBlock(continueBB);
}

ArgumentScope S(*this, Loc);
ManagedValue borrowedValue =
ManagedValue::forUnmanaged(resultSelfValue).borrow(*this, cleanupLoc);
Expand All @@ -101,6 +109,113 @@ void SILGenFunction::emitDestroyingDestructor(DestructorDecl *dd) {
B.createReturn(returnLoc, resultSelfValue);
}

void SILGenFunction::injectDistributedActorDestructorLifecycleCall(
DestructorDecl *dd, SILValue selfValue, SILBasicBlock *continueBB) {
ASTContext &ctx = getASTContext();

auto cd = cast<ClassDecl>(dd->getDeclContext());
assert(cd->isDistributedActor() &&
"only distributed actors have transport lifecycle hooks in deinit");

RegularLocation Loc(dd);
if (dd->isImplicit())
Loc.markAutoGenerated();

auto selfDecl = dd->getImplicitSelfDecl();
auto selfManagedValue = ManagedValue::forUnmanaged(selfValue);
auto selfType = selfDecl->getType();

// ==== locate: self.actorAddress
auto addressVarDeclRefs = cd->lookupDirect(ctx.Id_actorAddress);
assert(addressVarDeclRefs.size() == 1);
auto *addressVarDeclRef = dyn_cast<VarDecl>(addressVarDeclRefs.front());
assert(addressVarDeclRef);
auto addressRef =
B.createRefElementAddr(Loc, selfValue, addressVarDeclRef,
getLoweredType(addressVarDeclRef->getType()));

// ==== locate: self.actorTransport
auto transportVarDeclRefs = cd->lookupDirect(ctx.Id_actorTransport);
assert(transportVarDeclRefs.size() == 1);
auto *transportVarDeclRef = dyn_cast<VarDecl>(transportVarDeclRefs.front());
auto transportRef =
B.createRefElementAddr(Loc, selfValue, transportVarDeclRef,
getLoweredType(transportVarDeclRef->getType()));

// locate: self.transport.resignAddress(...)
auto *transportDecl = ctx.getActorTransportDecl();
auto resignFnDecls = transportDecl->lookupDirect(ctx.Id_resignAddress);
assert(resignFnDecls.size() == 1);
auto *resignFnDecl = resignFnDecls.front();
auto resignFnRef = SILDeclRef(resignFnDecl);

// we only transport.resignAddress if we are a local actor,
// and thus the address was created by transport.assignAddress.
auto isRemoteBB = createBasicBlock();
auto isLocalBB = createBasicBlock();

// if __isRemoteActor(self) {
// ...
// } else {
// ...
// }
{
FuncDecl *isRemoteFn = ctx.getIsRemoteDistributedActor();
assert(isRemoteFn && "Could not find 'is remote' function, is the "
"'_Distributed' module available?");

ManagedValue selfAnyObject =
B.createInitExistentialRef(Loc, getLoweredType(ctx.getAnyObjectType()),
CanType(selfType), selfManagedValue, {});
auto result = emitApplyOfLibraryIntrinsic(
Loc, isRemoteFn, SubstitutionMap(), {selfAnyObject}, SGFContext());

SILValue isRemoteResult =
std::move(result).forwardAsSingleValue(*this, Loc);
SILValue isRemoteResultUnwrapped =
emitUnwrapIntegerResult(Loc, isRemoteResult);

B.createCondBranch(Loc, isRemoteResultUnwrapped, isRemoteBB, isLocalBB);
}

// if remote
// === <noop>
{
B.emitBlock(isRemoteBB);
// noop, remote actors don't do anything in their deinit
B.createBranch(Loc, continueBB);
}

// if local
// === self.transport.resignAddress(self.address)
{
B.emitBlock(isLocalBB);

auto openedTransport =
OpenedArchetypeType::get(transportVarDeclRef->getType());
auto transportAddr = B.createOpenExistentialAddr(
Loc, /*operand=*/transportRef, getLoweredType(openedTransport),
OpenedExistentialAccess::Immutable);

auto resignFnType = SGM.M.Types.getConstantFunctionType(
TypeExpansionContext::minimal(), resignFnRef);

auto witness = B.createWitnessMethod(
Loc, openedTransport, ProtocolConformanceRef(transportDecl),
resignFnRef, SILType::getPrimitiveObjectType(resignFnType));

auto subs = SubstitutionMap::getProtocolSubstitutions(
transportDecl, openedTransport, ProtocolConformanceRef(transportDecl));

SmallVector<SILValue, 2> params;
params.push_back(addressRef);
params.push_back(transportAddr); // self for the call, as last param

B.createApply(Loc, witness, subs, params);
B.createBranch(Loc, continueBB);
}
}

void SILGenFunction::emitDeallocatingDestructor(DestructorDecl *dd) {
MagicFunctionName = DeclName(SGM.M.getASTContext().getIdentifier("deinit"));

Expand Down
6 changes: 5 additions & 1 deletion lib/SILGen/SILGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -614,10 +614,14 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
/// Emits code for a ClosureExpr.
void emitClosure(AbstractClosureExpr *ce);
/// Generates code for a class destroying destructor. This
/// emits the body code from the DestructorDecl, calls the base class
/// emits the body code from the DestructorDecl, calls the base class
/// destructor, then implicitly releases the elements of the class.
void emitDestroyingDestructor(DestructorDecl *dd);

/// Inject distributed actor and transport interaction code into the destructor.
void injectDistributedActorDestructorLifecycleCall(
DestructorDecl *dd, SILValue selfValue, SILBasicBlock *continueBB);

/// Generates code for an artificial top-level function that starts an
/// application based on a main type and optionally a main type.
void emitArtificialTopLevel(Decl *mainDecl);
Expand Down
84 changes: 79 additions & 5 deletions lib/Sema/CodeSynthesisDistributedActor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,6 @@ createDistributedActor_init_local(ClassDecl *classDecl,
/// \param initDecl The function decl whose body to synthesize.
static std::pair<BraceStmt *, bool>
createDistributedActor_init_resolve_body(AbstractFunctionDecl *initDecl, void *) {

auto *funcDC = cast<DeclContext>(initDecl);
auto &C = funcDC->getASTContext();

Expand Down Expand Up @@ -289,8 +288,8 @@ createDistributedActor_init_resolve_body(AbstractFunctionDecl *initDecl, void *)
statements.push_back(assignAddressExpr);
// end-of-FIXME: this must be checking with the transport instead

auto *body = BraceStmt::create(C, SourceLoc(), statements, SourceLoc(),
/*implicit=*/true);
BraceStmt *body = BraceStmt::create(C, SourceLoc(), statements, SourceLoc(),
/*implicit=*/true);

return { body, /*isTypeChecked=*/false };
}
Expand All @@ -306,7 +305,6 @@ static ConstructorDecl *
createDistributedActor_init_resolve(ClassDecl *classDecl,
ASTContext &ctx) {
auto &C = ctx;

auto conformanceDC = classDecl;

// Expected type: (Self) -> (ActorAddress, ActorTransport) -> (Self)
Expand Down Expand Up @@ -465,6 +463,81 @@ static void addImplicitDistributedActorConstructors(ClassDecl *decl) {
}
}

/******************************************************************************/
/******************************** DEINIT **************************************/
/******************************************************************************/

/// A distributed actor's deinit MUST call `transport.resignAddress` before it
/// is deallocated.
static void addImplicitResignAddress(ClassDecl *decl) {
auto &C = decl->getASTContext();

DestructorDecl *existingDeinit = decl->getDestructor();
assert(existingDeinit);

// DestructorDecl *deinitDecl = existingDeinit != nullptr ? existingDeinit :
// new (C) DestructorDecl(SourceLoc(), decl);
DestructorDecl *deinitDecl = new (C) DestructorDecl(SourceLoc(), decl);

BraceStmt *body = deinitDecl->getBody();

// == Copy all existing statements to the new deinit
SmallVector<ASTNode, 2> statements; // TODO: how to init at body statements count size?
for (auto stmt : body->getElements())
statements.push_back(stmt); // TODO: copy?

// TODO: INJECT THIS AS FIRST THING IN A DEFER {}

// == Inject the lifecycle 'resignAddress' interaction
// ==== self
auto *selfRef = DerivedConformance::createSelfDeclRef(deinitDecl);

// ==== `self.actorTransport`
auto *varTransportExpr = UnresolvedDotExpr::createImplicit(C, selfRef,
C.Id_actorTransport);

// ==== `self.actorAddress`
auto *varAddressExpr = UnresolvedDotExpr::createImplicit(C, selfRef,
C.Id_actorAddress);
// auto addressRef = new (C) DeclRefExpr(varAddressExpr, DeclNameLoc(),
// /*implicit*/ true);
// auto addressRef = new (C) DeclRefExpr(ConcreteDeclRef(varAddressExpr), DeclNameLoc(),
// /*implicit=*/true);
// addressRef->setType(varAddressExpr->getType());

// ==== `self.transport.resignAddress(self.actorAddress)`
auto loc = deinitDecl->getLoc();
// auto resignAddressRef = new (C) DeclRefExpr(varTransportExpr, DeclNameLoc(),
// /*implicit=*/true);
// resignAddressRef->setThrows(false);
auto resignFuncDecls = C.getActorTransportDecl()->lookupDirect(C.Id_resignAddress);
assert(resignFuncDecls.size() == 1);
AbstractFunctionDecl *resignFuncDecl = dyn_cast<AbstractFunctionDecl>(resignFuncDecls.front());
auto resignFuncRef = new (C) DeclRefExpr(resignFuncDecl, DeclNameLoc(), /*implicit=*/true);

auto *addressParam = new (C) ParamDecl(
SourceLoc(), SourceLoc(), Identifier(),
SourceLoc(), C.Id_address, decl);
addressParam->setInterfaceType(C.getActorAddressDecl()->getInterfaceType());
addressParam->setSpecifier(ParamSpecifier::Default);
addressParam->setImplicit();
auto *paramList = ParameterList::createWithoutLoc(addressParam);

auto *resignFuncRefRef = UnresolvedDotExpr::createImplicit(
C, varTransportExpr, C.Id_resignAddress, paramList);

Expr *resignAddressCall = CallExpr::createImplicit(C, resignFuncRefRef,
{ varAddressExpr },
{ Identifier() });
statements.push_back(resignAddressCall);

BraceStmt *newBody = BraceStmt::create(C, SourceLoc(), statements, SourceLoc(),
/*implicit=*/true);

newBody->dump();
deinitDecl->setBody(newBody, AbstractFunctionDecl::BodyKind::TypeChecked); // FIXME: no idea if Parsed is right, we are NOT type checked I guess?
}

/******************************************************************************/
/******************************** PROPERTIES **********************************/
/******************************************************************************/
Expand Down Expand Up @@ -577,7 +650,7 @@ synthesizeRemoteFuncStubBody(AbstractFunctionDecl *func, void *context) {
auto uintInit = ctx.getIntBuiltinInitDecl(uintDecl);

auto missingTransportDecl = ctx.getMissingDistributedActorTransport();
assert(missingTransportDecl);
assert(missingTransportDecl && "Could not locate '_missingDistributedActorTransport' function");

// Create a call to _Distributed._missingDistributedActorTransport
auto loc = func->getLoc();
Expand Down Expand Up @@ -733,4 +806,5 @@ void swift::addImplicitDistributedActorMembersToClass(ClassDecl *decl) {
addImplicitDistributedActorConstructors(decl);
addImplicitDistributedActorStoredProperties(decl);
addImplicitRemoteActorFunctions(decl);
// addImplicitResignAddress(decl);
}
31 changes: 31 additions & 0 deletions test.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@

struct ADDRESS {}

protocol TRANSPORT_PROTOCOL {
func RESIGN(address: ADDRESS)
}

struct TRANSPORT: TRANSPORT_PROTOCOL {
func RESIGN(address: ADDRESS) {
}
}

class ACTOR {
let address: ADDRESS
let transport: TRANSPORT_PROTOCOL

init() {
self.address = ADDRESS()
self.transport = TRANSPORT()
}

deinit {
if ISREMOTE() {
self.transport.RESIGN(address: self.address);
}
}
}

func ISREMOTE() -> Bool {
false
}
Loading