Skip to content

Commit aeef419

Browse files
committed
[Concurrency] Ensure that enqueue(partialTask:) is first in actor vtables.
Actor classes never have non-actor superclasses, so we can ensure that all actor classes have a common vtable prefix for the `enqueue(partialTask:)` operation. This allows us to treat all actor classes uniformly, without having to go through the Actor witness table every time.
1 parent c2b8565 commit aeef419

File tree

9 files changed

+95
-27
lines changed

9 files changed

+95
-27
lines changed

include/swift/AST/Decl.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6329,6 +6329,14 @@ class FuncDecl : public AbstractFunctionDecl {
63296329

63306330
bool isMainTypeMainMethod() const;
63316331

6332+
/// Whether the given name is enqueue(partialTask:), which is used for
6333+
/// actors.
6334+
static bool isEnqueuePartialTaskName(ASTContext &ctx, DeclName name);
6335+
6336+
/// Determine whether this function is the witness to the Actor protocol's
6337+
/// enqueue(partialTask:) operation within an actor.
6338+
bool isActorEnqueuePartialTaskWitness() const;
6339+
63326340
SelfAccessKind getSelfAccessKind() const;
63336341

63346342
void setSelfAccessKind(SelfAccessKind mod) {

lib/AST/Decl.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7508,6 +7508,56 @@ bool FuncDecl::isMainTypeMainMethod() const {
75087508
getParameters()->size() == 0;
75097509
}
75107510

7511+
bool FuncDecl::isEnqueuePartialTaskName(ASTContext &ctx, DeclName name) {
7512+
if (name.isCompoundName() && name.getBaseName() == ctx.Id_enqueue) {
7513+
auto argumentNames = name.getArgumentNames();
7514+
return argumentNames.size() == 1 && argumentNames[0] == ctx.Id_partialTask;
7515+
}
7516+
7517+
return false;
7518+
}
7519+
7520+
bool FuncDecl::isActorEnqueuePartialTaskWitness() const {
7521+
if (!isEnqueuePartialTaskName(getASTContext(), getName()))
7522+
return false;
7523+
7524+
auto classDecl = getDeclContext()->getSelfClassDecl();
7525+
if (!classDecl)
7526+
return false;
7527+
7528+
if (!classDecl->isActor())
7529+
return false;
7530+
7531+
ASTContext &ctx = getASTContext();
7532+
auto actorProto = ctx.getProtocol(KnownProtocolKind::Actor);
7533+
if (!actorProto)
7534+
return false;
7535+
7536+
FuncDecl *requirement = nullptr;
7537+
for (auto protoMember : actorProto->getParsedMembers()) {
7538+
if (auto protoFunc = dyn_cast<FuncDecl>(protoMember)) {
7539+
if (isEnqueuePartialTaskName(ctx, protoFunc->getName())) {
7540+
requirement = protoFunc;
7541+
break;
7542+
}
7543+
}
7544+
}
7545+
7546+
if (!requirement)
7547+
return false;
7548+
7549+
SmallVector<ProtocolConformance *, 1> conformances;
7550+
classDecl->lookupConformance(
7551+
classDecl->getModuleContext(), actorProto, conformances);
7552+
for (auto conformance : conformances) {
7553+
auto witness = conformance->getWitnessDecl(requirement);
7554+
if (witness == this)
7555+
return true;
7556+
}
7557+
7558+
return false;
7559+
}
7560+
75117561
ConstructorDecl::ConstructorDecl(DeclName Name, SourceLoc ConstructorLoc,
75127562
bool Failable, SourceLoc FailabilityLoc,
75137563
bool Throws,

lib/Sema/DerivedConformanceActor.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ ValueDecl *DerivedConformance::deriveActor(ValueDecl *requirement) {
287287
if (!func)
288288
return nullptr;
289289

290-
if (isEnqueuePartialTask(Context, func->getName()))
290+
if (FuncDecl::isEnqueuePartialTaskName(Context, func->getName()))
291291
return deriveActor_enqueuePartialTask(*this);
292292

293293
return nullptr;

lib/Sema/DerivedConformances.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ ValueDecl *DerivedConformance::getDerivableRequirement(NominalTypeDecl *nominal,
365365
}
366366

367367
// Actor.enqueue(partialTask: PartialTask)
368-
if (isEnqueuePartialTask(ctx, name)) {
368+
if (FuncDecl::isEnqueuePartialTaskName(ctx, name)) {
369369
return getRequirement(KnownProtocolKind::Actor);
370370
}
371371

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -749,12 +749,3 @@ ActorIsolation swift::getActorIsolation(ValueDecl *value) {
749749
ctx.evaluator, ActorIsolationRequest{value},
750750
ActorIsolation::forUnspecified());
751751
}
752-
753-
bool swift::isEnqueuePartialTask(ASTContext &ctx, DeclName name) {
754-
if (name.isCompoundName() && name.getBaseName() == ctx.Id_enqueue) {
755-
auto argumentNames = name.getArgumentNames();
756-
return argumentNames.size() == 1 && argumentNames[0] == ctx.Id_partialTask;
757-
}
758-
759-
return false;
760-
}

lib/Sema/TypeCheckConcurrency.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ class ActorIsolation;
2323
class ASTContext;
2424
class ClassDecl;
2525
class DeclContext;
26-
class DeclName;
2726
class Expr;
2827
class FuncDecl;
2928
class ValueDecl;
@@ -38,9 +37,6 @@ void checkActorIsolation(const Expr *expr, const DeclContext *dc);
3837
/// Determine how the given value declaration is isolated.
3938
ActorIsolation getActorIsolation(ValueDecl *value);
4039

41-
/// Whether the given declaration name is enqueue(partialTask:).
42-
bool isEnqueuePartialTask(ASTContext &ctx, DeclName name);
43-
4440
} // end namespace swift
4541

4642
#endif /* SWIFT_SEMA_TYPECHECKCONCURRENCY_H */

lib/Sema/TypeCheckDecl.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2704,6 +2704,15 @@ SemanticMembersRequest::evaluate(Evaluator &evaluator,
27042704

27052705
for (auto *member : idc->getMembers()) {
27062706
if (auto *afd = dyn_cast<AbstractFunctionDecl>(member)) {
2707+
// If this is a witness to Actor.enqueue(partialTask:), put it at the
2708+
// beginning of the vtable.
2709+
if (auto func = dyn_cast<FuncDecl>(afd)) {
2710+
if (func->isActorEnqueuePartialTaskWitness()) {
2711+
result.insert(result.begin(), func);
2712+
continue;
2713+
}
2714+
}
2715+
27072716
// Add synthesized members to a side table and sort them by their mangled
27082717
// name, since they could have been added to the class in any order.
27092718
if (afd->isSynthesized()) {

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4219,10 +4219,12 @@ void ConformanceChecker::resolveValueWitnesses() {
42194219
auto &C = witness->getASTContext();
42204220

42214221
// Ensure that Actor.enqueue(partialTask:) is implemented within the
4222-
// class itself.
4223-
if (isEnqueuePartialTask(C, requirement->getName()) &&
4222+
// actor class itself.
4223+
if (FuncDecl::isEnqueuePartialTaskName(C, requirement->getName()) &&
42244224
Proto->isSpecificProtocol(KnownProtocolKind::Actor) &&
4225-
DC != witness->getDeclContext()) {
4225+
DC != witness->getDeclContext() &&
4226+
Adoptee->getClassOrBoundGenericClass() &&
4227+
Adoptee->getClassOrBoundGenericClass()->isActor()) {
42264228
witness->diagnose(diag::enqueue_partial_task_not_in_context, Adoptee);
42274229
return;
42284230
}

test/SILGen/synthesized_conformance_actor.swift

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,17 @@ public actor class A1<T: DefaultInit> {
1616

1717
extension Int: DefaultInit { }
1818

19+
public actor class A2 {
20+
func f() { }
21+
@actorIndependent public func enqueue(partialTask: PartialAsyncTask) { }
22+
}
23+
1924
func buildIt() {
2025
_ = A1<Int>()
2126
}
2227

23-
// variable initialization expression of A1.$__actor_storage
24-
// CHECK-LABEL: sil hidden [transparent] [ossa] @$s29synthesized_conformance_actor2A1C03$__C8_storage33_550E67F1F00BFF89F882603E5B70A41BLL12_Concurrency17_NativeActorQueueVvpfi : $@convention(thin) <T where T : DefaultInit> () -> @out _NativeActorQueue {
25-
// CHECK: bb0([[PROPERTY:%.*]] : $*_NativeActorQueue):
26-
// CHECK-NEXT: [[META:%.*]] = metatype $@thick A1<T>.Type
27-
// CHECK-NEXT: [[ERASED_META:%.*]] = init_existential_metatype [[META]] : $@thick A1<T>.Type, $@thick AnyObject.Type
28-
// CHECK: [[INIT_FN:%.*]] = function_ref @$s12_Concurrency24_defaultActorQueueCreateyAA07_NativecD0VyXlXpF : $@convention(thin) (@thick AnyObject.Type) -> @out _NativeActorQueue
29-
// CHECK-NEXT: = apply [[INIT_FN]]([[PROPERTY]], [[ERASED_META]]) : $@convention(thin) (@thick AnyObject.Type) -> @out _NativeActorQueue
30-
3128
// A1.enqueue(partialTask:)
32-
// CHECK-LABEL: sil hidden [ossa] @$s29synthesized_conformance_actor2A1C7enqueue11partialTasky12_Concurrency012PartialAsyncG0V_tF : $@convention(method) <T where T : DefaultInit> (@in_guaranteed PartialAsyncTask, @guaranteed A1<T>) -> () {
29+
// CHECK-LABEL: sil [ossa] @$s29synthesized_conformance_actor2A1C7enqueue11partialTasky12_Concurrency012PartialAsyncG0V_tF : $@convention(method) <T where T : DefaultInit> (@in_guaranteed PartialAsyncTask, @guaranteed A1<T>) -> () {
3330
// CHECK: bb0([[PARTIAL_TASK:%.*]] : $*PartialAsyncTask, [[SELF:%.*]] : @guaranteed $A1<T>):
3431
// CHECK: [[SELF_COPY:%.*]] = copy_value [[SELF]] : $A1<T>
3532
// CHECK-NEXT: [[SELF_ANY_OBJECT:%.*]] = init_existential_ref [[SELF_COPY]] : $A1<T> : $A1<T>, $AnyObject
@@ -39,3 +36,18 @@ func buildIt() {
3936
// CHECK: [[ENQUEUE_FN:%.*]] = function_ref @$s12_Concurrency36_defaultActorQueueEnqueuePartialTask5actor5queue07partialG0yyXl_AA07_NativecD0VzAA0f5AsyncG0VtF : $@convention(thin) (@guaranteed AnyObject, @inout _NativeActorQueue, @in_guaranteed PartialAsyncTask) -> ()
4037
// CHECK-NEXT: apply [[ENQUEUE_FN]]([[SELF_ANY_OBJECT]], [[DYNAMIC_ACCESS]], [[PARTIAL_TASK]]) : $@convention(thin) (@guaranteed AnyObject, @inout _NativeActorQueue, @in_guaranteed PartialAsyncTask) -> ()
4138
// CHECK-NEXT: end_access [[DYNAMIC_ACCESS]] : $*_NativeActorQueue
39+
40+
// variable initialization expression of A1.$__actor_storage
41+
// CHECK-LABEL: sil [transparent] [ossa] @$s29synthesized_conformance_actor2A1C03$__C8_storage33{{.*}}12_Concurrency17_NativeActorQueueVvpfi : $@convention(thin) <T where T : DefaultInit> () -> @out _NativeActorQueue {
42+
// CHECK: bb0([[PROPERTY:%.*]] : $*_NativeActorQueue):
43+
// CHECK-NEXT: [[META:%.*]] = metatype $@thick A1<T>.Type
44+
// CHECK-NEXT: [[ERASED_META:%.*]] = init_existential_metatype [[META]] : $@thick A1<T>.Type, $@thick AnyObject.Type
45+
// CHECK: [[INIT_FN:%.*]] = function_ref @$s12_Concurrency24_defaultActorQueueCreateyAA07_NativecD0VyXlXpF : $@convention(thin) (@thick AnyObject.Type) -> @out _NativeActorQueue
46+
// CHECK-NEXT: = apply [[INIT_FN]]([[PROPERTY]], [[ERASED_META]]) : $@convention(thin) (@thick AnyObject.Type) -> @out _NativeActorQueue
47+
48+
// Ensure that enqueue(partialTask:) is the first slot in the vtable.
49+
// CHECK-LABEL: sil_vtable [serialized] A1 {
50+
// CHECK-NEXT: #A1.enqueue: <T where T : DefaultInit> (A1<T>) -> (PartialAsyncTask) -> () : @$s29synthesized_conformance_actor2A1C7enqueue11partialTasky12_Concurrency012PartialAsyncG0V_tF
51+
52+
// CHECK-LABEL: sil_vtable [serialized] A2 {
53+
// CHECK-NEXT: #A2.enqueue: (A2) -> (PartialAsyncTask) -> () : @$s29synthesized_conformance_actor2A2C7enqueue11partialTasky12_Concurrency012PartialAsyncG0V_tF // A2.enqueue(partialTask:)

0 commit comments

Comments
 (0)