Skip to content

Commit 71d9c16

Browse files
committed
[SE-0313] Implicit hop to an actor for an "isolated" parameter.
Fixes rdar://80907464.
1 parent 7379780 commit 71d9c16

File tree

7 files changed

+236
-106
lines changed

7 files changed

+236
-106
lines changed

include/swift/AST/Expr.h

Lines changed: 102 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1189,11 +1189,74 @@ class DiscardAssignmentExpr : public Expr {
11891189
}
11901190
};
11911191

1192+
/// Describes the actor to which an implicit-async expression will hop.
1193+
struct ImplicitActorHopTarget {
1194+
enum Kind {
1195+
/// The "self" instance.
1196+
InstanceSelf,
1197+
/// A global actor with the given type.
1198+
GlobalActor,
1199+
/// An isolated parameter in a call.
1200+
IsolatedParameter,
1201+
};
1202+
1203+
private:
1204+
/// The lower two bits are the Kind, and the remaining bits are used for
1205+
/// the payload, which might by a TypeBase * (for a global actor) or a
1206+
/// integer value (for an isolated parameter).
1207+
uintptr_t bits;
1208+
1209+
constexpr ImplicitActorHopTarget(uintptr_t bits) : bits(bits) { }
1210+
1211+
public:
1212+
/// Default-initialized to instance "self".
1213+
constexpr ImplicitActorHopTarget() : bits(0) { }
1214+
1215+
static ImplicitActorHopTarget forInstanceSelf() {
1216+
return ImplicitActorHopTarget(InstanceSelf);
1217+
}
1218+
1219+
static ImplicitActorHopTarget forGlobalActor(Type globalActor) {
1220+
uintptr_t bits =
1221+
reinterpret_cast<uintptr_t>(globalActor.getPointer()) | GlobalActor;
1222+
return ImplicitActorHopTarget(bits);
1223+
}
1224+
1225+
static ImplicitActorHopTarget forIsolatedParameter(unsigned index) {
1226+
uintptr_t bits = static_cast<uintptr_t>(index) << 2 | IsolatedParameter;
1227+
return ImplicitActorHopTarget(bits);
1228+
}
1229+
1230+
/// Determine the kind of implicit actor hop being performed.
1231+
Kind getKind() const {
1232+
return static_cast<Kind>(bits & 0x03);
1233+
}
1234+
1235+
operator Kind() const {
1236+
return getKind();
1237+
}
1238+
1239+
/// Retrieve the global actor type for an implicit hop to a global actor.
1240+
Type getGlobalActor() const {
1241+
assert(getKind() == GlobalActor);
1242+
return Type(reinterpret_cast<TypeBase *>(bits & ~0x03));
1243+
}
1244+
1245+
/// Retrieve the (zero-based) parameter index for the isolated parameter
1246+
/// in a call.
1247+
unsigned getIsolatedParameterIndex() const {
1248+
assert(getKind() == IsolatedParameter);
1249+
return bits >> 2;
1250+
}
1251+
};
1252+
1253+
11921254
/// DeclRefExpr - A reference to a value, "x".
11931255
class DeclRefExpr : public Expr {
11941256
/// The declaration pointer.
11951257
ConcreteDeclRef D;
11961258
DeclNameLoc Loc;
1259+
ImplicitActorHopTarget implicitActorHopTarget;
11971260

11981261
public:
11991262
DeclRefExpr(ConcreteDeclRef D, DeclNameLoc Loc, bool Implicit,
@@ -1220,13 +1283,18 @@ class DeclRefExpr : public Expr {
12201283
}
12211284

12221285
/// Determine whether this reference needs to happen asynchronously, i.e.,
1223-
/// guarded by hop_to_executor
1224-
bool isImplicitlyAsync() const { return Bits.DeclRefExpr.IsImplicitlyAsync; }
1286+
/// guarded by hop_to_executor, and if so describe the target.
1287+
Optional<ImplicitActorHopTarget> isImplicitlyAsync() const {
1288+
if (!Bits.DeclRefExpr.IsImplicitlyAsync)
1289+
return None;
1290+
1291+
return implicitActorHopTarget;
1292+
}
12251293

1226-
/// Set whether this reference needs to happen asynchronously, i.e.,
1227-
/// guarded by hop_to_executor
1228-
void setImplicitlyAsync(bool isImplicitlyAsync) {
1229-
Bits.DeclRefExpr.IsImplicitlyAsync = isImplicitlyAsync;
1294+
/// Note that this reference is implicitly async and set the target.
1295+
void setImplicitlyAsync(ImplicitActorHopTarget target) {
1296+
Bits.DeclRefExpr.IsImplicitlyAsync = true;
1297+
implicitActorHopTarget = target;
12301298
}
12311299

12321300
/// Determine whether this reference needs may implicitly throw.
@@ -1554,6 +1622,7 @@ class UnresolvedDeclRefExpr : public Expr {
15541622
class LookupExpr : public Expr {
15551623
Expr *Base;
15561624
ConcreteDeclRef Member;
1625+
ImplicitActorHopTarget implicitActorHopTarget;
15571626

15581627
protected:
15591628
explicit LookupExpr(ExprKind Kind, Expr *base, ConcreteDeclRef member,
@@ -1577,7 +1646,7 @@ class LookupExpr : public Expr {
15771646

15781647
/// Determine whether the operation has a known underlying declaration or not.
15791648
bool hasDecl() const { return static_cast<bool>(Member); }
1580-
1649+
15811650
/// Retrieve the declaration that this /// operation refers to.
15821651
/// Only valid when \c hasDecl() is true.
15831652
ConcreteDeclRef getDecl() const {
@@ -1592,13 +1661,18 @@ class LookupExpr : public Expr {
15921661
void setIsSuper(bool isSuper) { Bits.LookupExpr.IsSuper = isSuper; }
15931662

15941663
/// Determine whether this reference needs to happen asynchronously, i.e.,
1595-
/// guarded by hop_to_executor
1596-
bool isImplicitlyAsync() const { return Bits.LookupExpr.IsImplicitlyAsync; }
1664+
/// guarded by hop_to_executor, and if so describe the target.
1665+
Optional<ImplicitActorHopTarget> isImplicitlyAsync() const {
1666+
if (!Bits.LookupExpr.IsImplicitlyAsync)
1667+
return None;
1668+
1669+
return implicitActorHopTarget;
1670+
}
15971671

1598-
/// Set whether this reference needs to happen asynchronously, i.e.,
1599-
/// guarded by hop_to_executor
1600-
void setImplicitlyAsync(bool isImplicitlyAsync) {
1601-
Bits.LookupExpr.IsImplicitlyAsync = isImplicitlyAsync;
1672+
/// Note that this reference is implicitly async and set the target.
1673+
void setImplicitlyAsync(ImplicitActorHopTarget target) {
1674+
Bits.LookupExpr.IsImplicitlyAsync = true;
1675+
implicitActorHopTarget = target;
16021676
}
16031677

16041678
/// Determine whether this reference needs may implicitly throw.
@@ -4481,7 +4555,9 @@ class ApplyExpr : public Expr {
44814555

44824556
/// The argument being passed to it.
44834557
Expr *Arg;
4484-
4558+
4559+
ImplicitActorHopTarget implicitActorHopTarget;
4560+
44854561
/// Returns true if \c e could be used as the call's argument. For most \c ApplyExpr
44864562
/// subclasses, this means it is a \c ParenExpr or \c TupleExpr.
44874563
bool validateArg(Expr *e) const;
@@ -4554,11 +4630,19 @@ class ApplyExpr : public Expr {
45544630
///
45554631
/// where the new closure is declared to be async.
45564632
///
4557-
bool implicitlyAsync() const {
4558-
return Bits.ApplyExpr.ImplicitlyAsync;
4633+
/// When the application is implciitly async, the result describes
4634+
/// the actor to which we need to need to hop.
4635+
Optional<ImplicitActorHopTarget> isImplicitlyAsync() const {
4636+
if (!Bits.ApplyExpr.ImplicitlyAsync)
4637+
return None;
4638+
4639+
return implicitActorHopTarget;
45594640
}
4560-
void setImplicitlyAsync(bool flag) {
4561-
Bits.ApplyExpr.ImplicitlyAsync = flag;
4641+
4642+
/// Note that this application is implicitly async and set the target.
4643+
void setImplicitlyAsync(ImplicitActorHopTarget target) {
4644+
Bits.ApplyExpr.ImplicitlyAsync = true;
4645+
implicitActorHopTarget = target;
45624646
}
45634647

45644648
/// Is this application _implicitly_ required to be a throwing call?

lib/SILGen/SILGenApply.cpp

Lines changed: 32 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -3662,7 +3662,7 @@ class CallEmission {
36623662

36633663
Callee callee;
36643664
FormalEvaluationScope initialWritebackScope;
3665-
Optional<ActorIsolation> implicitAsyncIsolation;
3665+
Optional<ImplicitActorHopTarget> implicitActorHopTarget;
36663666
bool implicitlyThrows;
36673667

36683668
public:
@@ -3671,7 +3671,7 @@ class CallEmission {
36713671
FormalEvaluationScope &&writebackScope)
36723672
: SGF(SGF), callee(std::move(callee)),
36733673
initialWritebackScope(std::move(writebackScope)),
3674-
implicitAsyncIsolation(None),
3674+
implicitActorHopTarget(None),
36753675
implicitlyThrows(false) {}
36763676

36773677
/// A factory method for decomposing the apply expr \p e into a call
@@ -3712,8 +3712,9 @@ class CallEmission {
37123712
/// Sets a flag that indicates whether this call be treated as being
37133713
/// implicitly async, i.e., it requires a hop_to_executor prior to
37143714
/// invoking the sync callee, etc.
3715-
void setImplicitlyAsync(Optional<ActorIsolation> implicitAsyncIsolation) {
3716-
this->implicitAsyncIsolation = implicitAsyncIsolation;
3715+
void setImplicitlyAsync(
3716+
Optional<ImplicitActorHopTarget> implicitActorHopTarget) {
3717+
this->implicitActorHopTarget = implicitActorHopTarget;
37173718
}
37183719

37193720
/// Sets a flag that indicates whether this call be treated as being
@@ -3945,7 +3946,7 @@ RValue CallEmission::applyNormalCall(SGFContext C) {
39453946
return SGF.emitApply(
39463947
std::move(resultPlan), std::move(argScope), uncurriedLoc.getValue(), mv,
39473948
callee.getSubstitutions(), uncurriedArgs, calleeTypeInfo, options,
3948-
uncurriedContext, implicitAsyncIsolation);
3949+
uncurriedContext, implicitActorHopTarget);
39493950
}
39503951

39513952
static void emitPseudoFunctionArguments(SILGenFunction &SGF,
@@ -4260,28 +4261,9 @@ CallEmission CallEmission::forApplyExpr(SILGenFunction &SGF, ApplyExpr *e) {
42604261
apply.callSite->isNoThrows(),
42614262
apply.callSite->isNoAsync());
42624263

4263-
// For an implicitly-async call, determine the actor isolation.
4264-
if (apply.callSite->implicitlyAsync()) {
4265-
Optional<ActorIsolation> isolation;
4266-
4267-
// Check for global-actor isolation on the function type.
4268-
if (auto fnType = apply.callSite->getFn()->getType()
4269-
->castTo<FunctionType>()) {
4270-
if (Type globalActor = fnType->getGlobalActor()) {
4271-
isolation = ActorIsolation::forGlobalActor(globalActor, false);
4272-
}
4273-
}
4274-
4275-
// If there was no global-actor isolation on the function type, find
4276-
// the callee declaration and retrieve the isolation from it.
4277-
if (!isolation) {
4278-
if (auto decl = emission.callee.getDecl())
4279-
isolation = getActorIsolation(decl);
4280-
}
4281-
4282-
assert(isolation && "Implicitly asynchronous call without isolation");
4283-
emission.setImplicitlyAsync(isolation);
4284-
}
4264+
// For an implicitly-async call, record the target of the actor hop.
4265+
if (auto target = apply.callSite->isImplicitlyAsync())
4266+
emission.setImplicitlyAsync(target);
42854267
}
42864268

42874269
return emission;
@@ -4334,13 +4316,14 @@ bool SILGenModule::isNonMutatingSelfIndirect(SILDeclRef methodRef) {
43344316
/// lowered appropriately for the abstraction level but that the
43354317
/// result does need to be turned back into something matching a
43364318
/// formal type.
4337-
RValue SILGenFunction::emitApply(ResultPlanPtr &&resultPlan,
4338-
ArgumentScope &&argScope, SILLocation loc,
4339-
ManagedValue fn, SubstitutionMap subs,
4340-
ArrayRef<ManagedValue> args,
4341-
const CalleeTypeInfo &calleeTypeInfo,
4342-
ApplyOptions options, SGFContext evalContext,
4343-
Optional<ActorIsolation> implicitAsyncIsolation) {
4319+
RValue SILGenFunction::emitApply(
4320+
ResultPlanPtr &&resultPlan,
4321+
ArgumentScope &&argScope, SILLocation loc,
4322+
ManagedValue fn, SubstitutionMap subs,
4323+
ArrayRef<ManagedValue> args,
4324+
const CalleeTypeInfo &calleeTypeInfo,
4325+
ApplyOptions options, SGFContext evalContext,
4326+
Optional<ImplicitActorHopTarget> implicitActorHopTarget) {
43444327
auto substFnType = calleeTypeInfo.substFnType;
43454328
auto substResultType = calleeTypeInfo.substResultType;
43464329

@@ -4428,32 +4411,31 @@ RValue SILGenFunction::emitApply(ResultPlanPtr &&resultPlan,
44284411

44294412
ExecutorBreadcrumb breadcrumb;
44304413

4431-
// The presence of `implicitAsyncIsolation` indicates that the callee is a
4414+
// The presence of `implicitActorHopTarget` indicates that the callee is a
44324415
// synchronous function isolated to an actor other than our own.
44334416
// Such functions require the caller to hop to the callee's executor
44344417
// prior to invoking the callee.
4435-
if (implicitAsyncIsolation) {
4418+
if (implicitActorHopTarget) {
44364419
assert(F.isAsync() && "cannot hop_to_executor in a non-async func!");
44374420

4438-
switch (*implicitAsyncIsolation) {
4439-
case ActorIsolation::DistributedActorInstance: {
4440-
// TODO: if the actor is remote there is no need to hop
4441-
LLVM_FALLTHROUGH;
4442-
}
4443-
case ActorIsolation::ActorInstance:
4444-
breadcrumb = emitHopToTargetActor(loc, *implicitAsyncIsolation,
4445-
args.back());
4421+
SILValue executor;
4422+
switch (*implicitActorHopTarget) {
4423+
case ImplicitActorHopTarget::InstanceSelf:
4424+
executor = emitLoadActorExecutor(loc, args.back());
44464425
break;
44474426

4448-
case ActorIsolation::GlobalActor:
4449-
case ActorIsolation::GlobalActorUnsafe:
4450-
breadcrumb = emitHopToTargetActor(loc, *implicitAsyncIsolation, None);
4427+
case ImplicitActorHopTarget::GlobalActor:
4428+
executor = emitLoadGlobalActorExecutor(
4429+
implicitActorHopTarget->getGlobalActor());
44514430
break;
44524431

4453-
case ActorIsolation::Independent:
4454-
case ActorIsolation::Unspecified:
4455-
llvm_unreachable("Not actor-isolated");
4432+
case ImplicitActorHopTarget::IsolatedParameter:
4433+
executor = emitLoadActorExecutor(
4434+
loc, args[implicitActorHopTarget->getIsolatedParameterIndex()]);
4435+
break;
44564436
}
4437+
4438+
breadcrumb = emitHopToTargetExecutor(loc, executor);
44574439
} else if (ExpectedExecutor && substFnType->isAsync()) {
44584440
// Otherwise, if we're in an actor method ourselves, and we're calling into
44594441
// any sort of async function, we'll want to make sure to hop back to our

lib/SILGen/SILGenFunction.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -844,6 +844,11 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
844844
Optional<ActorIsolation> actorIso,
845845
Optional<ManagedValue> actorSelf);
846846

847+
/// Emit a hop to the target executor, returning a breadcrumb with enough
848+
/// enough information to hop back.
849+
ExecutorBreadcrumb emitHopToTargetExecutor(SILLocation loc,
850+
SILValue executor);
851+
847852
/// Generate a hop directly to a dynamic actor instance. This can only be done
848853
/// inside an async actor-independent function. No hop-back is expected.
849854
void emitHopToActorValue(SILLocation loc, ManagedValue actor);
@@ -1598,7 +1603,7 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
15981603
ArrayRef<ManagedValue> args,
15991604
const CalleeTypeInfo &calleeTypeInfo, ApplyOptions options,
16001605
SGFContext evalContext,
1601-
Optional<ActorIsolation> implicitAsyncIsolation);
1606+
Optional<ImplicitActorHopTarget> implicitActorHopTarget);
16021607

16031608
RValue emitApplyOfDefaultArgGenerator(SILLocation loc,
16041609
ConcreteDeclRef defaultArgsOwner,

lib/SILGen/SILGenProlog.cpp

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -632,20 +632,24 @@ ExecutorBreadcrumb SILGenFunction::emitHopToTargetActor(SILLocation loc,
632632
return ExecutorBreadcrumb();
633633

634634
if (auto executor = emitExecutor(loc, *maybeIso, maybeSelf)) {
635-
// Record the previous executor to hop back to when we no longer need to
636-
// be isolated to the target actor.
637-
//
638-
// If we're calling from an actor method ourselves, then we'll want to hop
639-
// back to our own actor.
640-
auto breadcrumb = ExecutorBreadcrumb(emitGetCurrentExecutor(loc));
641-
B.createHopToExecutor(loc, executor.getValue(), /*mandatory*/ false);
642-
643-
return breadcrumb;
635+
return emitHopToTargetExecutor(loc, *executor);
644636
} else {
645637
return ExecutorBreadcrumb();
646638
}
647639
}
648640

641+
ExecutorBreadcrumb SILGenFunction::emitHopToTargetExecutor(
642+
SILLocation loc, SILValue executor) {
643+
// Record the previous executor to hop back to when we no longer need to
644+
// be isolated to the target actor.
645+
//
646+
// If we're calling from an actor method ourselves, then we'll want to hop
647+
// back to our own actor.
648+
auto breadcrumb = ExecutorBreadcrumb(emitGetCurrentExecutor(loc));
649+
B.createHopToExecutor(loc, executor, /*mandatory*/ false);
650+
return breadcrumb;
651+
}
652+
649653
Optional<SILValue> SILGenFunction::emitExecutor(
650654
SILLocation loc, ActorIsolation isolation,
651655
Optional<ManagedValue> maybeSelf) {

0 commit comments

Comments
 (0)