Skip to content

Commit 15e1cd2

Browse files
committed
[SE-0313] Implicit hop to an actor for an "isolated" parameter.
Fixes rdar://80907464.
1 parent 52211b3 commit 15e1cd2

File tree

7 files changed

+233
-98
lines changed

7 files changed

+233
-98
lines changed

include/swift/AST/Expr.h

Lines changed: 102 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1186,11 +1186,74 @@ class DiscardAssignmentExpr : public Expr {
11861186
}
11871187
};
11881188

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

11951258
public:
11961259
DeclRefExpr(ConcreteDeclRef D, DeclNameLoc Loc, bool Implicit,
@@ -1216,13 +1279,18 @@ class DeclRefExpr : public Expr {
12161279
}
12171280

12181281
/// Determine whether this reference needs to happen asynchronously, i.e.,
1219-
/// guarded by hop_to_executor
1220-
bool isImplicitlyAsync() const { return Bits.DeclRefExpr.IsImplicitlyAsync; }
1282+
/// guarded by hop_to_executor, and if so describe the target.
1283+
Optional<ImplicitActorHopTarget> isImplicitlyAsync() const {
1284+
if (!Bits.DeclRefExpr.IsImplicitlyAsync)
1285+
return None;
1286+
1287+
return implicitActorHopTarget;
1288+
}
12211289

1222-
/// Set whether this reference needs to happen asynchronously, i.e.,
1223-
/// guarded by hop_to_executor
1224-
void setImplicitlyAsync(bool isImplicitlyAsync) {
1225-
Bits.DeclRefExpr.IsImplicitlyAsync = isImplicitlyAsync;
1290+
/// Note that this reference is implicitly async and set the target.
1291+
void setImplicitlyAsync(ImplicitActorHopTarget target) {
1292+
Bits.DeclRefExpr.IsImplicitlyAsync = true;
1293+
implicitActorHopTarget = target;
12261294
}
12271295

12281296
/// Retrieve the concrete declaration reference.
@@ -1534,6 +1602,7 @@ class UnresolvedDeclRefExpr : public Expr {
15341602
class LookupExpr : public Expr {
15351603
Expr *Base;
15361604
ConcreteDeclRef Member;
1605+
ImplicitActorHopTarget implicitActorHopTarget;
15371606

15381607
protected:
15391608
explicit LookupExpr(ExprKind Kind, Expr *base, ConcreteDeclRef member,
@@ -1556,7 +1625,7 @@ class LookupExpr : public Expr {
15561625

15571626
/// Determine whether the operation has a known underlying declaration or not.
15581627
bool hasDecl() const { return static_cast<bool>(Member); }
1559-
1628+
15601629
/// Retrieve the declaration that this /// operation refers to.
15611630
/// Only valid when \c hasDecl() is true.
15621631
ConcreteDeclRef getDecl() const {
@@ -1571,13 +1640,18 @@ class LookupExpr : public Expr {
15711640
void setIsSuper(bool isSuper) { Bits.LookupExpr.IsSuper = isSuper; }
15721641

15731642
/// Determine whether this reference needs to happen asynchronously, i.e.,
1574-
/// guarded by hop_to_executor
1575-
bool isImplicitlyAsync() const { return Bits.LookupExpr.IsImplicitlyAsync; }
1643+
/// guarded by hop_to_executor, and if so describe the target.
1644+
Optional<ImplicitActorHopTarget> isImplicitlyAsync() const {
1645+
if (!Bits.LookupExpr.IsImplicitlyAsync)
1646+
return None;
1647+
1648+
return implicitActorHopTarget;
1649+
}
15761650

1577-
/// Set whether this reference needs to happen asynchronously, i.e.,
1578-
/// guarded by hop_to_executor
1579-
void setImplicitlyAsync(bool isImplicitlyAsync) {
1580-
Bits.LookupExpr.IsImplicitlyAsync = isImplicitlyAsync;
1651+
/// Note that this reference is implicitly async and set the target.
1652+
void setImplicitlyAsync(ImplicitActorHopTarget target) {
1653+
Bits.LookupExpr.IsImplicitlyAsync = true;
1654+
implicitActorHopTarget = target;
15811655
}
15821656

15831657
static bool classof(const Expr *E) {
@@ -4446,7 +4520,9 @@ class ApplyExpr : public Expr {
44464520

44474521
/// The argument being passed to it, and whether it's a 'super' argument.
44484522
llvm::PointerIntPair<Expr *, 1, bool> ArgAndIsSuper;
4449-
4523+
4524+
ImplicitActorHopTarget implicitActorHopTarget;
4525+
44504526
/// Returns true if \c e could be used as the call's argument. For most \c ApplyExpr
44514527
/// subclasses, this means it is a \c ParenExpr or \c TupleExpr.
44524528
bool validateArg(Expr *e) const;
@@ -4523,11 +4599,19 @@ class ApplyExpr : public Expr {
45234599
///
45244600
/// where the new closure is declared to be async.
45254601
///
4526-
bool implicitlyAsync() const {
4527-
return Bits.ApplyExpr.ImplicitlyAsync;
4602+
/// When the application is implciitly async, the result describes
4603+
/// the actor to which we need to need to hop.
4604+
Optional<ImplicitActorHopTarget> isImplicitlyAsync() const {
4605+
if (!Bits.ApplyExpr.ImplicitlyAsync)
4606+
return None;
4607+
4608+
return implicitActorHopTarget;
45284609
}
4529-
void setImplicitlyAsync(bool flag) {
4530-
Bits.ApplyExpr.ImplicitlyAsync = flag;
4610+
4611+
/// Note that this application is implicitly async and set the target.
4612+
void setImplicitlyAsync(ImplicitActorHopTarget target) {
4613+
Bits.ApplyExpr.ImplicitlyAsync = true;
4614+
implicitActorHopTarget = target;
45314615
}
45324616

45334617
ValueDecl *getCalledValue() const;

lib/SILGen/SILGenApply.cpp

Lines changed: 32 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -3651,15 +3651,15 @@ class CallEmission {
36513651

36523652
Callee callee;
36533653
FormalEvaluationScope initialWritebackScope;
3654-
Optional<ActorIsolation> implicitAsyncIsolation;
3654+
Optional<ImplicitActorHopTarget> implicitActorHopTarget;
36553655

36563656
public:
36573657
/// Create an emission for a call of the given callee.
36583658
CallEmission(SILGenFunction &SGF, Callee &&callee,
36593659
FormalEvaluationScope &&writebackScope)
36603660
: SGF(SGF), callee(std::move(callee)),
36613661
initialWritebackScope(std::move(writebackScope)),
3662-
implicitAsyncIsolation(None) {}
3662+
implicitActorHopTarget(None) {}
36633663

36643664
/// A factory method for decomposing the apply expr \p e into a call
36653665
/// emission.
@@ -3699,8 +3699,9 @@ class CallEmission {
36993699
/// Sets a flag that indicates whether this call be treated as being
37003700
/// implicitly async, i.e., it requires a hop_to_executor prior to
37013701
/// invoking the sync callee, etc.
3702-
void setImplicitlyAsync(Optional<ActorIsolation> implicitAsyncIsolation) {
3703-
this->implicitAsyncIsolation = implicitAsyncIsolation;
3702+
void setImplicitlyAsync(
3703+
Optional<ImplicitActorHopTarget> implicitActorHopTarget) {
3704+
this->implicitActorHopTarget = implicitActorHopTarget;
37043705
}
37053706

37063707
CleanupHandle applyCoroutine(SmallVectorImpl<ManagedValue> &yields);
@@ -3926,7 +3927,7 @@ RValue CallEmission::applyNormalCall(SGFContext C) {
39263927
return SGF.emitApply(
39273928
std::move(resultPlan), std::move(argScope), uncurriedLoc.getValue(), mv,
39283929
callee.getSubstitutions(), uncurriedArgs, calleeTypeInfo, options,
3929-
uncurriedContext, implicitAsyncIsolation);
3930+
uncurriedContext, implicitActorHopTarget);
39303931
}
39313932

39323933
static void emitPseudoFunctionArguments(SILGenFunction &SGF,
@@ -4241,28 +4242,9 @@ CallEmission CallEmission::forApplyExpr(SILGenFunction &SGF, ApplyExpr *e) {
42414242
apply.callSite->isNoThrows(),
42424243
apply.callSite->isNoAsync());
42434244

4244-
// For an implicitly-async call, determine the actor isolation.
4245-
if (apply.callSite->implicitlyAsync()) {
4246-
Optional<ActorIsolation> isolation;
4247-
4248-
// Check for global-actor isolation on the function type.
4249-
if (auto fnType = apply.callSite->getFn()->getType()
4250-
->castTo<FunctionType>()) {
4251-
if (Type globalActor = fnType->getGlobalActor()) {
4252-
isolation = ActorIsolation::forGlobalActor(globalActor, false);
4253-
}
4254-
}
4255-
4256-
// If there was no global-actor isolation on the function type, find
4257-
// the callee declaration and retrieve the isolation from it.
4258-
if (!isolation) {
4259-
if (auto decl = emission.callee.getDecl())
4260-
isolation = getActorIsolation(decl);
4261-
}
4262-
4263-
assert(isolation && "Implicitly asynchronous call without isolation");
4264-
emission.setImplicitlyAsync(isolation);
4265-
}
4245+
// For an implicitly-async call, record the target of the actor hop.
4246+
if (auto target = apply.callSite->isImplicitlyAsync())
4247+
emission.setImplicitlyAsync(target);
42664248
}
42674249

42684250
return emission;
@@ -4315,13 +4297,14 @@ bool SILGenModule::isNonMutatingSelfIndirect(SILDeclRef methodRef) {
43154297
/// lowered appropriately for the abstraction level but that the
43164298
/// result does need to be turned back into something matching a
43174299
/// formal type.
4318-
RValue SILGenFunction::emitApply(ResultPlanPtr &&resultPlan,
4319-
ArgumentScope &&argScope, SILLocation loc,
4320-
ManagedValue fn, SubstitutionMap subs,
4321-
ArrayRef<ManagedValue> args,
4322-
const CalleeTypeInfo &calleeTypeInfo,
4323-
ApplyOptions options, SGFContext evalContext,
4324-
Optional<ActorIsolation> implicitAsyncIsolation) {
4300+
RValue SILGenFunction::emitApply(
4301+
ResultPlanPtr &&resultPlan,
4302+
ArgumentScope &&argScope, SILLocation loc,
4303+
ManagedValue fn, SubstitutionMap subs,
4304+
ArrayRef<ManagedValue> args,
4305+
const CalleeTypeInfo &calleeTypeInfo,
4306+
ApplyOptions options, SGFContext evalContext,
4307+
Optional<ImplicitActorHopTarget> implicitActorHopTarget) {
43254308
auto substFnType = calleeTypeInfo.substFnType;
43264309
auto substResultType = calleeTypeInfo.substResultType;
43274310

@@ -4409,28 +4392,31 @@ RValue SILGenFunction::emitApply(ResultPlanPtr &&resultPlan,
44094392

44104393
ExecutorBreadcrumb breadcrumb;
44114394

4412-
// The presence of `implicitAsyncIsolation` indicates that the callee is a
4395+
// The presence of `implicitActorHopTarget` indicates that the callee is a
44134396
// synchronous function isolated to an actor other than our own.
44144397
// Such functions require the caller to hop to the callee's executor
44154398
// prior to invoking the callee.
4416-
if (implicitAsyncIsolation) {
4399+
if (implicitActorHopTarget) {
44174400
assert(F.isAsync() && "cannot hop_to_executor in a non-async func!");
44184401

4419-
switch (*implicitAsyncIsolation) {
4420-
case ActorIsolation::ActorInstance:
4421-
breadcrumb = emitHopToTargetActor(loc, *implicitAsyncIsolation,
4422-
args.back());
4402+
SILValue executor;
4403+
switch (*implicitActorHopTarget) {
4404+
case ImplicitActorHopTarget::InstanceSelf:
4405+
executor = emitLoadActorExecutor(loc, args.back());
44234406
break;
44244407

4425-
case ActorIsolation::GlobalActor:
4426-
case ActorIsolation::GlobalActorUnsafe:
4427-
breadcrumb = emitHopToTargetActor(loc, *implicitAsyncIsolation, None);
4408+
case ImplicitActorHopTarget::GlobalActor:
4409+
executor = emitLoadGlobalActorExecutor(
4410+
implicitActorHopTarget->getGlobalActor());
44284411
break;
44294412

4430-
case ActorIsolation::Independent:
4431-
case ActorIsolation::Unspecified:
4432-
llvm_unreachable("Not actor-isolated");
4413+
case ImplicitActorHopTarget::IsolatedParameter:
4414+
executor = emitLoadActorExecutor(
4415+
loc, args[implicitActorHopTarget->getIsolatedParameterIndex()]);
4416+
break;
44334417
}
4418+
4419+
breadcrumb = emitHopToTargetExecutor(loc, executor);
44344420
} else if (ExpectedExecutor && substFnType->isAsync()) {
44354421
// Otherwise, if we're in an actor method ourselves, and we're calling into
44364422
// any sort of async function, we'll want to make sure to hop back to our

lib/SILGen/SILGenFunction.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -839,6 +839,15 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
839839
Optional<ActorIsolation> actorIso,
840840
Optional<ManagedValue> actorSelf);
841841

842+
/// Emit a hop to the target executor, returning a breadcrumb with enough
843+
/// enough information to hop back.
844+
ExecutorBreadcrumb emitHopToTargetExecutor(SILLocation loc,
845+
SILValue executor);
846+
847+
/// Generate a hop directly to a dynamic actor instance. This can only be done
848+
/// inside an async actor-independent function. No hop-back is expected.
849+
void emitHopToActorValue(SILLocation loc, ManagedValue actor);
850+
842851
/// A version of `emitHopToTargetActor` that is specialized to the needs
843852
/// of various types of ConstructorDecls, like class or value initializers,
844853
/// because their prolog emission is not the same as for regular functions.
@@ -1589,7 +1598,7 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
15891598
ArrayRef<ManagedValue> args,
15901599
const CalleeTypeInfo &calleeTypeInfo, ApplyOptions options,
15911600
SGFContext evalContext,
1592-
Optional<ActorIsolation> implicitAsyncIsolation);
1601+
Optional<ImplicitActorHopTarget> implicitActorHopTarget);
15931602

15941603
RValue emitApplyOfDefaultArgGenerator(SILLocation loc,
15951604
ConcreteDeclRef defaultArgsOwner,

lib/SILGen/SILGenProlog.cpp

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

628628
if (auto executor = emitExecutor(loc, *maybeIso, maybeSelf)) {
629-
// Record the previous executor to hop back to when we no longer need to
630-
// be isolated to the target actor.
631-
//
632-
// If we're calling from an actor method ourselves, then we'll want to hop
633-
// back to our own actor.
634-
auto breadcrumb = ExecutorBreadcrumb(emitGetCurrentExecutor(loc));
635-
B.createHopToExecutor(loc, executor.getValue());
636-
637-
return breadcrumb;
629+
return emitHopToTargetExecutor(loc, *executor);
638630
} else {
639631
return ExecutorBreadcrumb();
640632
}
641633
}
642634

635+
ExecutorBreadcrumb SILGenFunction::emitHopToTargetExecutor(
636+
SILLocation loc, SILValue executor) {
637+
// Record the previous executor to hop back to when we no longer need to
638+
// be isolated to the target actor.
639+
//
640+
// If we're calling from an actor method ourselves, then we'll want to hop
641+
// back to our own actor.
642+
auto breadcrumb = ExecutorBreadcrumb(emitGetCurrentExecutor(loc));
643+
B.createHopToExecutor(loc, executor);
644+
return breadcrumb;
645+
}
646+
643647
Optional<SILValue> SILGenFunction::emitExecutor(
644648
SILLocation loc, ActorIsolation isolation,
645649
Optional<ManagedValue> maybeSelf) {

0 commit comments

Comments
 (0)