Skip to content

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

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

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
c44a9fa
[Distributed] make witness be the distributed thunk
ktoso Jun 24, 2022
2c6b156
[Distributed] distributed func checks now work in protocols & dont crash
ktoso Jun 24, 2022
7e23e79
[Distributed] Skeleton implementation of distributed computed properties
ktoso Apr 12, 2022
3850e2b
[Distributed] Remove commented out code and print statements
xedin Jun 15, 2022
ea0bf88
fix checkDistributedTargetResultType to also return when not diagnosing
ktoso Jun 16, 2022
9b6b5ff
[Distributed] NFC: remove a couple of unused variables from sythensis…
xedin Jun 17, 2022
1abcddf
[Distributed] Synthesize thunks for distributed computed properties
xedin Jun 10, 2022
ef63ffb
[TypeChecker] Distributed: Verify properties before attempting to syn…
xedin Jun 16, 2022
ecd451f
[AST] Add new member access semantics - DistributedThunk
xedin Jun 10, 2022
753b68a
[Distributed] Add a new access strategy - DispatchToDistributedThunk
xedin Jun 10, 2022
9f5c1b1
[Distributed] SILGen: Remove `isDistributed` flags from accessor code
xedin Jun 15, 2022
3a23c0c
[Distributed] Sema: Add a new distributed-thunk attribute
xedin Jun 27, 2022
06ad613
[Distributed] Synthesize 'distributed thunk' accessor for distributed…
xedin Jun 27, 2022
7b53823
[MiscDiagnostics] Don't diagnose recursive property access in distrib…
xedin Jun 27, 2022
2ac6a6d
[Distributed] Teach `HasIsolatedSelfRequest` about distributed thunk …
xedin Jun 27, 2022
8f8067b
[Distributed] NFC: XFAIL a couple of incorrect attr use tests
xedin Jun 27, 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
8 changes: 7 additions & 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 Expand Up @@ -732,6 +732,12 @@ CONTEXTUAL_SIMPLE_DECL_ATTR(_local, KnownToBeLocal,
APIBreakingToAdd | APIBreakingToRemove,
130)

SIMPLE_DECL_ATTR(_distributed_thunk, DistributedThunk,
OnFunc | UserInaccessible |
ABIBreakingToAdd | ABIBreakingToRemove |
APIBreakingToAdd | APIBreakingToRemove,
131)

// If you're adding a new underscored attribute here, please document it in
// docs/ReferenceGuides/UnderscoredAttributes.md.

Expand Down
8 changes: 8 additions & 0 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -5345,6 +5345,10 @@ class VarDecl : public AbstractStorageDecl {
/// 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;

/// 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 @@ -6415,6 +6419,10 @@ 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;

/// For a 'distributed' target (func or computed property),
/// get the 'thunk' responsible for performing the 'remoteCall'.
///
Expand Down
9 changes: 9 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -4633,6 +4633,9 @@ ERROR(distributed_actor_isolated_method,none,
ERROR(distributed_local_cannot_be_used,none,
"'local' cannot be used in user-defined code currently",
())
ERROR(distributed_thunk_cannot_be_used,none,
"'distributed_thunk' cannot be used in user-defined code",
())
ERROR(distributed_actor_func_param_not_codable,none,
"parameter '%0' of type %1 in %2 does not conform to serialization requirement '%3'",
(StringRef, Type, DescriptiveDeclKind, StringRef))
Expand Down Expand Up @@ -4817,6 +4820,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 +4836,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
13 changes: 8 additions & 5 deletions include/swift/AST/TypeCheckRequests.h
Original file line number Diff line number Diff line change
Expand Up @@ -1279,17 +1279,20 @@ 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<VarDecl *, AbstractFunctionDecl *>),
RequestFlags::Cached> {
using Originator = llvm::PointerUnion<VarDecl *, 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<VarDecl *, 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
10 changes: 10 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 Expand Up @@ -1335,6 +1341,10 @@ void ValueDecl::dumpRef(raw_ostream &os) const {
os << " known-to-be-local";
}

if (getAttrs().hasAttribute<DistributedThunkAttr>()) {
os << " distributed-thunk";
}

// Print location.
auto &srcMgr = getASTContext().SourceMgr;
if (getLoc().isValid()) {
Expand Down
10 changes: 9 additions & 1 deletion lib/AST/ASTMangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3190,6 +3190,14 @@ void ASTMangler::appendEntity(const ValueDecl *decl) {
// Handle accessors specially, they are mangled as modifiers on the accessed
// declaration.
if (auto accessor = dyn_cast<AccessorDecl>(decl)) {
if (accessor->isDistributedThunk()) {
appendContextOf(decl);
appendDeclName(accessor->getStorage());
appendDeclType(accessor, FunctionMangling);
appendOperator("F");
return;
}

return appendAccessorEntity(
getCodeForAccessorKind(accessor->getAccessorKind()),
accessor->getStorage(), accessor->isStatic());
Expand Down Expand Up @@ -3484,7 +3492,7 @@ 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,
Expand Down
3 changes: 3 additions & 0 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
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 AbstractFunctionDecl::isDistributedThunk() const {
return getAttrs().hasAttribute<DistributedThunkAttr>();
}

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 *VarDecl::getDistributedThunk() const {
if (!isDistributed())
return nullptr;

auto mutableThis = const_cast<VarDecl *>(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
10 changes: 6 additions & 4 deletions lib/SILGen/SILGenApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5764,7 +5764,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 @@ -5838,8 +5839,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 @@ -5900,7 +5901,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 @@ -213,6 +213,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 @@ -3505,6 +3505,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
Loading