Skip to content

Commit 827ae01

Browse files
committed
complete implementation using localUnownedExecutor customization point
1 parent acff772 commit 827ae01

18 files changed

+162
-148
lines changed

include/swift/AST/Decl.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4533,6 +4533,7 @@ class ClassDecl final : public NominalTypeDecl {
45334533

45344534
/// Fetch this class's unownedExecutor property, if it has one.
45354535
const VarDecl *getUnownedExecutorProperty() const;
4536+
const VarDecl *getLocalUnownedExecutorProperty() const;
45364537

45374538
/// Is this the NSObject class type?
45384539
bool isNSObject() const;

lib/AST/Decl.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9625,6 +9625,29 @@ const VarDecl *ClassDecl::getUnownedExecutorProperty() const {
96259625
return nullptr;
96269626
}
96279627

9628+
const VarDecl *ClassDecl::getLocalUnownedExecutorProperty() const {
9629+
auto &C = getASTContext();
9630+
9631+
if (!isDistributedActor())
9632+
return nullptr;
9633+
9634+
llvm::SmallVector<ValueDecl *, 2> results;
9635+
this->lookupQualified(getSelfNominalTypeDecl(),
9636+
DeclNameRef(C.Id_localUnownedExecutor),
9637+
NL_ProtocolMembers,
9638+
results);
9639+
9640+
for (auto candidate: results) {
9641+
if (isa<ProtocolDecl>(candidate->getDeclContext()))
9642+
continue;
9643+
9644+
if (VarDecl *var = dyn_cast<VarDecl>(candidate))
9645+
return var;
9646+
}
9647+
9648+
return nullptr;
9649+
}
9650+
96289651
bool ClassDecl::isRootDefaultActor() const {
96299652
return isRootDefaultActor(getModuleContext(), ResilienceExpansion::Maximal);
96309653
}

lib/SILOptimizer/Mandatory/LowerHopToActor.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ SILValue LowerHopToActor::emitGetExecutor(SILBuilderWithScope &B,
192192
KnownProtocolKind::DistributedActor :
193193
KnownProtocolKind::Actor;
194194
auto actorProtocol = ctx.getProtocol(actorKind);
195+
bool optionalExecutor = false;
195196
auto req = getUnownedExecutorGetter(ctx, actorProtocol);
196197
assert(req && "Concurrency library broken");
197198
SILDeclRef fn(req, SILDeclRef::Kind::Func);
@@ -214,8 +215,6 @@ SILValue LowerHopToActor::emitGetExecutor(SILBuilderWithScope &B,
214215
auto executorDecl = ctx.getUnownedSerialExecutorDecl();
215216
auto executorProps = executorDecl->getStoredProperties();
216217
assert(executorProps.size() == 1);
217-
fprintf(stderr, "[%s:%d](%s) executorProps = \n", __FILE_NAME__, __LINE__, __FUNCTION__);
218-
executorProps[0]->dump();
219218
unmarkedExecutor =
220219
B.createStructExtract(loc, witnessCall, executorProps[0]);
221220
}

lib/Sema/DerivedConformanceDistributedActor.cpp

Lines changed: 19 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -620,81 +620,14 @@ static Expr *constructDistributedUnownedSerialExecutor(ASTContext &ctx,
620620
return nullptr;
621621
}
622622

623-
//static Expr *constructOptionalSome(ASTContext &ctx,
624-
// Expr *arg) {
625-
// auto optionalDecl = ctx.getOptionalDecl();
626-
// if (!optionalDecl) return nullptr;
627-
//
628-
// for (auto member: optionalDecl->getAllMembers()) {
629-
// auto ctor = dyn_cast<ConstructorDecl>(member);
630-
// if (!ctor) continue;
631-
//
632-
// // Find the init(_:) which initialized as `some`
633-
// auto params = ctor->getParameters();
634-
// if (params->size() != 1 ||
635-
// params->get(0)->getArgumentName() != Identifier())
636-
// continue;
637-
//
638-
// ctor->dump();
639-
//
640-
//
641-
//// Type optionalType = optionalDecl->getDeclaredInterfaceType();
642-
//// fprintf(stderr, "[%s:%d](%s) optionalDecl\n", __FILE_NAME__, __LINE__, __FUNCTION__);
643-
//// optionalType.dump();
644-
//
645-
// Type optionalType = BoundGenericEnumType::get(optionalDecl, Type(), {arg->getType()});
646-
// fprintf(stderr, "[%s:%d](%s) better optionalType\n", __FILE_NAME__, __LINE__, __FUNCTION__);
647-
// optionalType->dump();
648-
//
649-
// Type ctorType = ctor->getInterfaceType();
650-
// fprintf(stderr, "[%s:%d](%s) ctor type\n", __FILE_NAME__, __LINE__, __FUNCTION__);
651-
// ctorType->dump();
652-
//
653-
// // We have the right initializer. Build a reference to it of type:
654-
// // (Optional.Type)
655-
// // -> (T) -> T
656-
// auto initRef = new (ctx) DeclRefExpr(ctor, DeclNameLoc(), /*implicit*/true,
657-
// AccessSemantics::Ordinary,
658-
// ctorType);
659-
//
660-
// // Apply the initializer to the metatype, building an expression of type:
661-
// // (Builtin.Executor) -> Optional<UnownedSerialExecutor>
662-
// // auto metatypeRef = TypeExpr::createImplicit(optionalType, ctx);
663-
// auto metatypeRef = TypeExpr::createImplicit(optionalType, ctx);
664-
// GenericIdentTypeRepr::create(ctx, DeclNameLoc(optionalDecl->getNameLoc()),
665-
// DeclNameRef(optionalDecl->getBaseName()),
666-
// {arg->getType()}, SourceRange());
667-
////
668-
//// auto ctorAppliedType = new (ctx) TypeRepr(
669-
//// GenericIdentTypeRepr::create(ctx, DeclNameLoc(), )
670-
//// );
671-
//
672-
//// TypeExpr::createForSpecializedDecl(optionalType.get, {arg->getType()}, SourceRange(), ctx);
673-
//// Type ctorAppliedType = ctorType->getAs<FunctionType>()->getResult();
674-
// auto selfApply = ConstructorRefCallExpr::create(ctx, initRef, metatypeRef
675-
//// ,
676-
//// ctorAppliedType
677-
// );
678-
// selfApply->setImplicit(true);
679-
// selfApply->setThrows(false);
680-
//
681-
// // Call the constructor, building an expression of type
682-
// // Optional<T>.
683-
// auto *argList = ArgumentList::forImplicitUnlabeled(ctx, {arg});
684-
// auto call = CallExpr::createImplicit(ctx, selfApply, argList);
685-
// call->setType(optionalType);
686-
// call->setThrows(false);
687-
// return call;
688-
// }
689-
//
690-
// assert(false);
691-
//}
692-
693623
static std::pair<BraceStmt *, bool>
694-
deriveBodyDistributedActor_unownedExecutor(AbstractFunctionDecl *getter, void *) {
695-
// var unownedExecutor: UnownedSerialExecutor? {
624+
deriveBodyDistributedActor_localUnownedExecutor(AbstractFunctionDecl *getter, void *) {
625+
// var localUnownedExecutor: UnownedSerialExecutor? {
696626
// get {
697-
// return Builtin.buildDefaultActorExecutorRef(self)
627+
// guard __isLocalActor(self) else {
628+
// return nil
629+
// }
630+
// return Optional(Builtin.buildDefaultActorExecutorRef(self))
698631
// }
699632
// }
700633
ASTContext &ctx = getter->getASTContext();
@@ -751,16 +684,11 @@ deriveBodyDistributedActor_unownedExecutor(AbstractFunctionDecl *getter, void *)
751684

752685
auto body = BraceStmt::create(
753686
ctx, SourceLoc(), { returnNilIfRemoteStmt, ret }, SourceLoc(), /*implicit=*/true);
754-
755-
fprintf(stderr, "[%s:%d](%s) ================================================================================================\n", __FILE_NAME__, __LINE__, __FUNCTION__);
756-
body->dump();
757-
fprintf(stderr, "[%s:%d](%s) ================================================================================================\n", __FILE_NAME__, __LINE__, __FUNCTION__);
758-
759687
return { body, /*isTypeChecked=*/true };
760688
}
761689

762-
/// Derive the declaration of DistributedActor's unownedExecutor property.
763-
static ValueDecl *deriveDistributedActor_unownedExecutor(DerivedConformance &derived) {
690+
/// Derive the declaration of DistributedActor's localUnownedExecutor property.
691+
static ValueDecl *deriveDistributedActor_localUnownedExecutor(DerivedConformance &derived) {
764692
ASTContext &ctx = derived.Context;
765693

766694
// Retrieve the types and declarations we'll need to form this operation.
@@ -774,7 +702,7 @@ static ValueDecl *deriveDistributedActor_unownedExecutor(DerivedConformance &der
774702
Type optionalExecutorType = executorType->wrapInOptionalType();
775703

776704
if (auto classDecl = dyn_cast<ClassDecl>(derived.Nominal)) {
777-
if (auto existing = classDecl->getUnownedExecutorProperty()) {
705+
if (auto existing = classDecl->getLocalUnownedExecutorProperty()) {
778706
if (existing->getInterfaceType()->isEqual(optionalExecutorType)) {
779707
return const_cast<VarDecl *>(existing);
780708
} else {
@@ -785,7 +713,7 @@ static ValueDecl *deriveDistributedActor_unownedExecutor(DerivedConformance &der
785713
}
786714

787715
auto propertyPair = derived.declareDerivedProperty(
788-
DerivedConformance::SynthesizedIntroducer::Var, ctx.Id_unownedExecutor,
716+
DerivedConformance::SynthesizedIntroducer::Var, ctx.Id_localUnownedExecutor,
789717
optionalExecutorType, optionalExecutorType,
790718
/*static*/ false, /*final*/ false);
791719
auto property = propertyPair.first;
@@ -811,7 +739,7 @@ static ValueDecl *deriveDistributedActor_unownedExecutor(DerivedConformance &der
811739

812740
auto getter =
813741
derived.addGetterToReadOnlyDerivedProperty(property, optionalExecutorType);
814-
getter->setBodySynthesizer(deriveBodyDistributedActor_unownedExecutor);
742+
getter->setBodySynthesizer(deriveBodyDistributedActor_localUnownedExecutor);
815743

816744
// IMPORTANT: MUST BE AFTER [id, actorSystem].
817745
if (auto id = derived.Nominal->getDistributedActorIDProperty()) {
@@ -854,24 +782,24 @@ static void assertRequiredSynthesizedPropertyOrder(DerivedConformance &derived,
854782
if (auto id = Nominal->getDistributedActorIDProperty()) {
855783
if (auto system = Nominal->getDistributedActorSystemProperty()) {
856784
if (auto classDecl = dyn_cast<ClassDecl>(derived.Nominal)) {
857-
if (auto unownedExecutor = classDecl->getUnownedExecutorProperty()) {
858-
int idIdx, actorSystemIdx, unownedExecutorIdx = 0;
785+
if (auto localUnownedExecutor = classDecl->getLocalUnownedExecutorProperty()) {
786+
int idIdx, actorSystemIdx, localUnownedExecutorIdx = 0;
859787
int idx = 0;
860788
for (auto member: Nominal->getMembers()) {
861789
if (auto binding = dyn_cast<PatternBindingDecl>(member)) {
862790
if (binding->getSingleVar()->getName() == Context.Id_id) {
863791
idIdx = idx;
864792
} else if (binding->getSingleVar()->getName() == Context.Id_actorSystem) {
865793
actorSystemIdx = idx;
866-
} else if (binding->getSingleVar()->getName() == Context.Id_unownedExecutor) {
867-
unownedExecutorIdx = idx;
794+
} else if (binding->getSingleVar()->getName() == Context.Id_localUnownedExecutor) {
795+
localUnownedExecutorIdx = idx;
868796
}
869797
idx += 1;
870798
}
871799
}
872-
if (idIdx + actorSystemIdx + unownedExecutorIdx >= 0 + 1 + 2) {
800+
if (idIdx + actorSystemIdx + localUnownedExecutorIdx >= 0 + 1 + 2) {
873801
// we have found all the necessary fields, let's assert their order
874-
assert(idIdx < actorSystemIdx < unownedExecutorIdx && "order of fields MUST be exact.");
802+
assert(idIdx < actorSystemIdx < localUnownedExecutorIdx && "order of fields MUST be exact.");
875803
}
876804
}
877805
}
@@ -893,8 +821,8 @@ ValueDecl *DerivedConformance::deriveDistributedActor(ValueDecl *requirement) {
893821
derivedValue = deriveDistributedActor_id(*this);
894822
} else if (var->getName() == Context.Id_actorSystem) {
895823
derivedValue = deriveDistributedActor_actorSystem(*this);
896-
} else if (var->getName() == Context.Id_unownedExecutor) {
897-
derivedValue = deriveDistributedActor_unownedExecutor(*this);
824+
} else if (var->getName() == Context.Id_localUnownedExecutor) {
825+
derivedValue = deriveDistributedActor_localUnownedExecutor(*this);
898826
}
899827

900828
assertRequiredSynthesizedPropertyOrder(*this, derivedValue);

lib/Sema/DerivedConformances.cpp

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -335,11 +335,11 @@ ValueDecl *DerivedConformance::getDerivableRequirement(NominalTypeDecl *nominal,
335335

336336
// Actor.unownedExecutor
337337
if (name.isSimpleName(ctx.Id_unownedExecutor)) {
338-
if (nominal->isDistributedActor()) {
339-
return getRequirement(KnownProtocolKind::DistributedActor);
340-
} else {
338+
// if (nominal->isDistributedActor()) {
339+
// return getRequirement(KnownProtocolKind::DistributedActor);
340+
// } else {
341341
return getRequirement(KnownProtocolKind::Actor);
342-
}
342+
// }
343343
}
344344

345345
// DistributedActor.id
@@ -350,6 +350,11 @@ ValueDecl *DerivedConformance::getDerivableRequirement(NominalTypeDecl *nominal,
350350
if (name.isSimpleName(ctx.Id_actorSystem))
351351
return getRequirement(KnownProtocolKind::DistributedActor);
352352

353+
// DistributedActor.localUnownedExecutor
354+
if (name.isSimpleName(ctx.Id_localUnownedExecutor)) {
355+
return getRequirement(KnownProtocolKind::DistributedActor);
356+
}
357+
353358
return nullptr;
354359
}
355360

@@ -682,7 +687,6 @@ GuardStmt *DerivedConformance::returnFalseIfNotEqualGuard(ASTContext &C,
682687
GuardStmt *DerivedConformance::returnNilIfFalseGuardTypeChecked(ASTContext &C,
683688
Expr *testExpr,
684689
Type optionalWrappedType) {
685-
auto trueExpr = new (C) BooleanLiteralExpr(true, SourceLoc(), /*implicit=*/true);
686690
auto nilExpr = new (C) NilLiteralExpr(SourceLoc(), /*implicit=*/true);
687691
nilExpr->setType(optionalWrappedType->wrapInOptionalType());
688692

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,19 +192,39 @@ bool IsDefaultActorRequest::evaluate(
192192
// If we synthesized the unownedExecutor property, we should've
193193
// added a semantics attribute to it (if it was actually a default
194194
// actor).
195+
bool foundExecutorPropertyImpl = false;
196+
bool isDefaultActor = false;
195197
if (auto executorProperty = classDecl->getUnownedExecutorProperty()) {
196-
bool isDefaultActor =
198+
foundExecutorPropertyImpl = true;
199+
isDefaultActor = isDefaultActor ||
197200
executorProperty->getAttrs().hasSemanticsAttr(SEMANTICS_DEFAULT_ACTOR);
201+
}
202+
203+
// Maybe it was a distributed actor, let's double-check it's localUnownedExecutor property.
204+
// If we synthesized that one with appropriate semantics we may still be a default actor.
205+
if (!isDefaultActor && classDecl->isDistributedActor()) {
206+
if (auto localExecutorProperty = classDecl->getLocalUnownedExecutorProperty()) {
207+
foundExecutorPropertyImpl = true;
208+
isDefaultActor = isDefaultActor ||
209+
localExecutorProperty->getAttrs().hasSemanticsAttr(SEMANTICS_DEFAULT_ACTOR);
210+
}
211+
}
212+
213+
// Only if we found one of the executor properties, do we return the status of default or not,
214+
// based on the findings of the semantics attribute of that located property.
215+
if (foundExecutorPropertyImpl) {
198216
if (!isDefaultActor &&
199217
classDecl->getASTContext().LangOpts.isConcurrencyModelTaskToThread() &&
200218
!AvailableAttr::isUnavailable(classDecl)) {
201219
classDecl->diagnose(
202220
diag::concurrency_task_to_thread_model_custom_executor,
203221
"task-to-thread concurrency model");
204222
}
223+
205224
return isDefaultActor;
206225
}
207226

227+
// Otherwise, we definitely are a default actor.
208228
return true;
209229
}
210230

lib/Sema/TypeCheckDeclOverride.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2039,8 +2039,8 @@ static bool checkSingleOverride(ValueDecl *override, ValueDecl *base) {
20392039
cast<ClassDecl>(prop->getDeclContext())->isActor() &&
20402040
!prop->isStatic() &&
20412041
prop->getName() == ctx.Id_unownedExecutor &&
2042-
prop->getInterfaceType()->getAnyNominal() ==
2043-
ctx.getUnownedSerialExecutorDecl());
2042+
prop->getName() == ctx.Id_localUnownedExecutor &&
2043+
prop->getInterfaceType()->getAnyNominal() == ctx.getUnownedSerialExecutorDecl());
20442044
};
20452045

20462046
if (isActorUnownedExecutor()) {

stdlib/public/Concurrency/Executor.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,8 @@ public struct UnownedSerialExecutor: Sendable {
110110
#endif
111111

112112
@inlinable
113-
internal init(_ executor: Builtin.Executor) {
113+
// @_spi(ConcurrencyExecutors) // FIXME:!!!!
114+
public init(_ executor: Builtin.Executor) {
114115
#if compiler(>=5.5) && $BuiltinExecutor
115116
self.executor = executor
116117
#endif

0 commit comments

Comments
 (0)