Skip to content

🍒[5.7][Distributed] Implement distributed computed properties via special accessor #59809

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 22 commits into from
Jun 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
a4813e6
[Distributed] make witness be the distributed thunk
ktoso Jun 24, 2022
49cd8eb
[Distributed] distributed func checks now work in protocols & dont crash
ktoso Jun 24, 2022
473d440
Use requirement isDistributed() check in SILGenPoly.cpp
ktoso Jun 29, 2022
efb889e
[Distributed] Skeleton implementation of distributed computed properties
ktoso Apr 12, 2022
839a7a8
[Distributed] Remove commented out code and print statements
xedin Jun 15, 2022
d3f66ab
fix checkDistributedTargetResultType to also return when not diagnosing
ktoso Jun 16, 2022
9b73d7c
[Distributed] NFC: remove a couple of unused variables from sythensis…
xedin Jun 17, 2022
03d47a5
[Distributed] Synthesize thunks for distributed computed properties
xedin Jun 10, 2022
c4f26d1
[TypeChecker] Distributed: Verify properties before attempting to syn…
xedin Jun 16, 2022
2c616c4
[AST] Add new member access semantics - DistributedThunk
xedin Jun 10, 2022
fde104d
[Distributed] Add a new access strategy - DispatchToDistributedThunk
xedin Jun 10, 2022
b2c6635
[Distributed] SILGen: Remove `isDistributed` flags from accessor code
xedin Jun 15, 2022
b17b5ee
[Distributed] Decl: Add a new distributed-thunk bit
xedin Jun 27, 2022
5f0e73d
[Distributed] Synthesize 'distributed thunk' accessor for distributed…
xedin Jun 27, 2022
27fde3e
[MiscDiagnostics] Don't diagnose recursive property access in distrib…
xedin Jun 27, 2022
b635756
[Distributed] Teach `HasIsolatedSelfRequest` about distributed thunk …
xedin Jun 27, 2022
75d8564
[Distributed] NFC: XFAIL a couple of incorrect attr use tests
xedin Jun 27, 2022
2ed19c6
[Distributed] Mangling: Add a comment about distributed accessor thunks
xedin Jun 29, 2022
879bf58
[Distributed] Allow requesting distributed thunks on `AbstractStorage…
xedin Jun 29, 2022
4963fda
[TypeChecker] Distributed: rework `checkDistributedAccess`
xedin Jun 29, 2022
172d9b2
[Distributed] Generate thunk for accessor as a regular method
xedin Jun 30, 2022
50e04f2
fixup cherry-pick of ModuleFormat
ktoso 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 @@ -661,7 +661,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 @@ -5109,6 +5113,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 @@ -5342,9 +5353,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 @@ -6314,6 +6322,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 @@ -6415,6 +6424,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
6 changes: 6 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -4817,6 +4817,9 @@ ERROR(distributed_actor_func_static,none,
ERROR(distributed_actor_func_not_in_distributed_actor,none,
"'distributed' method can only be declared within 'distributed actor'",
())
ERROR(distributed_method_requirement_must_be_async_throws,none, // FIXME(distributed): this is an implementation limitation we should lift
"'distributed' protocol requirement %0 must currently be declared explicitly 'async throws'",
(DeclName))
ERROR(distributed_actor_user_defined_special_property,none,
"property %0 cannot be defined explicitly, as it conflicts with "
"distributed actor synthesized stored property",
Expand All @@ -4830,6 +4833,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
5 changes: 5 additions & 0 deletions include/swift/AST/DistributedDecl.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ Type getDistributedActorSystemType(NominalTypeDecl *actor);
/// Determine the `ID` type for the given actor.
Type getDistributedActorIDType(NominalTypeDecl *actor);

/// Similar to `getDistributedSerializationRequirementType`, however, from the
/// perspective of a concrete function. This way we're able to get the
/// serialization requirement for specific members, also in protocols.
Type getConcreteReplacementForMemberSerializationRequirement(ValueDecl *member);

/// Get specific 'SerializationRequirement' as defined in 'nominal'
/// type, which must conform to the passed 'protocol' which is expected
/// to require the 'SerializationRequirement'.
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,
};

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 @@ -1279,17 +1279,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 @@ -137,7 +137,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
3 changes: 1 addition & 2 deletions include/swift/SIL/SILDeclRef.h
Original file line number Diff line number Diff line change
Expand Up @@ -414,8 +414,7 @@ struct SILDeclRef {
defaultArgIndex,
pointer.get<AutoDiffDerivativeFunctionIdentifier *>());
}
/// Returns the distributed entry point corresponding to the same
/// decl.
/// Returns the distributed entry point corresponding to the same decl.
SILDeclRef asDistributed(bool distributed = true) const {
return SILDeclRef(loc.getOpaqueValue(), kind,
/*foreign=*/false,
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 @@ -742,6 +743,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 @@ -872,6 +875,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 @@ -3484,10 +3484,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();
// ```
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 @@ -2259,6 +2259,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 @@ -6417,10 +6420,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
44 changes: 44 additions & 0 deletions lib/AST/DistributedDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,37 @@ Type swift::getConcreteReplacementForProtocolActorSystemType(ValueDecl *member)
llvm_unreachable("Unable to fetch ActorSystem type!");
}

Type swift::getConcreteReplacementForMemberSerializationRequirement(
ValueDecl *member) {
auto &C = member->getASTContext();
auto *DC = member->getDeclContext();
auto DA = C.getDistributedActorDecl();

// === When declared inside an actor, we can get the type directly
if (auto classDecl = DC->getSelfClassDecl()) {
return getDistributedSerializationRequirementType(classDecl, C.getDistributedActorDecl());
}

/// === Maybe the value is declared in a protocol?
if (auto protocol = DC->getSelfProtocolDecl()) {
GenericSignature signature;
if (auto *genericContext = member->getAsGenericContext()) {
signature = genericContext->getGenericSignature();
} else {
signature = DC->getGenericSignatureOfContext();
}

auto SerReqAssocType = DA->getAssociatedType(C.Id_SerializationRequirement)
->getDeclaredInterfaceType();

// Note that this may be null, e.g. if we're a distributed func inside
// a protocol that did not declare a specific actor system requirement.
return signature->getConcreteType(SerReqAssocType);
}

llvm_unreachable("Unable to fetch ActorSystem type!");
}

Type swift::getDistributedActorSystemType(NominalTypeDecl *actor) {
assert(!dyn_cast<ProtocolDecl>(actor) &&
"Use getConcreteReplacementForProtocolActorSystemType instead to get"
Expand Down Expand Up @@ -1330,6 +1361,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 @@ -1373,6 +1408,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 @@ -1832,6 +1832,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
Loading