basic implementation of performing an init of the id after an assign of the actorSystem.

The resignID call within the initializer moved into DI, because an assignment to
the actorSystem, and thus initialization of the id, is no longer guaranteed to happen.
Thus, while we previously could model the resignation as a clean-up emitted on all
unwind paths in an initializer, now it's conditional, based on whether the id was
initialized. This is exactly what DI is designed to do, so we inject the resignation
call just before we call the identity's deinit.
This commit is contained in:
Kavon Farvardin
2022-03-15 18:20:00 -07:00
parent 13cbe0dd15
commit af683dc271
25 changed files with 290 additions and 117 deletions

View File

@@ -137,24 +137,24 @@ static SILArgument *findFirstDistributedActorSystemArg(SILFunction &F) {
auto *module = F.getModule().getSwiftModule();
auto &C = F.getASTContext();
auto *transportProto = C.getProtocol(KnownProtocolKind::DistributedActorSystem);
Type transportTy = transportProto->getDeclaredInterfaceType();
auto *DAS = C.getDistributedActorSystemDecl();
Type systemTy = DAS->getDeclaredInterfaceType();
for (auto arg : F.getArguments()) {
// TODO(distributed): also be able to locate a generic transport
// TODO(distributed): also be able to locate a generic system
Type argTy = arg->getType().getASTType();
auto argDecl = arg->getDecl();
auto conformsToTransport =
module->lookupConformance(argDecl->getInterfaceType(), transportProto);
auto conformsToSystem =
module->lookupConformance(argDecl->getInterfaceType(), DAS);
// Is it a protocol that conforms to DistributedActorSystem?
if (argTy->isEqual(transportTy) || conformsToTransport) {
if (argTy->isEqual(systemTy) || conformsToSystem) {
return arg;
}
// Is it some specific DistributedActorSystem?
auto result = module->lookupConformance(argTy, transportProto);
auto result = module->lookupConformance(argTy, DAS);
if (!result.isInvalid()) {
return arg;
}
@@ -172,7 +172,8 @@ static SILArgument *findFirstDistributedActorSystemArg(SILFunction &F) {
static void emitActorSystemInit(SILGenFunction &SGF,
ConstructorDecl *ctor,
SILLocation loc,
ManagedValue actorSelf) {
ManagedValue actorSelf,
SILValue systemValue) {
assert(ctor->isImplicit() && "unexpected explicit dist actor init");
assert(ctor->isDesignatedInit());
@@ -183,11 +184,10 @@ static void emitActorSystemInit(SILGenFunction &SGF,
// By construction, automatically generated distributed actor ctors have
// exactly one ActorSystem-conforming argument to the constructor,
// so we grab the first one from the params.
SILValue systemArg = findFirstDistributedActorSystemArg(SGF.F);
VarDecl *var = lookupProperty(classDecl, C.Id_actorSystem);
assert(var);
initializeProperty(SGF, loc, actorSelf.getValue(), var, systemArg);
initializeProperty(SGF, loc, actorSelf.getValue(), var, systemValue);
}
/// Emits the distributed actor's identity (`id`) initialization.
@@ -196,72 +196,98 @@ static void emitActorSystemInit(SILGenFunction &SGF,
/// \verbatim
/// self.id = system.assignID(Self.self)
/// \endverbatim
static void emitIDInit(SILGenFunction &SGF, ConstructorDecl *ctor,
SILLocation loc, ManagedValue borrowedSelfArg) {
assert(ctor->isImplicit() && "unexpected explicit dist actor init");
void SILGenFunction::emitDistActorIdentityInit(ConstructorDecl *ctor,
SILLocation loc,
SILValue borrowedSelfArg,
SILValue actorSystem) {
assert(ctor->isDesignatedInit());
auto &C = ctor->getASTContext();
auto &B = SGF.B;
auto &F = SGF.F;
auto *dc = ctor->getDeclContext();
auto classDecl = dc->getSelfClassDecl();
assert(classDecl->isDistributedActor());
// --- prepare `Self.self` metatype
auto *selfTyDecl = ctor->getParent()->getSelfNominalTypeDecl();
auto selfTy = F.mapTypeIntoContext(selfTyDecl->getDeclaredInterfaceType());
auto selfMetatype = SGF.getLoweredType(MetatypeType::get(selfTy));
auto selfMetatype = getLoweredType(MetatypeType::get(selfTy));
SILValue selfMetatypeValue = B.createMetatype(loc, selfMetatype);
// since we're doing this only for the implicitly defined ctors, grab from arg
SILValue actorSystem = findFirstDistributedActorSystemArg(SGF.F);
// --- create a temporary storage for the result of the call
// it will be deallocated automatically as we exit this scope
VarDecl *var = lookupProperty(classDecl, C.Id_id);
auto resultTy = SGF.getLoweredType(
F.mapTypeIntoContext(var->getInterfaceType()));
auto temp = SGF.emitTemporaryAllocation(loc, resultTy);
auto resultTy = getLoweredType(F.mapTypeIntoContext(var->getInterfaceType()));
auto temp = emitTemporaryAllocation(loc, resultTy);
// --- emit the call itself.
emitDistributedActorSystemWitnessCall(
B, loc, C.Id_assignID,
actorSystem, SGF.getLoweredType(selfTy),
actorSystem, getLoweredType(selfTy),
{ temp, selfMetatypeValue });
// --- initialize the property.
initializeProperty(SGF, loc, borrowedSelfArg.getValue(), var, temp);
initializeProperty(*this, loc, borrowedSelfArg, var, temp);
}
namespace {
/// Cleanup to resign the identity of a distributed actor if an abnormal exit happens.
class ResignIdentity : public Cleanup {
ClassDecl *actorDecl;
SILValue self;
public:
ResignIdentity(ClassDecl *actorDecl, SILValue self)
: actorDecl(actorDecl), self(self) {
assert(actorDecl->isDistributedActor());
}
InitializeDistActorIdentity::InitializeDistActorIdentity(ConstructorDecl *ctor,
ManagedValue actorSelf)
: ctor(ctor),
actorSelf(actorSelf) {
systemVar = ctor->getDeclContext()
->getSelfClassDecl()
->getDistributedActorSystemProperty();
assert(systemVar);
}
void emit(SILGenFunction &SGF, CleanupLocation l, ForUnwind_t forUnwind) override {
if (forUnwind == IsForUnwind) {
l.markAutoGenerated();
SGF.emitDistributedActorSystemResignIDCall(l, actorDecl,
ManagedValue::forUnmanaged(self));
}
void InitializeDistActorIdentity::emit(SILGenFunction &SGF, CleanupLocation loc,
ForUnwind_t forUnwind) {
// If we're unwinding, that must mean we're in the case where the
// evaluating the expression being assigned to the actorSystem has
// thrown an error. In that case, we cannot initialize the identity,
// since there is no actorSystem.
if (forUnwind == IsForUnwind)
return;
// Save the current clean-up depth
auto baseDepth = SGF.getCleanupsDepth();
{
loc.markAutoGenerated();
auto borrowedSelf = actorSelf.borrow(SGF, loc);
// load the actorSystem value
Type formalType = SGF.F.mapTypeIntoContext(systemVar->getInterfaceType());
SILType loweredType = SGF.getLoweredType(formalType).getAddressType();
auto ref =
SGF.B.createRefElementAddr(loc, borrowedSelf, systemVar, loweredType);
SGFContext ctx;
auto systemVal =
SGF.emitLoad(loc, ref.getValue(),
SGF.getTypeLowering(loweredType), ctx, IsNotTake);
// Important that we mark the location as auto-generated, since the id
// is a @_compilerInitialized field.
SGF.emitDistActorIdentityInit(ctor, loc,
borrowedSelf.getValue(), systemVal.getValue());
}
void dump(SILGenFunction &SGF) const override {
// Emit any active clean-ups we just pushed.
while (SGF.getTopCleanup() != baseDepth)
SGF.Cleanups.popAndEmitCleanup(SGF.getTopCleanup(), loc, forUnwind);
}
void InitializeDistActorIdentity::dump(SILGenFunction &) const {
#ifndef NDEBUG
llvm::errs() << "ResignIdentity "
<< "State:" << getState() << " "
<< "Self: " << self << "\n";
llvm::errs() << "InitializeDistActorIdentity\n"
<< "State: " << getState()
<< "\n";
#endif
}
};
} // end anonymous namespace
}
void SILGenFunction::emitDistributedActorImplicitPropertyInits(
ConstructorDecl *ctor, ManagedValue selfArg) {
@@ -273,18 +299,18 @@ void SILGenFunction::emitDistributedActorImplicitPropertyInits(
selfArg = selfArg.borrow(*this, loc);
// register a clean-up to resign the identity upon abnormal exit
// we do this regardless of initializer kind, since it's easy to do in SILGen.
auto *actorDecl = cast<ClassDecl>(ctor->getParent()->getAsDecl());
Cleanups.pushCleanup<ResignIdentity>(actorDecl, selfArg.getValue());
// Users must initialize the actorSystem property explicitly in their ctors
if (!ctor->isImplicit())
// implicit ctors initialize the system and identity from
// its ActorSystem parameter.
if (ctor->isImplicit()) {
SILValue actorSystem = findFirstDistributedActorSystemArg(F);
emitActorSystemInit(*this, ctor, loc, selfArg, actorSystem);
emitDistActorIdentityInit(ctor, loc, selfArg.getValue(), actorSystem);
return;
}
// implicit ctors initialize these from the ActorSystem parameter.
emitActorSystemInit(*this, ctor, loc, selfArg);
emitIDInit(*this, ctor, loc, selfArg);
// for explicit ctors, store (but do not push) a clean-up that will
// initialize the identity in whichever scope it's pushed to.
DistActorCtorContext = InitializeDistActorIdentity(ctor, selfArg);
}
void SILGenFunction::emitDistributedActorReady(
@@ -505,7 +531,7 @@ SILGenFunction::emitConditionalResignIdentityCall(SILLocation loc,
ManagedValue actorSelf,
SILBasicBlock *continueBB) {
assert(actorDecl->isDistributedActor() &&
"only distributed actors have transport lifecycle hooks in deinit");
"only distributed actors have actorSystem lifecycle hooks in deinit");
auto selfTy = actorDecl->getDeclaredInterfaceType();