Skip to content

[Distributed] Implement distributed computed properties via special accessor #59700

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
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
febfef9
[Distributed] Skeleton implementation of distributed computed properties
ktoso Apr 12, 2022
5bdf94f
[Distributed] Remove commented out code and print statements
xedin Jun 15, 2022
927a58f
fix checkDistributedTargetResultType to also return when not diagnosing
ktoso Jun 16, 2022
c0479a5
[Distributed] NFC: remove a couple of unused variables from sythensis…
xedin Jun 17, 2022
d68961d
[Distributed] Synthesize thunks for distributed computed properties
xedin Jun 10, 2022
a016fae
[TypeChecker] Distributed: Verify properties before attempting to syn…
xedin Jun 16, 2022
fce3b85
[AST] Add new member access semantics - DistributedThunk
xedin Jun 10, 2022
e8987b4
[Distributed] Add a new access strategy - DispatchToDistributedThunk
xedin Jun 10, 2022
59ec5ab
[Distributed] SILGen: Remove `isDistributed` flags from accessor code
xedin Jun 15, 2022
b2b1efd
[Distributed] Decl: Add a new distributed-thunk bit
xedin Jun 27, 2022
84fa232
[Distributed] Synthesize 'distributed thunk' accessor for distributed…
xedin Jun 27, 2022
13dbd37
[MiscDiagnostics] Don't diagnose recursive property access in distrib…
xedin Jun 27, 2022
9d5f546
[Distributed] Teach `HasIsolatedSelfRequest` about distributed thunk …
xedin Jun 27, 2022
3006e92
[Distributed] NFC: XFAIL a couple of incorrect attr use tests
xedin Jun 27, 2022
3f3410d
[Distributed] Mangling: Add a comment about distributed accessor thunks
xedin Jun 29, 2022
8d9962e
[Distributed] Allow requesting distributed thunks on `AbstractStorage…
xedin Jun 29, 2022
d9a0130
[TypeChecker] Distributed: rework `checkDistributedAccess`
xedin Jun 29, 2022
2480c99
[Distributed] Generate thunk for accessor as a regular method
xedin Jun 30, 2022
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
2 changes: 1 addition & 1 deletion include/swift/AST/ASTMangler.h
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ class ASTMangler : public Mangler {
Type GlobalActorBound,
ModuleDecl *Module);

std::string mangleDistributedThunk(const FuncDecl *thunk);
std::string mangleDistributedThunk(const AbstractFunctionDecl *thunk);

/// Mangle a completion handler block implementation function, used for importing ObjC
/// APIs as async.
Expand Down
2 changes: 1 addition & 1 deletion include/swift/AST/Attr.def
Original file line number Diff line number Diff line change
Expand Up @@ -664,7 +664,7 @@ SIMPLE_DECL_ATTR(_inheritActorContext, InheritActorContext,
// 117 was 'spawn' and is now unused

CONTEXTUAL_SIMPLE_DECL_ATTR(distributed, DistributedActor,
DeclModifier | OnClass | OnFunc | OnVar |
DeclModifier | OnClass | OnFunc | OnAccessor | OnVar |
ABIBreakingToAdd | ABIBreakingToRemove |
APIBreakingToAdd | APIBreakingToRemove,
118)
Expand Down
29 changes: 24 additions & 5 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,7 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
SWIFT_INLINE_BITFIELD(SubscriptDecl, VarDecl, 2,
StaticSpelling : 2
);
SWIFT_INLINE_BITFIELD(AbstractFunctionDecl, ValueDecl, 3+2+8+1+1+1+1+1+1,
SWIFT_INLINE_BITFIELD(AbstractFunctionDecl, ValueDecl, 3+2+8+1+1+1+1+1+1+1,
/// \see AbstractFunctionDecl::BodyKind
BodyKind : 3,

Expand All @@ -439,7 +439,11 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {

/// Whether peeking into this function detected nested type declarations.
/// This is set when skipping over the decl at parsing.
HasNestedTypeDeclarations : 1
HasNestedTypeDeclarations : 1,

/// Whether this function is a distributed thunk for a distributed
/// function or computed property.
DistributedThunk: 1
);

SWIFT_INLINE_BITFIELD(FuncDecl, AbstractFunctionDecl, 1+1+2+1+1+2+1,
Expand Down Expand Up @@ -5119,6 +5123,13 @@ class AbstractStorageDecl : public ValueDecl {

bool hasAnyNativeDynamicAccessors() const;

/// Does this have a 'distributed' modifier?
bool isDistributed() const;

/// Return a distributed thunk if this computed property is marked as
/// 'distributed' and and nullptr otherwise.
FuncDecl *getDistributedThunk() const;

// Implement isa/cast/dyncast/etc.
static bool classof(const Decl *D) {
return D->getKind() >= DeclKind::First_AbstractStorageDecl &&
Expand Down Expand Up @@ -5352,9 +5363,6 @@ class VarDecl : public AbstractStorageDecl {
/// Is this an "async let" property?
bool isAsyncLet() const;

/// Does this have a 'distributed' modifier?
bool isDistributed() const;

/// Is this var known to be a "local" distributed actor,
/// if so the implicit throwing ans some isolation checks can be skipped.
bool isKnownToBeLocal() const;
Expand Down Expand Up @@ -6331,6 +6339,7 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
Bits.AbstractFunctionDecl.Throws = Throws;
Bits.AbstractFunctionDecl.HasSingleExpressionBody = false;
Bits.AbstractFunctionDecl.HasNestedTypeDeclarations = false;
Bits.AbstractFunctionDecl.DistributedThunk = false;
}

void setBodyKind(BodyKind K) {
Expand Down Expand Up @@ -6429,6 +6438,16 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
/// Returns 'true' if the function is distributed.
bool isDistributed() const;

/// Is this a thunk function used to access a distributed method
/// or computed property outside of its actor isolation context?
bool isDistributedThunk() const {
return Bits.AbstractFunctionDecl.DistributedThunk;
}

void setDistributedThunk(bool isThunk) {
Bits.AbstractFunctionDecl.DistributedThunk = isThunk;
}

/// For a 'distributed' target (func or computed property),
/// get the 'thunk' responsible for performing the 'remoteCall'.
///
Expand Down
3 changes: 3 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -4804,6 +4804,9 @@ ERROR(distributed_property_cannot_be_static,none,
ERROR(distributed_property_can_only_be_computed,none,
"%0 %1 cannot be 'distributed', only computed properties can",
(DescriptiveDeclKind, DeclName))
ERROR(distributed_property_accessor_only_get_can_be_distributed,none,
"only 'get' accessors may be 'distributed'",
())
NOTE(distributed_actor_isolated_property,none,
"access to %0 %1 is only permitted within distributed actor %2",
(DescriptiveDeclKind, DeclName, DeclName))
Expand Down
14 changes: 14 additions & 0 deletions include/swift/AST/Expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,12 @@ enum class AccessSemantics : uint8_t {
/// This is an ordinary access to a declaration, using whatever
/// polymorphism is expected.
Ordinary,

/// This is an access to the underlying storage through a distributed thunk.
///
/// The declaration must be a 'distributed' computed property used outside
/// of its actor isolation context.
DistributedThunk,
};

/// Expr - Base class for all expressions in swift.
Expand Down Expand Up @@ -1684,6 +1690,14 @@ class MemberRefExpr : public LookupExpr {
return (AccessSemantics) Bits.MemberRefExpr.Semantics;
}

/// Informs IRGen to that this member should be applied via its distributed
/// thunk, rather than invoking it directly.
///
/// Only intended to be set on distributed get-only computed properties.
void setAccessViaDistributedThunk() {
Bits.MemberRefExpr.Semantics = (unsigned)AccessSemantics::DistributedThunk;
}

SourceLoc getLoc() const { return NameLoc.getBaseNameLoc(); }
SourceLoc getStartLoc() const {
SourceLoc BaseStartLoc = getBase()->getStartLoc();
Expand Down
11 changes: 10 additions & 1 deletion include/swift/AST/StorageImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ class AccessStrategy {
/// separately performing a Read into a temporary variable followed by
/// a Write access back into the storage.
MaterializeToTemporary,

/// The access is to a computed distributed property, and thus the
/// get-accessor is a distributed thunk which may perform a remote call.
DispatchToDistributedThunk,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

};

private:
Expand Down Expand Up @@ -149,6 +153,10 @@ class AccessStrategy {
return { dispatched ? DispatchToAccessor : DirectToAccessor, accessor };
}

static AccessStrategy getDistributedThunkDispatchStrategy() {
return {DispatchToDistributedThunk, AccessorKind::Get};
}

static AccessStrategy getMaterializeToTemporary(AccessStrategy read,
AccessStrategy write) {
return { read, write };
Expand All @@ -157,7 +165,8 @@ class AccessStrategy {
Kind getKind() const { return TheKind; }

bool hasAccessor() const {
return TheKind == DirectToAccessor || TheKind == DispatchToAccessor;
return TheKind == DirectToAccessor || TheKind == DispatchToAccessor ||
TheKind == DispatchToDistributedThunk;
}

AccessorKind getAccessor() const {
Expand Down
15 changes: 10 additions & 5 deletions include/swift/AST/TypeCheckRequests.h
Original file line number Diff line number Diff line change
Expand Up @@ -1237,17 +1237,22 @@ class GetDistributedRemoteCallArgumentInitFunctionRequest :
///
/// The thunk is responsible for invoking 'remoteCall' when invoked on a remote
/// 'distributed actor'.
class GetDistributedThunkRequest :
public SimpleRequest<GetDistributedThunkRequest,
FuncDecl *(AbstractFunctionDecl *),
RequestFlags::Cached> {
class GetDistributedThunkRequest
: public SimpleRequest<GetDistributedThunkRequest,
FuncDecl *(
llvm::PointerUnion<AbstractStorageDecl *,
AbstractFunctionDecl *>),
RequestFlags::Cached> {
using Originator =
llvm::PointerUnion<AbstractStorageDecl *, AbstractFunctionDecl *>;

public:
using SimpleRequest::SimpleRequest;

private:
friend SimpleRequest;

FuncDecl *evaluate(Evaluator &evaluator, AbstractFunctionDecl *distributedFunc) const;
FuncDecl *evaluate(Evaluator &evaluator, Originator originator) const;

public:
// Caching
Expand Down
2 changes: 1 addition & 1 deletion include/swift/AST/TypeCheckerTypeIDZone.def
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ SWIFT_REQUEST(TypeChecker, GetDistributedTargetInvocationResultHandlerOnReturnFu
AbstractFunctionDecl *(NominalTypeDecl *),
Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, GetDistributedThunkRequest,
FuncDecl *(AbstractFunctionDecl *),
FuncDecl *(llvm::PointerUnion<AbstractStorageDecl *, AbstractFunctionDecl *>),
Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, GetDistributedActorIDPropertyRequest,
VarDecl *(NominalTypeDecl *),
Expand Down
6 changes: 6 additions & 0 deletions lib/AST/ASTDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ static StringRef getAccessSemanticsString(AccessSemantics value) {
case AccessSemantics::Ordinary: return "ordinary";
case AccessSemantics::DirectToStorage: return "direct_to_storage";
case AccessSemantics::DirectToImplementation: return "direct_to_impl";
case AccessSemantics::DistributedThunk: return "distributed_thunk";
}

llvm_unreachable("Unhandled AccessSemantics in switch.");
Expand Down Expand Up @@ -745,6 +746,8 @@ namespace {

void visitVarDecl(VarDecl *VD) {
printCommon(VD, "var_decl");
if (VD->isDistributed())
PrintWithColorRAII(OS, DeclModifierColor) << " distributed";
if (VD->isLet())
PrintWithColorRAII(OS, DeclModifierColor) << " let";
if (VD->getAttrs().hasAttribute<LazyAttr>())
Expand Down Expand Up @@ -875,6 +878,9 @@ namespace {
if (D->isDistributed()) {
PrintWithColorRAII(OS, ExprModifierColor) << " distributed";
}
if (D->isDistributedThunk()) {
PrintWithColorRAII(OS, ExprModifierColor) << " distributed-thunk";
}

if (auto fac = D->getForeignAsyncConvention()) {
OS << " foreign_async=";
Expand Down
25 changes: 24 additions & 1 deletion lib/AST/ASTMangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3486,10 +3486,33 @@ ASTMangler::mangleOpaqueTypeDescriptorRecord(const OpaqueTypeDecl *decl) {
return finalize();
}

std::string ASTMangler::mangleDistributedThunk(const FuncDecl *thunk) {
std::string ASTMangler::mangleDistributedThunk(const AbstractFunctionDecl *thunk) {
// Marker protocols cannot be checked at runtime, so there is no point
// in recording them for distributed thunks.
llvm::SaveAndRestore<bool> savedAllowMarkerProtocols(AllowMarkerProtocols,
false);

// Since computed property SILDeclRef's refer to the "originator"
// of the thunk, we need to mangle distributed thunks of accessors
// specially.
if (auto *accessor = dyn_cast<AccessorDecl>(thunk)) {
// TODO: This needs to use accessor type instead of
// distributed thunk after all SILDeclRefs are switched
// to use "originator" instead of the thunk itself.
//
// ```
// beginMangling();
// appendContextOf(thunk);
// appendDeclName(accessor->getStorage());
// appendDeclType(accessor, FunctionMangling);
// appendOperator("F");
// appendSymbolKind(SymbolKind::DistributedThunk);
// return finalize();
// ```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the TODO, that's on me after we fixed the SILDeclRefs

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, no worries!

auto *storage = accessor->getStorage();
thunk = storage->getDistributedThunk();
assert(thunk);
}

return mangleEntity(thunk, SymbolKind::DistributedThunk);
}
7 changes: 3 additions & 4 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2272,6 +2272,9 @@ AbstractStorageDecl::getAccessStrategy(AccessSemantics semantics,
assert(hasStorage());
return AccessStrategy::getStorage();

case AccessSemantics::DistributedThunk:
return AccessStrategy::getDistributedThunkDispatchStrategy();

case AccessSemantics::Ordinary:
// Skip these checks for local variables, both because they're unnecessary
// and because we won't necessarily have computed access.
Expand Down Expand Up @@ -6452,10 +6455,6 @@ bool VarDecl::isAsyncLet() const {
return getAttrs().hasAttribute<AsyncAttr>();
}

bool VarDecl::isDistributed() const {
return getAttrs().hasAttribute<DistributedActorAttr>();
}

bool VarDecl::isKnownToBeLocal() const {
return getAttrs().hasAttribute<KnownToBeLocalAttr>();
}
Expand Down
13 changes: 13 additions & 0 deletions lib/AST/DistributedDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1290,6 +1290,10 @@ bool AbstractFunctionDecl::isDistributed() const {
return getAttrs().hasAttribute<DistributedActorAttr>();
}

bool AbstractStorageDecl::isDistributed() const {
return getAttrs().hasAttribute<DistributedActorAttr>();
}

ConstructorDecl *
NominalTypeDecl::getDistributedRemoteCallTargetInitFunction() const {
auto mutableThis = const_cast<NominalTypeDecl *>(this);
Expand Down Expand Up @@ -1333,6 +1337,15 @@ AbstractFunctionDecl *ASTContext::getRemoteCallOnDistributedActorSystem(
/********************** Distributed Actor Properties **************************/
/******************************************************************************/

FuncDecl *AbstractStorageDecl::getDistributedThunk() const {
if (!isDistributed())
return nullptr;

auto mutableThis = const_cast<AbstractStorageDecl *>(this);
return evaluateOrDefault(getASTContext().evaluator,
GetDistributedThunkRequest{mutableThis}, nullptr);
}

FuncDecl*
AbstractFunctionDecl::getDistributedThunk() const {
if (!isDistributed())
Expand Down
1 change: 1 addition & 0 deletions lib/SILGen/SILGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1839,6 +1839,7 @@ SILGenModule::canStorageUseStoredKeyPathComponent(AbstractStorageDecl *decl,
case AccessStrategy::DirectToAccessor:
case AccessStrategy::DispatchToAccessor:
case AccessStrategy::MaterializeToTemporary:
case AccessStrategy::DispatchToDistributedThunk:
return false;
}
llvm_unreachable("unhandled strategy");
Expand Down
10 changes: 6 additions & 4 deletions lib/SILGen/SILGenApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5773,7 +5773,8 @@ RValue SILGenFunction::emitGetAccessor(SILLocation loc, SILDeclRef get,
ArgumentSource &&selfValue, bool isSuper,
bool isDirectUse,
PreparedArguments &&subscriptIndices,
SGFContext c, bool isOnSelfParameter) {
SGFContext c,
bool isOnSelfParameter) {
// Scope any further writeback just within this operation.
FormalEvaluationScope writebackScope(*this);

Expand Down Expand Up @@ -5847,8 +5848,8 @@ void SILGenFunction::emitSetAccessor(SILLocation loc, SILDeclRef set,
ManagedValue SILGenFunction::emitAddressorAccessor(
SILLocation loc, SILDeclRef addressor, SubstitutionMap substitutions,
ArgumentSource &&selfValue, bool isSuper, bool isDirectUse,
PreparedArguments &&subscriptIndices, SILType addressType,
bool isOnSelfParameter) {
PreparedArguments &&subscriptIndices,
SILType addressType, bool isOnSelfParameter) {
// Scope any further writeback just within this operation.
FormalEvaluationScope writebackScope(*this);

Expand Down Expand Up @@ -5909,7 +5910,8 @@ SILGenFunction::emitCoroutineAccessor(SILLocation loc, SILDeclRef accessor,
Callee callee =
emitSpecializedAccessorFunctionRef(*this, loc, accessor,
substitutions, selfValue,
isSuper, isDirectUse, isOnSelfParameter);
isSuper, isDirectUse,
isOnSelfParameter);

// We're already in a full formal-evaluation scope.
// Make a dead writeback scope; applyCoroutine won't try to pop this.
Expand Down
1 change: 1 addition & 0 deletions lib/SILGen/SILGenDistributed.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ void SILGenFunction::emitDistActorIdentityInit(ConstructorDecl *ctor,
initializeProperty(*this, loc, borrowedSelfArg, var, temp);
}

// TODO(distributed): rename to DistributedActorID
InitializeDistActorIdentity::InitializeDistActorIdentity(ConstructorDecl *ctor,
ManagedValue actorSelf)
: ctor(ctor),
Expand Down
8 changes: 8 additions & 0 deletions lib/SILGen/SILGenExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3508,6 +3508,14 @@ getIdForKeyPathComponentComputedProperty(SILGenModule &SGM,
// Identify the property by its vtable or wtable slot.
return SGM.getAccessorDeclRef(getRepresentativeAccessorForKeyPath(storage));
}

case AccessStrategy::DispatchToDistributedThunk: {
auto thunkRef = SILDeclRef(cast<VarDecl>(storage)->getDistributedThunk(),
SILDeclRef::Kind::Func,
/*isForeign=*/false,
/*isDistributed=*/true);
return SGM.getFunction(thunkRef, NotForDefinition);
}
}
llvm_unreachable("unhandled access strategy");
}
Expand Down
3 changes: 2 additions & 1 deletion lib/SILGen/SILGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -1477,7 +1477,8 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
ManagedValue emitAddressorAccessor(
SILLocation loc, SILDeclRef addressor, SubstitutionMap substitutions,
ArgumentSource &&optionalSelfValue, bool isSuper,
bool isDirectAccessorUse, PreparedArguments &&optionalSubscripts,
bool isDirectAccessorUse,
PreparedArguments &&optionalSubscripts,
SILType addressType, bool isOnSelfParameter);

CleanupHandle emitCoroutineAccessor(SILLocation loc, SILDeclRef accessor,
Expand Down
Loading