Skip to content

🍒[5.7][Distributed] Fixes to distributed funcs and protocols #59736

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 3 commits into from
Jun 30, 2022
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
3 changes: 3 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 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
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
31 changes: 31 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
23 changes: 15 additions & 8 deletions lib/SILGen/SILGenPoly.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4435,15 +4435,22 @@ void SILGenFunction::emitProtocolWitness(
SmallVector<ManagedValue, 8> origParams;
collectThunkParams(loc, origParams);

// If the witness is isolated to a distributed actor, but the requirement is
// not, go through the distributed thunk.
if (witness.hasDecl() &&
getActorIsolation(witness.getDecl()).isDistributedActor() &&
requirement.hasDecl() &&
!getActorIsolation(requirement.getDecl()).isDistributedActor()) {
witness = SILDeclRef(
cast<AbstractFunctionDecl>(witness.getDecl())->getDistributedThunk())
.asDistributed();
getActorIsolation(witness.getDecl()).isDistributedActor()) {
// We witness protocol requirements using the distributed thunk, when:
// - the witness is isolated to a distributed actor, but the requirement is not
// - the requirement is a distributed func, and therefore can only be witnessed
// by a distributed func; we handle this by witnessing the requirement with the thunk
// FIXME(distributed): this limits us to only allow distributed explicitly throwing async requirements... we need to fix this somehow.
if (requirement.hasDecl()) {
if ((!getActorIsolation(requirement.getDecl()).isDistributedActor()) ||
(isa<FuncDecl>(requirement.getDecl()) &&
requirement.getFuncDecl()->isDistributed())) {
auto thunk = cast<AbstractFunctionDecl>(witness.getDecl())
->getDistributedThunk();
witness = SILDeclRef(thunk).asDistributed();
}
}
} else if (enterIsolation) {
// If we are supposed to enter the actor, do so now by hopping to the
// actor.
Expand Down
13 changes: 7 additions & 6 deletions lib/Sema/CodeSynthesisDistributedActor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -613,8 +613,13 @@ static FuncDecl *createDistributedThunkFunction(FuncDecl *func) {
auto &C = func->getASTContext();
auto DC = func->getDeclContext();

auto systemTy = getConcreteReplacementForProtocolActorSystemType(func);
assert(systemTy &&
// NOTE: So we don't need a thunk in the protocol, we should call the underlying
// thing instead, which MUST have a thunk, since it must be a distributed func as well...
if (dyn_cast<ProtocolDecl>(DC)) {
return nullptr;
}

assert(getConcreteReplacementForProtocolActorSystemType(func) &&
"Thunk synthesis must have concrete actor system type available");

DeclName thunkName = func->getName();
Expand Down Expand Up @@ -753,7 +758,6 @@ FuncDecl *GetDistributedThunkRequest::evaluate(
return nullptr;

auto &C = distributedTarget->getASTContext();
auto DC = distributedTarget->getDeclContext();

if (!getConcreteReplacementForProtocolActorSystemType(distributedTarget)) {
// Don't synthesize thunks, unless there is a *concrete* ActorSystem.
Expand All @@ -777,9 +781,6 @@ FuncDecl *GetDistributedThunkRequest::evaluate(
if (!C.getLoadedModule(C.Id_Distributed))
return nullptr;

auto nominal = DC->getSelfNominalTypeDecl(); // NOTE: Always from DC
assert(nominal);

// --- Prepare the "distributed thunk" which does the "maybe remote" dance:
return createDistributedThunkFunction(func);
}
Expand Down
18 changes: 18 additions & 0 deletions lib/Sema/TypeCheckAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5793,6 +5793,24 @@ void AttributeChecker::visitDistributedActorAttr(DistributedActorAttr *attr) {
}
return;
}

// Diagnose for the limitation that we currently have to require distributed
// actor constrained protocols to declare the distributed requirements as
// 'async throws'
// FIXME: rdar://95949498 allow requirements to not declare explicit async/throws in protocols; those effects are implicit in any case
if (isa<ProtocolDecl>(dc)) {
if (!funcDecl->hasAsync() || !funcDecl->hasThrows()) {
auto diag = funcDecl->diagnose(diag::distributed_method_requirement_must_be_async_throws,
funcDecl->getName());
if (!funcDecl->hasAsync()) {
diag.fixItInsertAfter(funcDecl->getThrowsLoc(), " async");
}
if (!funcDecl->hasThrows()) {
diag.fixItInsertAfter(funcDecl->getThrowsLoc(), " throws");
}
return;
}
}
}
}

Expand Down
1 change: 1 addition & 0 deletions lib/Sema/TypeCheckDeclPrimary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2880,6 +2880,7 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
}

TypeChecker::checkDeclAttributes(FD);
TypeChecker::checkDistributedFunc(FD);

if (!checkOverrides(FD)) {
// If a method has an 'override' keyword but does not
Expand Down
38 changes: 36 additions & 2 deletions lib/Sema/TypeCheckDistributed.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -502,9 +502,25 @@ bool CheckDistributedFunctionRequest::evaluate(
serializationRequirements = getDistributedSerializationRequirementProtocols(
getDistributedActorSystemType(actor)->getAnyNominal(),
C.getProtocol(KnownProtocolKind::DistributedActorSystem));
} else if (isa<ProtocolDecl>(DC)) {
if (auto seqReqTy =
getConcreteReplacementForMemberSerializationRequirement(func)) {
auto seqReqTyDes = seqReqTy->castTo<ExistentialType>()->getConstraintType()->getDesugaredType();
for (auto req : flattenDistributedSerializationTypeToRequiredProtocols(seqReqTyDes)) {
serializationRequirements.insert(req);
}
}

// The distributed actor constrained protocol has no serialization requirements
// or actor system defined, so these will only be enforced, by implementations
// of DAs conforming to it, skip checks here.
if (serializationRequirements.empty()) {
return false;
}
} else {
llvm_unreachable("Cannot handle types other than extensions and actor "
"declarations in distributed function checking.");
llvm_unreachable("Distributed function detected in type other than extension, "
"distributed actor, or protocol! This should not be possible "
", please file a bug.");
}

// If the requirement is exactly `Codable` we diagnose it ia bit nicer.
Expand Down Expand Up @@ -652,12 +668,23 @@ void TypeChecker::checkDistributedActor(SourceFile *SF, NominalTypeDecl *nominal
// If applicable, this will create the default 'init(transport:)' initializer
(void)nominal->getDefaultInitializer();


for (auto member : nominal->getMembers()) {
// --- Ensure all thunks
if (auto func = dyn_cast<AbstractFunctionDecl>(member)) {
if (!func->isDistributed())
continue;

if (!isa<ProtocolDecl>(nominal)) {
auto systemTy = getConcreteReplacementForProtocolActorSystemType(func);
if (!systemTy || systemTy->hasError()) {
nominal->diagnose(
diag::distributed_actor_conformance_missing_system_type,
nominal->getName());
return;
}
}

if (auto thunk = func->getDistributedThunk()) {
SF->DelayedFunctions.push_back(thunk);
}
Expand All @@ -675,6 +702,13 @@ void TypeChecker::checkDistributedActor(SourceFile *SF, NominalTypeDecl *nominal
(void)nominal->getDistributedActorIDProperty();
}

void TypeChecker::checkDistributedFunc(FuncDecl *func) {
if (!func->isDistributed())
return;

swift::checkDistributedFunction(func);
}

llvm::SmallPtrSet<ProtocolDecl *, 2>
swift::getDistributedSerializationRequirementProtocols(
NominalTypeDecl *nominal, ProtocolDecl *protocol) {
Expand Down
3 changes: 3 additions & 0 deletions lib/Sema/TypeChecker.h
Original file line number Diff line number Diff line change
Expand Up @@ -1104,6 +1104,9 @@ diagnosePotentialOpaqueTypeUnavailability(SourceRange ReferenceRange,
/// Type check a 'distributed actor' declaration.
void checkDistributedActor(SourceFile *SF, NominalTypeDecl *decl);

/// Type check a single 'distributed func' declaration.
void checkDistributedFunc(FuncDecl *func);

void checkConcurrencyAvailability(SourceRange ReferenceRange,
const DeclContext *ReferenceDC);

Expand Down
Loading