Skip to content

Generalize ActorIsolation to capture arbitrary isolated parameters #60799

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
Aug 26, 2022
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
36 changes: 27 additions & 9 deletions include/swift/AST/ActorIsolation.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,17 @@ class ActorIsolation {
Type globalActor;
void *pointer;
};
uint8_t kind : 3;
uint8_t isolatedByPreconcurrency : 1;
unsigned kind : 3;
unsigned isolatedByPreconcurrency : 1;
unsigned parameterIndex : 28;

ActorIsolation(Kind kind, NominalTypeDecl *actor)
: actor(actor), kind(kind), isolatedByPreconcurrency(false) { }
ActorIsolation(Kind kind, NominalTypeDecl *actor, unsigned parameterIndex)
: actor(actor), kind(kind), isolatedByPreconcurrency(false),
parameterIndex(parameterIndex) { }

ActorIsolation(Kind kind, Type globalActor)
: globalActor(globalActor), kind(kind), isolatedByPreconcurrency(false) { }
: globalActor(globalActor), kind(kind), isolatedByPreconcurrency(false),
parameterIndex(0) { }

public:
static ActorIsolation forUnspecified() {
Expand All @@ -92,8 +95,13 @@ class ActorIsolation {
return ActorIsolation(Independent, nullptr);
}

static ActorIsolation forActorInstance(NominalTypeDecl *actor) {
return ActorIsolation(ActorInstance, actor);
static ActorIsolation forActorInstanceSelf(NominalTypeDecl *actor) {
return ActorIsolation(ActorInstance, actor, 0);
}

static ActorIsolation forActorInstanceParameter(NominalTypeDecl *actor,
unsigned parameterIndex) {
return ActorIsolation(ActorInstance, actor, parameterIndex + 1);
}

static ActorIsolation forGlobalActor(Type globalActor, bool unsafe) {
Expand All @@ -109,6 +117,14 @@ class ActorIsolation {

bool isIndependent() const { return kind == Independent; }

/// Retrieve the parameter to which actor-instance isolation applies.
///
/// Parameter 0 is `self`.
unsigned getActorInstanceParameter() const {
assert(getKind() == ActorInstance);
return parameterIndex;
}

bool isActorIsolated() const {
switch (getKind()) {
case ActorInstance:
Expand Down Expand Up @@ -169,7 +185,7 @@ class ActorIsolation {
return true;

case ActorInstance:
return lhs.actor == rhs.actor;
return lhs.actor == rhs.actor && lhs.parameterIndex == rhs.parameterIndex;

case GlobalActor:
case GlobalActorUnsafe:
Expand All @@ -183,7 +199,9 @@ class ActorIsolation {
}

friend llvm::hash_code hash_value(const ActorIsolation &state) {
return llvm::hash_combine(state.kind, state.pointer);
return llvm::hash_combine(
state.kind, state.pointer, state.isolatedByPreconcurrency,
state.parameterIndex);
}
};

Expand Down
89 changes: 15 additions & 74 deletions include/swift/AST/Expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -1163,80 +1163,19 @@ class DiscardAssignmentExpr : public Expr {
}
};

/// Describes the actor to which an implicit-async expression will hop.
struct ImplicitActorHopTarget {
enum Kind {
/// The "self" instance.
InstanceSelf,
/// A global actor with the given type.
GlobalActor,
/// An isolated parameter in a call.
IsolatedParameter,
};

private:
/// The lower two bits are the Kind, and the remaining bits are used for
/// the payload, which might by a TypeBase * (for a global actor) or a
/// integer value (for an isolated parameter).
uintptr_t bits;

constexpr ImplicitActorHopTarget(uintptr_t bits) : bits(bits) { }

public:
/// Default-initialized to instance "self".
constexpr ImplicitActorHopTarget() : bits(0) { }

static ImplicitActorHopTarget forInstanceSelf() {
return ImplicitActorHopTarget(InstanceSelf);
}

static ImplicitActorHopTarget forGlobalActor(Type globalActor) {
uintptr_t bits =
reinterpret_cast<uintptr_t>(globalActor.getPointer()) | GlobalActor;
return ImplicitActorHopTarget(bits);
}

static ImplicitActorHopTarget forIsolatedParameter(unsigned index) {
uintptr_t bits = static_cast<uintptr_t>(index) << 2 | IsolatedParameter;
return ImplicitActorHopTarget(bits);
}

/// Determine the kind of implicit actor hop being performed.
Kind getKind() const {
return static_cast<Kind>(bits & 0x03);
}

operator Kind() const {
return getKind();
}

/// Retrieve the global actor type for an implicit hop to a global actor.
Type getGlobalActor() const {
assert(getKind() == GlobalActor);
return Type(reinterpret_cast<TypeBase *>(bits & ~0x03));
}

/// Retrieve the (zero-based) parameter index for the isolated parameter
/// in a call.
unsigned getIsolatedParameterIndex() const {
assert(getKind() == IsolatedParameter);
return bits >> 2;
}
};


/// DeclRefExpr - A reference to a value, "x".
class DeclRefExpr : public Expr {
/// The declaration pointer.
ConcreteDeclRef D;
DeclNameLoc Loc;
ImplicitActorHopTarget implicitActorHopTarget;
ActorIsolation implicitActorHopTarget;

public:
DeclRefExpr(ConcreteDeclRef D, DeclNameLoc Loc, bool Implicit,
AccessSemantics semantics = AccessSemantics::Ordinary,
Type Ty = Type())
: Expr(ExprKind::DeclRef, Implicit, Ty), D(D), Loc(Loc) {
: Expr(ExprKind::DeclRef, Implicit, Ty), D(D), Loc(Loc),
implicitActorHopTarget(ActorIsolation::forUnspecified()) {
Bits.DeclRefExpr.Semantics = (unsigned) semantics;
Bits.DeclRefExpr.FunctionRefKind =
static_cast<unsigned>(Loc.isCompound() ? FunctionRefKind::Compound
Expand All @@ -1258,15 +1197,15 @@ class DeclRefExpr : public Expr {

/// Determine whether this reference needs to happen asynchronously, i.e.,
/// guarded by hop_to_executor, and if so describe the target.
Optional<ImplicitActorHopTarget> isImplicitlyAsync() const {
Optional<ActorIsolation> isImplicitlyAsync() const {
if (!Bits.DeclRefExpr.IsImplicitlyAsync)
return None;

return implicitActorHopTarget;
}

/// Note that this reference is implicitly async and set the target.
void setImplicitlyAsync(ImplicitActorHopTarget target) {
void setImplicitlyAsync(ActorIsolation target) {
Bits.DeclRefExpr.IsImplicitlyAsync = true;
implicitActorHopTarget = target;
}
Expand Down Expand Up @@ -1600,12 +1539,13 @@ class UnresolvedDeclRefExpr : public Expr {
class LookupExpr : public Expr {
Expr *Base;
ConcreteDeclRef Member;
ImplicitActorHopTarget implicitActorHopTarget;
ActorIsolation implicitActorHopTarget;

protected:
explicit LookupExpr(ExprKind Kind, Expr *base, ConcreteDeclRef member,
bool Implicit)
: Expr(Kind, Implicit), Base(base), Member(member) {
: Expr(Kind, Implicit), Base(base), Member(member),
implicitActorHopTarget(ActorIsolation::forUnspecified()) {
Bits.LookupExpr.IsSuper = false;
Bits.LookupExpr.IsImplicitlyAsync = false;
Bits.LookupExpr.IsImplicitlyThrows = false;
Expand Down Expand Up @@ -1640,15 +1580,15 @@ class LookupExpr : public Expr {

/// Determine whether this reference needs to happen asynchronously, i.e.,
/// guarded by hop_to_executor, and if so describe the target.
Optional<ImplicitActorHopTarget> isImplicitlyAsync() const {
Optional<ActorIsolation> isImplicitlyAsync() const {
if (!Bits.LookupExpr.IsImplicitlyAsync)
return None;

return implicitActorHopTarget;
}

/// Note that this reference is implicitly async and set the target.
void setImplicitlyAsync(ImplicitActorHopTarget target) {
void setImplicitlyAsync(ActorIsolation target) {
Bits.LookupExpr.IsImplicitlyAsync = true;
implicitActorHopTarget = target;
}
Expand Down Expand Up @@ -4482,12 +4422,13 @@ class ApplyExpr : public Expr {
/// The list of arguments to call the function with.
ArgumentList *ArgList;

ImplicitActorHopTarget implicitActorHopTarget;
ActorIsolation implicitActorHopTarget;

protected:
ApplyExpr(ExprKind kind, Expr *fn, ArgumentList *argList, bool implicit,
Type ty = Type())
: Expr(kind, implicit, ty), Fn(fn), ArgList(argList) {
: Expr(kind, implicit, ty), Fn(fn), ArgList(argList),
implicitActorHopTarget(ActorIsolation::forUnspecified()) {
assert(ArgList);
assert(classof((Expr*)this) && "ApplyExpr::classof out of date");
Bits.ApplyExpr.ThrowsIsSet = false;
Expand Down Expand Up @@ -4553,15 +4494,15 @@ class ApplyExpr : public Expr {
///
/// When the application is implicitly async, the result describes
/// the actor to which we need to need to hop.
Optional<ImplicitActorHopTarget> isImplicitlyAsync() const {
Optional<ActorIsolation> isImplicitlyAsync() const {
if (!Bits.ApplyExpr.ImplicitlyAsync)
return None;

return implicitActorHopTarget;
}

/// Note that this application is implicitly async and set the target.
void setImplicitlyAsync(ImplicitActorHopTarget target) {
void setImplicitlyAsync(ActorIsolation target) {
Bits.ApplyExpr.ImplicitlyAsync = true;
implicitActorHopTarget = target;
}
Expand Down
3 changes: 2 additions & 1 deletion lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9232,7 +9232,8 @@ ActorIsolation swift::getActorIsolationOfContext(DeclContext *dc) {
auto actor = selfDecl->getType()->getReferenceStorageReferent()
->getAnyActor();
assert(actor && "Bad closure actor isolation?");
return ActorIsolation::forActorInstance(actor)
// FIXME: This could be a parameter... or a capture... hmmm.
return ActorIsolation::forActorInstanceSelf(actor)
.withPreconcurrency(isolation.preconcurrency());
}
}
Expand Down
26 changes: 16 additions & 10 deletions lib/SILGen/SILGenApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3886,7 +3886,7 @@ class CallEmission {

Callee callee;
FormalEvaluationScope initialWritebackScope;
Optional<ImplicitActorHopTarget> implicitActorHopTarget;
Optional<ActorIsolation> implicitActorHopTarget;
bool implicitlyThrows;

public:
Expand Down Expand Up @@ -3937,7 +3937,7 @@ class CallEmission {
/// implicitly async, i.e., it requires a hop_to_executor prior to
/// invoking the sync callee, etc.
void setImplicitlyAsync(
Optional<ImplicitActorHopTarget> implicitActorHopTarget) {
Optional<ActorIsolation> implicitActorHopTarget) {
this->implicitActorHopTarget = implicitActorHopTarget;
}

Expand Down Expand Up @@ -4573,7 +4573,7 @@ RValue SILGenFunction::emitApply(
ArrayRef<ManagedValue> args,
const CalleeTypeInfo &calleeTypeInfo,
ApplyOptions options, SGFContext evalContext,
Optional<ImplicitActorHopTarget> implicitActorHopTarget) {
Optional<ActorIsolation> implicitActorHopTarget) {
auto substFnType = calleeTypeInfo.substFnType;
auto substResultType = calleeTypeInfo.substResultType;

Expand Down Expand Up @@ -4672,18 +4672,24 @@ RValue SILGenFunction::emitApply(

SILValue executor;
switch (*implicitActorHopTarget) {
case ImplicitActorHopTarget::InstanceSelf:
executor = emitLoadActorExecutor(loc, args.back());
case ActorIsolation::ActorInstance:
if (unsigned paramIndex =
implicitActorHopTarget->getActorInstanceParameter()) {
executor = emitLoadActorExecutor(loc, args[paramIndex-1]);
} else {
executor = emitLoadActorExecutor(loc, args.back());
}
break;

case ImplicitActorHopTarget::GlobalActor:
case ActorIsolation::GlobalActor:
case ActorIsolation::GlobalActorUnsafe:
executor = emitLoadGlobalActorExecutor(
implicitActorHopTarget->getGlobalActor());
break;

case ImplicitActorHopTarget::IsolatedParameter:
executor = emitLoadActorExecutor(
loc, args[implicitActorHopTarget->getIsolatedParameterIndex()]);
case ActorIsolation::Unspecified:
case ActorIsolation::Independent:
llvm_unreachable("Not isolated");
break;
}

Expand Down Expand Up @@ -5897,7 +5903,7 @@ RValue SILGenFunction::emitGetAccessor(
PreparedArguments &&subscriptIndices,
SGFContext c,
bool isOnSelfParameter,
Optional<ImplicitActorHopTarget> implicitActorHopTarget) {
Optional<ActorIsolation> implicitActorHopTarget) {
// Scope any further writeback just within this operation.
FormalEvaluationScope writebackScope(*this);

Expand Down
4 changes: 2 additions & 2 deletions lib/SILGen/SILGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -1451,7 +1451,7 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
bool isDirectAccessorUse,
PreparedArguments &&optionalSubscripts, SGFContext C,
bool isOnSelfParameter,
Optional<ImplicitActorHopTarget> implicitActorHopTarget = None);
Optional<ActorIsolation> implicitActorHopTarget = None);

void emitSetAccessor(SILLocation loc, SILDeclRef setter,
SubstitutionMap substitutions,
Expand Down Expand Up @@ -1684,7 +1684,7 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
ArrayRef<ManagedValue> args,
const CalleeTypeInfo &calleeTypeInfo, ApplyOptions options,
SGFContext evalContext,
Optional<ImplicitActorHopTarget> implicitActorHopTarget);
Optional<ActorIsolation> implicitActorHopTarget);

RValue emitApplyOfDefaultArgGenerator(SILLocation loc,
ConcreteDeclRef defaultArgsOwner,
Expand Down
12 changes: 1 addition & 11 deletions lib/SILGen/SILGenLValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1648,23 +1648,13 @@ namespace {
RValue rvalue;
FormalEvaluationScope scope(SGF);

// FIXME: This somewhat silly, because the original expression should
// already have one of these.
Optional<ImplicitActorHopTarget> implicitActorHopTarget;
if (ActorIso) {
implicitActorHopTarget = ActorIso->isGlobalActor()
? ImplicitActorHopTarget::forGlobalActor(
ActorIso->getGlobalActor())
: ImplicitActorHopTarget::forInstanceSelf();
}

auto args =
std::move(*this).prepareAccessorArgs(SGF, loc, base, getter);

rvalue = SGF.emitGetAccessor(
loc, getter, Substitutions, std::move(args.base), IsSuper,
IsDirectAccessorUse, std::move(args.Indices), c,
IsOnSelfParameter, implicitActorHopTarget);
IsOnSelfParameter, ActorIso);

return rvalue;
}
Expand Down
1 change: 1 addition & 0 deletions lib/SILGen/SILGenPoly.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4438,6 +4438,7 @@ void SILGenFunction::emitProtocolWitness(

// For an instance actor, get the actor 'self'.
if (*enterIsolation == ActorIsolation::ActorInstance) {
assert(enterIsolation->getActorInstanceParameter() == 0 && "Not self?");
auto actorSelfVal = origParams.back();

if (actorSelfVal.getType().isAddress()) {
Expand Down
Loading