Skip to content

[Distributed] distributed decls working across files #39087

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 4 commits into from
Aug 30, 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
4 changes: 4 additions & 0 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -5986,6 +5986,10 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
/// Returns 'true' if the function is distributed.
bool isDistributed() const;

/// Get (or synthesize) the associated remote function for this one.
/// For example, for `distributed func hi()` get `func _remote_hi()`.
AbstractFunctionDecl *getDistributedActorRemoteFuncDecl() const;

PolymorphicEffectKind getPolymorphicEffectKind(EffectKind kind) const;

// FIXME: Hack that provides names with keyword arguments for accessors.
Expand Down
16 changes: 8 additions & 8 deletions include/swift/AST/TypeCheckRequests.h
Original file line number Diff line number Diff line change
Expand Up @@ -933,18 +933,18 @@ class IsDistributedActorRequest :
bool isCached() const { return true; }
};

/// Determine whether the given func is distributed.
class IsDistributedFuncRequest :
public SimpleRequest<IsDistributedFuncRequest,
bool(FuncDecl *),
RequestFlags::Cached> {
/// Obtain the 'remote' counterpart of a 'distributed func'.
class GetDistributedRemoteFuncRequest :
public SimpleRequest<GetDistributedRemoteFuncRequest,
AbstractFunctionDecl *(AbstractFunctionDecl *),
RequestFlags::Cached> {
public:
using SimpleRequest::SimpleRequest;
using SimpleRequest::SimpleRequest;

private:
friend SimpleRequest;
friend SimpleRequest;

bool evaluate(Evaluator &evaluator, FuncDecl *func) const;
AbstractFunctionDecl *evaluate(Evaluator &evaluator, AbstractFunctionDecl *func) 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 @@ -100,7 +100,7 @@ SWIFT_REQUEST(TypeChecker, IsDefaultActorRequest,
Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, IsDistributedActorRequest, bool(NominalTypeDecl *),
Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, IsDistributedFuncRequest, bool(FuncDecl *),
SWIFT_REQUEST(TypeChecker, GetDistributedRemoteFuncRequest, AbstractFunctionDecl *(AbstractFunctionDecl *),
Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, GlobalActorInstanceRequest,
VarDecl *(NominalTypeDecl *),
Expand Down
19 changes: 12 additions & 7 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7217,14 +7217,19 @@ bool AbstractFunctionDecl::isSendable() const {
}

bool AbstractFunctionDecl::isDistributed() const {
auto func = dyn_cast<FuncDecl>(this);
if (!func)
return false;
return this->getAttrs().hasAttribute<DistributedActorAttr>();
}

auto mutableFunc = const_cast<FuncDecl *>(func);
return evaluateOrDefault(getASTContext().evaluator,
IsDistributedFuncRequest{mutableFunc},
false);
AbstractFunctionDecl*
AbstractFunctionDecl::getDistributedActorRemoteFuncDecl() const {
if (!this->isDistributed())
return nullptr;

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

BraceStmt *AbstractFunctionDecl::getBody(bool canSynthesize) const {
Expand Down
32 changes: 32 additions & 0 deletions lib/AST/NameLookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1672,6 +1672,35 @@ static void installPropertyWrapperMembersIfNeeded(NominalTypeDecl *target,
}
}

static void installDistributedRemoteFunctionsIfNeeded(NominalTypeDecl *target,
DeclNameRef member) {
auto &Context = target->getASTContext();
auto baseName = member.getBaseName();

if (member.isSimpleName() || baseName.isSpecial())
return;

// We only need to install _remote functions.
if (!baseName.getIdentifier().str().startswith("_remote_"))
return;

// _remote_-prefixed functions can be generated by distributed funcs.
auto prefixLen = strlen("_remote_");
auto originalFuncName =
Context.getIdentifier(
baseName.getIdentifier().str().substr(prefixLen));

for (auto member : target->lookupDirect(originalFuncName)) {
auto func = dyn_cast<FuncDecl>(member);
if (!func) continue;

auto sourceFile = func->getDeclContext()->getParentSourceFile();
if (sourceFile && sourceFile->Kind != SourceFileKind::Interface) {
(void)func->getDistributedActorRemoteFuncDecl();
}
}
}

bool DeclContext::lookupQualified(ArrayRef<NominalTypeDecl *> typeDecls,
DeclNameRef member,
NLOptions options,
Expand Down Expand Up @@ -1725,6 +1754,9 @@ QualifiedLookupRequest::evaluate(Evaluator &eval, const DeclContext *DC,
// Make sure we've resolved property wrappers, if we need them.
installPropertyWrapperMembersIfNeeded(current, member);

// Make sure we've resolved synthesized _remote funcs for distributed actors.
installDistributedRemoteFunctionsIfNeeded(current, member);

// Look for results within the current nominal type and its extensions.
bool currentIsProtocol = isa<ProtocolDecl>(current);
auto flags = OptionSet<NominalTypeDecl::LookupDirectFlags>();
Expand Down
3 changes: 3 additions & 0 deletions lib/Sema/CodeSynthesis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,9 @@ static ConstructorDecl *createImplicitConstructor(NominalTypeDecl *decl,
"Only 'distributed actor' type can gain implicit distributed actor init");

if (swift::ensureDistributedModuleLoaded(decl)) {
// copy access level of distributed actor init from the nominal decl
accessLevel = decl->getEffectiveAccess();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

this fixes the accessibility of the created init to be the same as the class for a dist actor.


auto transportDecl = ctx.getActorTransportDecl();

// Create the parameter.
Expand Down
65 changes: 25 additions & 40 deletions lib/Sema/CodeSynthesisDistributedActor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,16 +91,10 @@ synthesizeRemoteFuncStubBody(AbstractFunctionDecl *func, void *context) {
file->setBuiltinInitializer(staticStringInit);

auto startLineAndCol = SM.getPresumedLineAndColumnForLoc(distributedFunc->getStartLoc());
// auto *line = new (ctx) MagicIdentifierLiteralExpr(
// MagicIdentifierLiteralExpr::Line, loc, /*Implicit=*/true);
// auto *line = new (ctx) IntegerLiteralExpr(startLineAndCol.first, loc,
// /*implicit*/ true);
auto *line = IntegerLiteralExpr::createFromUnsigned(ctx, startLineAndCol.first);
line->setType(uintType);
line->setBuiltinInitializer(uintInit);

// auto *column = new (ctx) MagicIdentifierLiteralExpr(
// MagicIdentifierLiteralExpr::Column, loc, /*Implicit=*/true);
auto *column = IntegerLiteralExpr::createFromUnsigned(ctx, startLineAndCol.second);
column->setType(uintType);
column->setBuiltinInitializer(uintInit);
Expand All @@ -111,20 +105,24 @@ synthesizeRemoteFuncStubBody(AbstractFunctionDecl *func, void *context) {
call->setThrows(false);

SmallVector<ASTNode, 2> stmts;
stmts.push_back(call); // something() -> Never
// stmts.push_back(new (ctx) ReturnStmt(SourceLoc(), /*Result=*/nullptr)); // FIXME: this causes 'different types for return type: String vs. ()'
stmts.push_back(call);
auto body = BraceStmt::create(ctx, SourceLoc(), stmts, SourceLoc(),
/*implicit=*/true);
return { body, /*isTypeChecked=*/true };
}

static Identifier makeRemoteFuncIdentifier(FuncDecl* func) {
auto &C = func->getASTContext();
auto localFuncName = func->getBaseIdentifier().str().str();
static Identifier makeRemoteFuncIdentifier(FuncDecl* distributedFunc) {
auto &C = distributedFunc->getASTContext();
assert(distributedFunc->isDistributed());
auto localFuncName = distributedFunc->getBaseIdentifier().str().str();
auto remoteFuncIdent = C.getIdentifier("_remote_" + localFuncName);
return remoteFuncIdent;
}

/******************************************************************************/
/************************ SYNTHESIS ENTRY POINT *******************************/
/******************************************************************************/

/// Create a remote stub for the passed in \c func.
/// The remote stub function is not user accessible and mirrors the API of
/// the local function. It is always throwing, async, and user-inaccessible.
Expand All @@ -138,7 +136,21 @@ static Identifier makeRemoteFuncIdentifier(FuncDecl* func) {
///
/// and is intended to be replaced by a transport library by providing an
/// appropriate @_dynamicReplacement function.
static void addImplicitRemoteActorFunction(ClassDecl *decl, FuncDecl *func) {
AbstractFunctionDecl *TypeChecker::addImplicitDistributedActorRemoteFunction(
ClassDecl *decl, AbstractFunctionDecl *AFD) {
if (!decl->isDistributedActor())
return nullptr;

auto func = dyn_cast<FuncDecl>(AFD);
if (!func || !func->isDistributed())
return nullptr;

// ==== if the remote func already exists, return it
if (auto existing = decl->lookupDirectRemoteFunc(func))
return existing;

// ==== Synthesize and add 'remote' func to the actor decl

auto &C = decl->getASTContext();
auto parentDC = decl;

Expand Down Expand Up @@ -174,33 +186,6 @@ static void addImplicitRemoteActorFunction(ClassDecl *decl, FuncDecl *func) {
remoteFuncDecl->copyFormalAccessFrom(func, /*sourceIsParentContext=*/false);

decl->addMember(remoteFuncDecl);
}

/// Synthesize dynamic _remote stub functions for each encountered distributed function.
static void addImplicitRemoteActorFunctions(ClassDecl *decl) {
assert(decl->isDistributedActor());

for (auto member : decl->getMembers()) {
auto func = dyn_cast<FuncDecl>(member);
if (func && func->isDistributed()) {
addImplicitRemoteActorFunction(decl, func);
}
}
}

/******************************************************************************/
/************************ SYNTHESIS ENTRY POINT *******************************/
/******************************************************************************/

/// Entry point for adding all computed members to a distributed actor decl.
void swift::addImplicitDistributedActorMembersToClass(ClassDecl *decl) {
// Bail out if not a distributed actor definition.
if (!decl->isDistributedActor())
return;

// If the _Distributed module is missing we cannot synthesize anything.
if (!swift::ensureDistributedModuleLoaded(decl))
return;

addImplicitRemoteActorFunctions(decl);
return remoteFuncDecl;
}
4 changes: 4 additions & 0 deletions lib/Sema/TypeCheckDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2637,6 +2637,10 @@ static ArrayRef<Decl *> evaluateMembersRequest(
(void) var->getPropertyWrapperAuxiliaryVariables();
(void) var->getPropertyWrapperInitializerInfo();
}

if (auto *func = dyn_cast<FuncDecl>(member)) {
(void) func->getDistributedActorRemoteFuncDecl();
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

so basically we need to trigger synthesis/"get" in same spots as property wrappers most of the time -- one is here, and another is in lookups

Copy link
Contributor Author

Choose a reason for hiding this comment

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

(The lookups solve the across files case.)

}
}

Expand Down
31 changes: 3 additions & 28 deletions lib/Sema/TypeCheckDeclPrimary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1410,28 +1410,6 @@ static void maybeDiagnoseClassWithoutInitializers(ClassDecl *classDecl) {
diagnoseClassWithoutInitializers(classDecl);
}

void TypeChecker::checkResultType(Type resultType,
DeclContext *owner) {
// // Only distributed functions have special requirements on return types.
// if (!owner->isDistributed())
// return;
//
// auto conformanceDC = owner->getConformanceContext();
//
// // Result type of distributed functions must be Codable.
// auto target =
// conformanceDC->mapTypeIntoContext(it->second->getValueInterfaceType());
// if (TypeChecker::conformsToProtocol(target, derived.Protocol, conformanceDC)
// .isInvalid()) {
// TypeLoc typeLoc = {
// it->second->getTypeReprOrParentPatternTypeRepr(),
// it->second->getType(),
// };
// it->second->diagnose(diag::codable_non_conforming_property_here,
// derived.getProtocolType(), typeLoc);
// propertiesAreValid = false;
}

void TypeChecker::diagnoseDuplicateBoundVars(Pattern *pattern) {
SmallVector<VarDecl *, 2> boundVars;
pattern->collectVariables(boundVars);
Expand Down Expand Up @@ -2694,7 +2672,6 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
checkAccessControl(FD);

TypeChecker::checkParameterList(FD->getParameters(), FD);
TypeChecker::checkResultType(FD->getResultInterfaceType(), FD);
}

TypeChecker::checkDeclAttributes(FD);
Expand Down Expand Up @@ -2930,11 +2907,6 @@ class DeclChecker : public DeclVisitor<DeclChecker> {

TypeChecker::checkDeclAttributes(ED);

if (nominal->isDistributedActor()) {
auto decl = dyn_cast<ClassDecl>(nominal);
TypeChecker::checkDistributedActor(decl);
}

for (Decl *Member : ED->getMembers())
visit(Member);

Expand All @@ -2945,6 +2917,9 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
checkAccessControl(ED);

checkExplicitAvailability(ED);

if (nominal->isDistributedActor())
TypeChecker::checkDistributedActor(dyn_cast<ClassDecl>(nominal));
}

void visitTopLevelCodeDecl(TopLevelCodeDecl *TLCD) {
Expand Down
47 changes: 34 additions & 13 deletions lib/Sema/TypeCheckDistributed.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,34 @@ bool IsDistributedActorRequest::evaluate(
return classDecl->isExplicitDistributedActor();
}

bool IsDistributedFuncRequest::evaluate(
Evaluator &evaluator, FuncDecl *func) const {
// Check whether the attribute was explicitly specified.
if (auto attr = func->getAttrs().getAttribute<DistributedActorAttr>()) {
return true;
} else {
return false;
AbstractFunctionDecl *GetDistributedRemoteFuncRequest::evaluate(
Evaluator &evaluator, AbstractFunctionDecl *func) const {

if (!func->isDistributed())
return nullptr;

auto &C = func->getASTContext();
DeclContext *DC = func->getDeclContext();

// not via `ensureDistributedModuleLoaded` to avoid generating a warning,
// we won't be emitting the offending decl after all.
if (!C.getLoadedModule(C.Id_Distributed))
return nullptr;

// Locate the actor decl that the member must be synthesized to.
// TODO(distributed): should this just be added to the extension instead when we're in one?
ClassDecl *decl = dyn_cast<ClassDecl>(DC);
if (!decl) {
if (auto ED = dyn_cast<ExtensionDecl>(DC)) {
decl = dyn_cast<ClassDecl>(ED->getExtendedNominal());
}
}

/// A distributed func cannot be added to a non-distributed actor;
/// If the 'decl' was not a distributed actor we must have declared and
/// requested it from a illegal context, let's just ignore the synthesis.
assert(decl && "Can't find actor detect to add implicit _remote function to");
return TypeChecker::addImplicitDistributedActorRemoteFunction(decl, func);
}

// ==== ------------------------------------------------------------------------
Expand Down Expand Up @@ -228,17 +248,18 @@ void TypeChecker::checkDistributedActor(ClassDecl *decl) {
// If applicable, this will create the default 'init(transport:)' initializer
(void)decl->getDefaultInitializer();

// --- Check all constructors
for (auto member : decl->getMembers())
for (auto member : decl->getMembers()) {
// --- Check all constructors
if (auto ctor = dyn_cast<ConstructorDecl>(member))
checkDistributedActorConstructor(decl, ctor);

// --- synthesize _remote functions for distributed functions
if (auto func = dyn_cast<FuncDecl>(member))
(void)addImplicitDistributedActorRemoteFunction(decl, func);
}

// ==== Properties
// --- Check for any illegal re-definitions
checkDistributedActorProperties(decl);

// --- Synthesize properties
// TODO: those could technically move to DerivedConformance style
swift::addImplicitDistributedActorMembersToClass(decl);
}

4 changes: 0 additions & 4 deletions lib/Sema/TypeCheckDistributed.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,6 @@ void checkDistributedActorConstructor(const ClassDecl *decl, ConstructorDecl *ct

bool checkDistributedFunction(FuncDecl *decl, bool diagnose);

/// Synthesis of members which are not directly driven filling in protocol requirements,
/// such as the default local and resolve constructors, and `_remote_` function stubs.
void addImplicitDistributedActorMembersToClass(ClassDecl *decl);

}


Expand Down
Loading