Skip to content

Commit d610493

Browse files
committed
fix bugs with actor inits and flow-isolation
This is a combination of fixes: - inject hops in self-isolated delegating actor initializers after self becomes initialized. - fix sendable and isolation for convenience inits - fix bug in distributed actor inits that I introduced when implementing flow-isolation. - fix / add test coverage.
1 parent 8432f94 commit d610493

File tree

8 files changed

+521
-839
lines changed

8 files changed

+521
-839
lines changed

lib/SILGen/SILGenConstructor.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,17 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF,
315315
return;
316316
}
317317

318+
// FIXME: the callers of ctorHopsInjectedByDefiniteInit is not correct (rdar://87485045)
319+
// we must still set the SGF.ExpectedExecutor field to say that we must
320+
// hop to the executor after every apply in the constructor. This seems to
321+
// happen for the main actor isolated async inits, but not for the plain ones,
322+
// where 'self' is not going to directly be the instance. We have to extend the
323+
// ExecutorBreadcrumb class to detect whether it needs to do a load or not
324+
// in it's emit method.
325+
//
326+
// So, the big problem right now is that for a delegating async actor init,
327+
// after calling an async function, no hop-back is being emitted.
328+
318329
/// Returns true if the given async constructor will have its
319330
/// required actor hops injected later by definite initialization.
320331
static bool ctorHopsInjectedByDefiniteInit(ConstructorDecl *ctor,

lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -790,7 +790,7 @@ static bool isDistributedActorCtor(SILFunction &F) {
790790
/// Injects a hop_to_executor instruction after the specified insertion point.
791791
static void injectHopToExecutorAfter(SILLocation loc,
792792
SILBasicBlock::iterator insertPt,
793-
SILValue actor, bool needsBorrow = true) {
793+
DIMemoryObjectInfo &TheMemory) {
794794

795795
LLVM_DEBUG(llvm::dbgs() << "hop-injector: requested insertion after "
796796
<< *insertPt);
@@ -805,20 +805,30 @@ static void injectHopToExecutorAfter(SILLocation loc,
805805
auto injectAfter = [&](SILInstruction *insertPt) -> void {
806806
LLVM_DEBUG(llvm::dbgs() << "hop-injector: injecting after " << *insertPt);
807807
SILBuilderWithScope::insertAfter(insertPt, [&](SILBuilder &b) {
808-
if (needsBorrow)
809-
actor = b.createBeginBorrow(loc, actor);
808+
809+
const bool delegating = !TheMemory.isNonDelegatingInit();
810+
SILValue val = TheMemory.getUninitializedValue();
811+
812+
// delegating inits always have an alloc we need to load it from.
813+
if (delegating)
814+
val = b.createLoad(loc.asAutoGenerated(), val,
815+
LoadOwnershipQualifier::Copy);
816+
817+
SILValue actor = b.createBeginBorrow(loc.asAutoGenerated(), val);
810818

811819
b.createHopToExecutor(loc.asAutoGenerated(), actor, /*mandatory=*/false);
812820

813821
// Distributed actors also need to notify their transport immediately
814822
// after performing the hop.
815-
if (isDistributedActorCtor(b.getFunction())) {
823+
if (!delegating && isDistributedActorCtor(b.getFunction())) {
816824
auto transport = findFirstDistributedActorSystemArg(b.getFunction());
817825
emitActorReadyCall(b, loc.asAutoGenerated(), actor, transport);
818826
}
819827

820-
if (needsBorrow)
821-
b.createEndBorrow(loc, actor);
828+
b.createEndBorrow(loc.asAutoGenerated(), actor);
829+
830+
if (delegating)
831+
b.createDestroyValue(loc.asAutoGenerated(), val);
822832
});
823833
};
824834

@@ -892,7 +902,7 @@ void LifetimeChecker::injectActorHopForBlock(
892902
// Make sure we found the initializing use of TheMemory.
893903
assert(bbi != bbe && "this block is not initializing?");
894904

895-
injectHopToExecutorAfter(loc, bbi, TheMemory.getUninitializedValue());
905+
injectHopToExecutorAfter(loc, bbi, TheMemory);
896906
}
897907

898908
static bool isFailableInitReturnUseOfEnum(EnumInst *EI);
@@ -929,8 +939,8 @@ void LifetimeChecker::injectActorHops() {
929939
// We insert this directly after the mark_uninitialized instruction, so
930940
// that it happens as early as `self` is available.`
931941
if (TheMemory.getNumElements() == 0) {
932-
auto *selfDef = TheMemory.getUninitializedValue();
933-
return injectHopToExecutorAfter(ctor, selfDef->getIterator(), selfDef);
942+
auto *selfDef = TheMemory.getUninitializedValue(); // FIXME: this might be wrong for convenience inits (rdar://87485045)
943+
return injectHopToExecutorAfter(ctor, selfDef->getIterator(), TheMemory);
934944
}
935945

936946
// Returns true iff a block returns normally from the initializer,

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,13 @@ bool swift::usesFlowSensitiveIsolation(AbstractFunctionDecl const *fn) {
136136
if (isa<DestructorDecl>(fn))
137137
return true;
138138

139-
return requiresFlowIsolation(ActorIsolation::forActorInstance(nominal),
140-
cast<ConstructorDecl>(fn));
139+
// construct an isolation corresponding to the type.
140+
auto actorTypeIso =
141+
nominal->isDistributedActor()
142+
? ActorIsolation::forDistributedActorInstance(nominal)
143+
: ActorIsolation::forActorInstance(nominal);
144+
145+
return requiresFlowIsolation(actorTypeIso, cast<ConstructorDecl>(fn));
141146
}
142147

143148
// Otherwise, the type must be isolated to a global actor.
@@ -514,13 +519,12 @@ ActorIsolationRestriction ActorIsolationRestriction::forDeclaration(
514519
}
515520

516521
case ActorIsolation::Independent:
517-
// While some synchronous, non-delegating actor inits are
518-
// nonisolated, they need cross-actor restrictions (e.g., for Sendable).
522+
// While some synchronous actor inits are not isolated they still need
523+
// cross-actor restrictions (e.g., for Sendable) for safety.
519524
if (auto *ctor = dyn_cast<ConstructorDecl>(decl))
520-
if (!ctor->isConvenienceInit())
521-
if (auto *parent = ctor->getParent()->getSelfClassDecl())
522-
if (parent->isAnyActor())
523-
return forActorSelf(parent, /*isCrossActor=*/true);
525+
if (auto *parent = ctor->getParent()->getSelfClassDecl())
526+
if (parent->isAnyActor())
527+
return forActorSelf(parent, /*isCrossActor=*/true);
524528

525529
// `nonisolated let` members are cross-actor as well.
526530
if (auto var = dyn_cast<VarDecl>(decl)) {
@@ -3570,12 +3574,11 @@ ActorIsolation ActorIsolationRequest::evaluate(
35703574
}
35713575
}
35723576

3573-
// Every actor's convenience or synchronous init is
3574-
// assumed to be actor-independent.
3577+
// When no other isolation applies, an actor's non-async init is independent
35753578
if (auto nominal = value->getDeclContext()->getSelfNominalTypeDecl())
35763579
if (nominal->isAnyActor())
35773580
if (auto ctor = dyn_cast<ConstructorDecl>(value))
3578-
if (ctor->isConvenienceInit() || !ctor->hasAsync())
3581+
if (!ctor->hasAsync())
35793582
defaultIsolation = ActorIsolation::forIndependent();
35803583

35813584
// Function used when returning an inferred isolation.

0 commit comments

Comments
 (0)