Skip to content

[concurrency] allow 'get' access to properties from outside the actor #35965

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 7 commits into from
Mar 5, 2021
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
53 changes: 29 additions & 24 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -4190,8 +4190,12 @@ ERROR(async_call_without_await_in_autoclosure,none,
ERROR(async_call_without_await_in_async_let,none,
"call is 'async' in an 'async let' initializer that is not marked "
"with 'await'", ())
ERROR(async_prop_access_without_await,none,
"property access is 'async' but is not marked with 'await'", ())
ERROR(async_subscript_access_without_await,none,
"subscript access is 'async' but is not marked with 'await'", ())
WARNING(no_async_in_await,none,
"no calls to 'async' functions occur within 'await' expression", ())
"no 'async' operations occur within 'await' expression", ())
ERROR(async_call_in_illegal_context,none,
"'async' call cannot occur in "
"%select{<<ERROR>>|a default argument|a property wrapper initializer|a property initializer|a global variable initializer|an enum case raw value|a catch pattern|a catch guard expression|a defer body}0",
Expand All @@ -4201,7 +4205,7 @@ ERROR(await_in_illegal_context,none,
"%select{<<ERROR>>|a default argument|a property wrapper initializer|a property initializer|a global variable initializer|an enum case raw value|a catch pattern|a catch guard expression|a defer body}0",
(unsigned))
ERROR(async_in_nonasync_function,none,
"%select{'async'|'await'|'async let'}0 in "
"%select{'async'|'async' call|'await'|'async let'|'async' property access|'async' subscript access}0 in "
"%select{a function|an autoclosure}1 that does not support concurrency",
(unsigned, bool))
NOTE(note_add_async_to_function,none,
Expand Down Expand Up @@ -4279,13 +4283,13 @@ ERROR(actor_with_nonactor_superclass,none,
"actor class cannot inherit from non-actor class %0", (DeclName))

ERROR(actor_isolated_non_self_reference,none,
"actor-isolated %0 %1 can only be referenced "
"%select{inside the actor|on 'self'}2",
(DescriptiveDeclKind, DeclName, bool))
"actor-isolated %0 %1 can only be %select{referenced|mutated|used 'inout'}3 "
"%select{from inside the actor|on 'self'}2",
(DescriptiveDeclKind, DeclName, bool, unsigned))
ERROR(actor_isolated_self_independent_context,none,
"actor-isolated %0 %1 can not be referenced from an "
"actor-isolated %0 %1 can not be %select{referenced|mutated|used 'inout'}2 from an "
"'@actorIndependent' context",
(DescriptiveDeclKind, DeclName))
(DescriptiveDeclKind, DeclName, unsigned))
ERROR(actor_isolated_inout_state,none,
"actor-isolated %0 %1 cannot be passed 'inout' to"
"%select{| implicitly}2 'async' function call",
Expand All @@ -4298,34 +4302,34 @@ ERROR(actor_isolated_global_actor_context,none,
"actor %2",
(DescriptiveDeclKind, DeclName, Type))
ERROR(global_actor_from_instance_actor_context,none,
"%0 %1 isolated to global actor %2 can not be referenced from actor %3",
(DescriptiveDeclKind, DeclName, Type, DeclName))
"%0 %1 isolated to global actor %2 can not be %select{referenced|mutated|used 'inout'}4 from actor %3",
(DescriptiveDeclKind, DeclName, Type, DeclName, unsigned))
ERROR(global_actor_from_other_global_actor_context,none,
"%0 %1 isolated to global actor %2 can not be referenced from "
"different global actor %3",
(DescriptiveDeclKind, DeclName, Type, Type))
"%0 %1 isolated to global actor %2 can not be %select{referenced|mutated|used 'inout'}4"
" from different global actor %3",
(DescriptiveDeclKind, DeclName, Type, Type, unsigned))
ERROR(global_actor_from_nonactor_context,none,
"%0 %1 isolated to global actor %2 can not be referenced from "
"%select{this|an '@actorIndependent'}3 context",
(DescriptiveDeclKind, DeclName, Type, bool))
"%0 %1 isolated to global actor %2 can not be %select{referenced|mutated|used 'inout'}4"
" from %select{this|an '@actorIndependent'}3 context",
(DescriptiveDeclKind, DeclName, Type, bool, unsigned))
ERROR(actor_isolated_partial_apply,none,
"actor-isolated %0 %1 can not be partially applied",
(DescriptiveDeclKind, DeclName))
ERROR(concurrent_access_local,none,
"use of local %0 %1 in concurrently-executing code",
(DescriptiveDeclKind, DeclName))
ERROR(actor_isolated_from_concurrent_closure,none,
"actor-isolated %0 %1 cannot be referenced from a concurrent closure",
(DescriptiveDeclKind, DeclName))
"actor-isolated %0 %1 cannot be %select{referenced|mutated|used 'inout'}2 from a concurrent closure",
(DescriptiveDeclKind, DeclName, unsigned))
ERROR(actor_isolated_from_concurrent_function,none,
"actor-isolated %0 %1 cannot be referenced from a concurrent function",
(DescriptiveDeclKind, DeclName))
"actor-isolated %0 %1 cannot be %select{referenced|mutated|used 'inout'}2 from a concurrent function",
(DescriptiveDeclKind, DeclName, unsigned))
ERROR(actor_isolated_from_async_let,none,
"actor-isolated %0 %1 cannot be referenced from 'async let' initializer",
(DescriptiveDeclKind, DeclName))
"actor-isolated %0 %1 cannot be %select{referenced|mutated|used 'inout'}2 from 'async let' initializer",
(DescriptiveDeclKind, DeclName, unsigned))
ERROR(actor_isolated_from_escaping_closure,none,
"actor-isolated %0 %1 cannot be referenced from an '@escaping' closure",
(DescriptiveDeclKind, DeclName))
"actor-isolated %0 %1 cannot be %select{referenced|mutated|used 'inout'}2 from an '@escaping' closure",
(DescriptiveDeclKind, DeclName, unsigned))
ERROR(local_function_executed_concurrently,none,
"concurrently-executed %0 %1 must be marked as '@concurrent'",
(DescriptiveDeclKind, DeclName))
Expand All @@ -4338,7 +4342,8 @@ NOTE(actor_isolated_sync_func,none,
"implicitly asynchronous",
(DescriptiveDeclKind, DeclName))
NOTE(actor_mutable_state,none,
"mutable state is only available within the actor instance", ())
"mutation of this %0 is only permitted within the actor",
(DescriptiveDeclKind))
WARNING(shared_mutable_state_access,none,
"reference to %0 %1 is not concurrency-safe because it involves "
"shared mutable state", (DescriptiveDeclKind, DeclName))
Expand Down
35 changes: 30 additions & 5 deletions include/swift/AST/Expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,9 @@ class alignas(8) Expr {

SWIFT_INLINE_BITFIELD_EMPTY(LiteralExpr, Expr);
SWIFT_INLINE_BITFIELD_EMPTY(IdentityExpr, Expr);
SWIFT_INLINE_BITFIELD(LookupExpr, Expr, 1,
IsSuper : 1
SWIFT_INLINE_BITFIELD(LookupExpr, Expr, 1+1,
IsSuper : 1,
IsImplicitlyAsync : 1
);
SWIFT_INLINE_BITFIELD_EMPTY(DynamicLookupExpr, LookupExpr);

Expand All @@ -193,9 +194,10 @@ class alignas(8) Expr {
LiteralCapacity : 32
);

SWIFT_INLINE_BITFIELD(DeclRefExpr, Expr, 2+2,
SWIFT_INLINE_BITFIELD(DeclRefExpr, Expr, 2+2+1,
Semantics : 2, // an AccessSemantics
FunctionRefKind : 2
FunctionRefKind : 2,
IsImplicitlyAsync : 1
);

SWIFT_INLINE_BITFIELD(UnresolvedDeclRefExpr, Expr, 2+2,
Expand Down Expand Up @@ -1190,6 +1192,7 @@ class DeclRefExpr : public Expr {
Bits.DeclRefExpr.FunctionRefKind =
static_cast<unsigned>(Loc.isCompound() ? FunctionRefKind::Compound
: FunctionRefKind::Unapplied);
Bits.DeclRefExpr.IsImplicitlyAsync = false;
}

/// Retrieve the declaration to which this expression refers.
Expand All @@ -1203,6 +1206,16 @@ class DeclRefExpr : public Expr {
return (AccessSemantics) Bits.DeclRefExpr.Semantics;
}

/// Determine whether this reference needs to happen asynchronously, i.e.,
/// guarded by hop_to_executor
bool isImplicitlyAsync() const { return Bits.DeclRefExpr.IsImplicitlyAsync; }

/// Set whether this reference needs to happen asynchronously, i.e.,
/// guarded by hop_to_executor
void setImplicitlyAsync(bool isImplicitlyAsync) {
Bits.DeclRefExpr.IsImplicitlyAsync = isImplicitlyAsync;
}

/// Retrieve the concrete declaration reference.
ConcreteDeclRef getDeclRef() const {
return D;
Expand Down Expand Up @@ -1518,6 +1531,7 @@ class LookupExpr : public Expr {
bool Implicit)
: Expr(Kind, Implicit), Base(base), Member(member) {
Bits.LookupExpr.IsSuper = false;
Bits.LookupExpr.IsImplicitlyAsync = false;
assert(Base);
}

Expand Down Expand Up @@ -1547,6 +1561,16 @@ class LookupExpr : public Expr {
/// Set whether this reference refers to the superclass's property.
void setIsSuper(bool isSuper) { Bits.LookupExpr.IsSuper = isSuper; }

/// Determine whether this reference needs to happen asynchronously, i.e.,
/// guarded by hop_to_executor
bool isImplicitlyAsync() const { return Bits.LookupExpr.IsImplicitlyAsync; }

/// Set whether this reference needs to happen asynchronously, i.e.,
/// guarded by hop_to_executor
void setImplicitlyAsync(bool isImplicitlyAsync) {
Bits.LookupExpr.IsImplicitlyAsync = isImplicitlyAsync;
}

static bool classof(const Expr *E) {
return E->getKind() >= ExprKind::First_LookupExpr &&
E->getKind() <= ExprKind::Last_LookupExpr;
Expand Down Expand Up @@ -3118,7 +3142,7 @@ class DestructureTupleExpr final : public ImplicitConversionExpr,
class LoadExpr : public ImplicitConversionExpr {
public:
LoadExpr(Expr *subExpr, Type type)
: ImplicitConversionExpr(ExprKind::Load, subExpr, type) {}
: ImplicitConversionExpr(ExprKind::Load, subExpr, type) { }

static bool classof(const Expr *E) { return E->getKind() == ExprKind::Load; }
};
Expand Down Expand Up @@ -4398,6 +4422,7 @@ class ApplyExpr : public Expr {
}

/// Is this application _implicitly_ required to be an async call?
/// That is, does it need to be guarded by hop_to_executor.
/// Note that this is _not_ a check for whether the callee is async!
/// Only meaningful after complete type-checking.
///
Expand Down
25 changes: 20 additions & 5 deletions lib/SILGen/LValue.h
Original file line number Diff line number Diff line change
Expand Up @@ -209,10 +209,16 @@ class PhysicalPathComponent : public PathComponent {
virtual void _anchor() override;

protected:
PhysicalPathComponent(LValueTypeData typeData, KindTy Kind)
: PathComponent(typeData, Kind) {
Optional<ActorIsolation> ActorIso;
PhysicalPathComponent(LValueTypeData typeData, KindTy Kind,
Optional<ActorIsolation> actorIso = None)
: PathComponent(typeData, Kind), ActorIso(actorIso) {
assert(isPhysical() && "PhysicalPathComponent Kind isn't physical");
}

public:
// Obtains the actor-isolation required for any loads of this component.
Optional<ActorIsolation> getActorIsolation() const { return ActorIso; }
};

inline PhysicalPathComponent &PathComponent::asPhysical() {
Expand Down Expand Up @@ -421,12 +427,19 @@ class LValue {
Path.emplace_back(new T(std::forward<As>(args)...));
}

// NOTE: Optional<ActorIsolation> inside of LValues
// Some path components carry an ActorIsolation value, which is an indicator
// that the access to that component must be performed by switching to the
// given actor's isolation domain. If the indicator is not present, that
// only means that a switch does not need to be emitted during the access.

void addNonMemberVarComponent(SILGenFunction &SGF, SILLocation loc,
VarDecl *var, SubstitutionMap subs,
LValueOptions options,
SGFAccessKind accessKind,
AccessStrategy strategy,
CanType formalRValueType);
CanType formalRValueType,
Optional<ActorIsolation> actorIso = None);

/// Add a member component to the access path of this lvalue.
void addMemberComponent(SILGenFunction &SGF, SILLocation loc,
Expand All @@ -448,7 +461,8 @@ class LValue {
SGFAccessKind accessKind,
AccessStrategy accessStrategy,
CanType formalRValueType,
bool isOnSelf = false);
bool isOnSelf = false,
Optional<ActorIsolation> actorIso = None);

void addMemberSubscriptComponent(SILGenFunction &SGF, SILLocation loc,
SubscriptDecl *subscript,
Expand All @@ -460,7 +474,8 @@ class LValue {
CanType formalRValueType,
PreparedArguments &&indices,
Expr *indexExprForDiagnostics,
bool isOnSelfParameter = false);
bool isOnSelfParameter = false,
Optional<ActorIsolation> actorIso = None);

/// Add a subst-to-orig reabstraction component. That is, given
/// that this l-value trafficks in values following the substituted
Expand Down
36 changes: 9 additions & 27 deletions lib/SILGen/SILGenApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4285,30 +4285,6 @@ bool SILGenModule::isNonMutatingSelfIndirect(SILDeclRef methodRef) {
return self.isFormalIndirect();
}

Optional<SILValue> SILGenFunction::emitLoadActorExecutorForCallee(
ValueDecl *calleeVD,
ArrayRef<ManagedValue> args) {
if (auto *funcDecl = dyn_cast_or_null<AbstractFunctionDecl>(calleeVD)) {
auto actorIso = getActorIsolation(funcDecl);
switch (actorIso.getKind()) {
case ActorIsolation::Unspecified:
case ActorIsolation::Independent:
case ActorIsolation::IndependentUnsafe:
break;

case ActorIsolation::ActorInstance: {
assert(args.size() > 0 && "no self argument for actor-instance call?");
auto calleeSelf = args.back();
return calleeSelf.borrow(*this, F.getLocation()).getValue();
}

case ActorIsolation::GlobalActor:
case ActorIsolation::GlobalActorUnsafe:
return emitLoadGlobalActorExecutor(actorIso.getGlobalActor());
}
}
return None;
}

//===----------------------------------------------------------------------===//
// Top Level Entrypoints
Expand Down Expand Up @@ -4418,10 +4394,16 @@ RValue SILGenFunction::emitApply(ResultPlanPtr &&resultPlan,
assert(F.isAsync() && "cannot hop_to_executor in a non-async func!");

auto calleeVD = implicitlyAsyncApply.getValue();
auto maybeExecutor = emitLoadActorExecutorForCallee(calleeVD, args);
if (auto *funcDecl = dyn_cast_or_null<AbstractFunctionDecl>(calleeVD)) {
Optional<ManagedValue> actorSelf;

assert(maybeExecutor.hasValue());
B.createHopToExecutor(loc, maybeExecutor.getValue());
if (args.size() > 0)
actorSelf = args.back();

auto didHop = emitHopToTargetActor(loc, getActorIsolation(funcDecl),
actorSelf);
assert(didHop);
}
}

SILValue rawDirectResult;
Expand Down
3 changes: 2 additions & 1 deletion lib/SILGen/SILGenExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
#include "Callee.h"
#include "Condition.h"
#include "Conversion.h"
#include "ExitableFullExpr.h"
#include "Initialization.h"
#include "LValue.h"
#include "RValue.h"
Expand Down Expand Up @@ -2199,6 +2198,7 @@ RValue RValueEmitter::visitMemberRefExpr(MemberRefExpr *e,

RValue RValueEmitter::visitDynamicMemberRefExpr(DynamicMemberRefExpr *E,
SGFContext C) {
assert(!E->isImplicitlyAsync() && "actors do not have @objc members");
return SGF.emitDynamicMemberRefExpr(E, C);
}

Expand All @@ -2220,6 +2220,7 @@ RValue RValueEmitter::visitSubscriptExpr(SubscriptExpr *E, SGFContext C) {

RValue RValueEmitter::visitDynamicSubscriptExpr(
DynamicSubscriptExpr *E, SGFContext C) {
assert(!E->isImplicitlyAsync() && "actors do not have @objc members");
return SGF.emitDynamicSubscriptExpr(E, C);
}

Expand Down
11 changes: 6 additions & 5 deletions lib/SILGen/SILGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -857,12 +857,13 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
// Concurrency
//===--------------------------------------------------------------------===//

/// Generates code to obtain the callee function's executor, if the function
/// is actor-isolated.
/// Generates code to obtain the executor for the given actor isolation,
/// as-needed, and emits a \c hop_to_executor to that executor.
///
/// \returns a SILValue representing the executor, if an executor exists.
Optional<SILValue> emitLoadActorExecutorForCallee(ValueDecl *calleeVD,
ArrayRef<ManagedValue> args);
/// \returns a non-null pointer if a \c hop_to_executor was emitted.
HopToExecutorInst* emitHopToTargetActor(SILLocation loc,
Optional<ActorIsolation> actorIso,
Optional<ManagedValue> actorSelf);

/// Generates code to obtain the executor given the actor's decl.
/// \returns a SILValue representing the executor.
Expand Down
Loading