[Concurrency] Distributed actor's unownedExecutor should be optional (#64499)

This commit is contained in:
Konrad `ktoso` Malawski
2023-03-22 08:40:41 +09:00
committed by GitHub
parent 230dfcc30c
commit 345c221f56
25 changed files with 575 additions and 72 deletions

View File

@@ -621,10 +621,13 @@ static Expr *constructDistributedUnownedSerialExecutor(ASTContext &ctx,
}
static std::pair<BraceStmt *, bool>
deriveBodyDistributedActor_unownedExecutor(AbstractFunctionDecl *getter, void *) {
// var unownedExecutor: UnownedSerialExecutor {
deriveBodyDistributedActor_localUnownedExecutor(AbstractFunctionDecl *getter, void *) {
// var localUnownedExecutor: UnownedSerialExecutor? {
// get {
// return Builtin.buildDefaultActorExecutorRef(self)
// guard __isLocalActor(self) else {
// return nil
// }
// return Optional(Builtin.buildDefaultActorExecutorRef(self))
// }
// }
ASTContext &ctx = getter->getASTContext();
@@ -641,33 +644,53 @@ deriveBodyDistributedActor_unownedExecutor(AbstractFunctionDecl *getter, void *)
Expr *selfArg = DerivedConformance::createSelfDeclRef(getter);
selfArg->setType(selfType);
// Prepare the builtin call, we'll use it after the guard, but want to take the type
// of its return type earlier, so we prepare it here.
// The builtin call gives us a Builtin.Executor.
auto builtinCall =
DerivedConformance::createBuiltinCall(ctx,
BuiltinValueKind::BuildDefaultActorExecutorRef,
{selfType}, {}, {selfArg});
// Turn that into an UnownedSerialExecutor.
auto initCall = constructDistributedUnownedSerialExecutor(ctx, builtinCall);
if (!initCall) return failure();
auto ret = new (ctx) ReturnStmt(SourceLoc(), initCall, /*implicit*/ true);
// guard __isLocalActor(self) else {
// return nil
// }
auto isLocalActorDecl = ctx.getIsLocalDistributedActor();
DeclRefExpr *isLocalActorExpr =
new (ctx) DeclRefExpr(ConcreteDeclRef(isLocalActorDecl), DeclNameLoc(), /*implicit=*/true,
AccessSemantics::Ordinary,
FunctionType::get({AnyFunctionType::Param(ctx.getAnyObjectType())},
ctx.getBoolType()));
Expr *selfForIsLocalArg = DerivedConformance::createSelfDeclRef(getter);
selfForIsLocalArg->setType(selfType);
auto *argListForIsLocal =
ArgumentList::forImplicitSingle(ctx, Identifier(),
ErasureExpr::create(ctx, selfForIsLocalArg, ctx.getAnyObjectType(), {}, {}));
CallExpr *isLocalActorCall = CallExpr::createImplicit(ctx, isLocalActorExpr, argListForIsLocal);
isLocalActorCall->setType(ctx.getBoolType());
isLocalActorCall->setThrows(false);
auto returnNilIfRemoteStmt = DerivedConformance::returnNilIfFalseGuardTypeChecked(
ctx, isLocalActorCall, /*optionalWrappedType=*/initCall->getType());
// Finalize preparing the unowned executor for returning.
auto wrappedCall = new (ctx) InjectIntoOptionalExpr(initCall, initCall->getType()->wrapInOptionalType());
auto ret = new (ctx) ReturnStmt(SourceLoc(), wrappedCall, /*implicit*/ true);
auto body = BraceStmt::create(
ctx, SourceLoc(), { ret }, SourceLoc(), /*implicit=*/true);
ctx, SourceLoc(), { returnNilIfRemoteStmt, ret }, SourceLoc(), /*implicit=*/true);
return { body, /*isTypeChecked=*/true };
}
/// Derive the declaration of DistributedActor's unownedExecutor property.
static ValueDecl *deriveDistributedActor_unownedExecutor(DerivedConformance &derived) {
/// Derive the declaration of DistributedActor's localUnownedExecutor property.
static ValueDecl *deriveDistributedActor_localUnownedExecutor(DerivedConformance &derived) {
ASTContext &ctx = derived.Context;
if (auto classDecl = dyn_cast<ClassDecl>(derived.Nominal)) {
if (auto existing = classDecl->getUnownedExecutorProperty()) {
return const_cast<VarDecl*>(existing);
}
}
// Retrieve the types and declarations we'll need to form this operation.
auto executorDecl = ctx.getUnownedSerialExecutorDecl();
if (!executorDecl) {
@@ -676,16 +699,28 @@ static ValueDecl *deriveDistributedActor_unownedExecutor(DerivedConformance &der
return nullptr;
}
Type executorType = executorDecl->getDeclaredInterfaceType();
Type optionalExecutorType = executorType->wrapInOptionalType();
if (auto classDecl = dyn_cast<ClassDecl>(derived.Nominal)) {
if (auto existing = classDecl->getLocalUnownedExecutorProperty()) {
if (existing->getInterfaceType()->isEqual(optionalExecutorType)) {
return const_cast<VarDecl *>(existing);
} else {
// bad type, should be diagnosed elsewhere
return nullptr;
}
}
}
auto propertyPair = derived.declareDerivedProperty(
DerivedConformance::SynthesizedIntroducer::Var, ctx.Id_unownedExecutor,
executorType, executorType,
DerivedConformance::SynthesizedIntroducer::Var, ctx.Id_localUnownedExecutor,
optionalExecutorType, optionalExecutorType,
/*static*/ false, /*final*/ false);
auto property = propertyPair.first;
property->setSynthesized(true);
property->getAttrs().add(new (ctx) SemanticsAttr(SEMANTICS_DEFAULT_ACTOR,
SourceLoc(), SourceRange(),
/*implicit*/ true));
/*implicit*/ true));
property->getAttrs().add(new (ctx) NonisolatedAttr(/*IsImplicit=*/true));
// Make the property implicitly final.
@@ -703,8 +738,8 @@ static ValueDecl *deriveDistributedActor_unownedExecutor(DerivedConformance &der
property, asAvailableAs, ctx);
auto getter =
derived.addGetterToReadOnlyDerivedProperty(property, executorType);
getter->setBodySynthesizer(deriveBodyDistributedActor_unownedExecutor);
derived.addGetterToReadOnlyDerivedProperty(property, optionalExecutorType);
getter->setBodySynthesizer(deriveBodyDistributedActor_localUnownedExecutor);
// IMPORTANT: MUST BE AFTER [id, actorSystem].
if (auto id = derived.Nominal->getDistributedActorIDProperty()) {
@@ -747,8 +782,8 @@ static void assertRequiredSynthesizedPropertyOrder(DerivedConformance &derived,
if (auto id = Nominal->getDistributedActorIDProperty()) {
if (auto system = Nominal->getDistributedActorSystemProperty()) {
if (auto classDecl = dyn_cast<ClassDecl>(derived.Nominal)) {
if (auto unownedExecutor = classDecl->getUnownedExecutorProperty()) {
int idIdx, actorSystemIdx, unownedExecutorIdx = 0;
if (auto localUnownedExecutor = classDecl->getLocalUnownedExecutorProperty()) {
int idIdx, actorSystemIdx, localUnownedExecutorIdx = 0;
int idx = 0;
for (auto member: Nominal->getMembers()) {
if (auto binding = dyn_cast<PatternBindingDecl>(member)) {
@@ -756,15 +791,15 @@ static void assertRequiredSynthesizedPropertyOrder(DerivedConformance &derived,
idIdx = idx;
} else if (binding->getSingleVar()->getName() == Context.Id_actorSystem) {
actorSystemIdx = idx;
} else if (binding->getSingleVar()->getName() == Context.Id_unownedExecutor) {
unownedExecutorIdx = idx;
} else if (binding->getSingleVar()->getName() == Context.Id_localUnownedExecutor) {
localUnownedExecutorIdx = idx;
}
idx += 1;
}
}
if (idIdx + actorSystemIdx + unownedExecutorIdx >= 0 + 1 + 2) {
if (idIdx + actorSystemIdx + localUnownedExecutorIdx >= 0 + 1 + 2) {
// we have found all the necessary fields, let's assert their order
assert(idIdx < actorSystemIdx < unownedExecutorIdx && "order of fields MUST be exact.");
assert(idIdx < actorSystemIdx < localUnownedExecutorIdx && "order of fields MUST be exact.");
}
}
}
@@ -786,8 +821,8 @@ ValueDecl *DerivedConformance::deriveDistributedActor(ValueDecl *requirement) {
derivedValue = deriveDistributedActor_id(*this);
} else if (var->getName() == Context.Id_actorSystem) {
derivedValue = deriveDistributedActor_actorSystem(*this);
} else if (var->getName() == Context.Id_unownedExecutor) {
derivedValue = deriveDistributedActor_unownedExecutor(*this);
} else if (var->getName() == Context.Id_localUnownedExecutor) {
derivedValue = deriveDistributedActor_localUnownedExecutor(*this);
}
assertRequiredSynthesizedPropertyOrder(*this, derivedValue);