Skip to content

Commit 9c285c5

Browse files
committed
[SE-0316] Global actor qualification on a type implies Sendable.
1 parent d4bbb82 commit 9c285c5

File tree

3 files changed

+91
-29
lines changed

3 files changed

+91
-29
lines changed

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 74 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3502,6 +3502,20 @@ bool swift::checkSendableConformance(
35023502
return true;
35033503
}
35043504

3505+
// Global-actor-isolated types can be Sendable. We do not check the
3506+
// instance properties.
3507+
switch (getActorIsolation(nominal)) {
3508+
case ActorIsolation::Unspecified:
3509+
case ActorIsolation::ActorInstance:
3510+
case ActorIsolation::DistributedActorInstance:
3511+
case ActorIsolation::Independent:
3512+
break;
3513+
3514+
case ActorIsolation::GlobalActor:
3515+
case ActorIsolation::GlobalActorUnsafe:
3516+
return false;
3517+
}
3518+
35053519
if (classDecl) {
35063520
// An non-final class cannot conform to `Sendable`.
35073521
if (!classDecl->isSemanticallyFinal()) {
@@ -3536,23 +3550,16 @@ bool swift::checkSendableConformance(
35363550

35373551
NormalProtocolConformance *GetImplicitSendableRequest::evaluate(
35383552
Evaluator &evaluator, NominalTypeDecl *nominal) const {
3539-
// Only structs and enums can get implicit Sendable conformances.
3540-
if (!isa<StructDecl>(nominal) && !isa<EnumDecl>(nominal))
3553+
// Protocols never get implicit Sendable conformances.
3554+
if (isa<ProtocolDecl>(nominal))
35413555
return nullptr;
35423556

3543-
// Public, non-frozen structs and enums defined in Swift don't get implicit
3544-
// Sendable conformances.
3545-
if (!nominal->getASTContext().LangOpts.EnableInferPublicSendable &&
3546-
nominal->getFormalAccessScope(
3547-
/*useDC=*/nullptr,
3548-
/*treatUsableFromInlineAsPublic=*/true).isPublic() &&
3549-
!(nominal->hasClangNode() ||
3550-
nominal->getAttrs().hasAttribute<FixedLayoutAttr>() ||
3551-
nominal->getAttrs().hasAttribute<FrozenAttr>())) {
3557+
// Actor types are always Sendable; they don't get it via this path.
3558+
auto classDecl = dyn_cast<ClassDecl>(nominal);
3559+
if (classDecl && classDecl->isActor())
35523560
return nullptr;
3553-
}
35543561

3555-
// Check the context in which the conformance occurs.
3562+
// Check whether we can infer conformance at all.
35563563
if (auto *file = dyn_cast<FileUnit>(nominal->getModuleScopeContext())) {
35573564
switch (file->getKind()) {
35583565
case FileUnitKind::Source:
@@ -3586,23 +3593,65 @@ NormalProtocolConformance *GetImplicitSendableRequest::evaluate(
35863593
return nullptr;
35873594
}
35883595

3589-
// Check the instance storage for Sendable conformance.
3590-
if (checkSendableInstanceStorage(
3591-
nominal, nominal, SendableCheck::Implicit))
3596+
// Local function to form the implicit conformance.
3597+
auto formConformance = [&]() -> NormalProtocolConformance * {
3598+
ASTContext &ctx = nominal->getASTContext();
3599+
auto proto = ctx.getProtocol(KnownProtocolKind::Sendable);
3600+
if (!proto)
3601+
return nullptr;
3602+
3603+
auto conformance = ctx.getConformance(
3604+
nominal->getDeclaredInterfaceType(), proto, nominal->getLoc(),
3605+
nominal, ProtocolConformanceState::Complete);
3606+
conformance->setSourceKindAndImplyingConformance(
3607+
ConformanceEntryKind::Synthesized, nullptr);
3608+
3609+
return conformance;
3610+
};
3611+
3612+
// A non-protocol type with a global actor is implicitly Sendable.
3613+
if (nominal->getGlobalActorAttr()) {
3614+
// If this is a class, check the superclass. We won't infer Sendable
3615+
// if the superclass is already Sendable, to avoid introducing redundant
3616+
// conformances.
3617+
if (classDecl) {
3618+
if (Type superclass = classDecl->getSuperclass()) {
3619+
auto classModule = classDecl->getParentModule();
3620+
if (TypeChecker::conformsToKnownProtocol(
3621+
classDecl->mapTypeIntoContext(superclass),
3622+
KnownProtocolKind::Sendable,
3623+
classModule))
3624+
return nullptr;
3625+
}
3626+
}
3627+
3628+
// Form the implicit conformance to Sendable.
3629+
return formConformance();
3630+
}
3631+
3632+
// Only structs and enums can get implicit Sendable conformances by
3633+
// considering their instance data.
3634+
if (!isa<StructDecl>(nominal) && !isa<EnumDecl>(nominal))
35923635
return nullptr;
35933636

3594-
ASTContext &ctx = nominal->getASTContext();
3595-
auto proto = ctx.getProtocol(KnownProtocolKind::Sendable);
3596-
if (!proto)
3637+
// Public, non-frozen structs and enums defined in Swift don't get implicit
3638+
// Sendable conformances.
3639+
if (!nominal->getASTContext().LangOpts.EnableInferPublicSendable &&
3640+
nominal->getFormalAccessScope(
3641+
/*useDC=*/nullptr,
3642+
/*treatUsableFromInlineAsPublic=*/true).isPublic() &&
3643+
!(nominal->hasClangNode() ||
3644+
nominal->getAttrs().hasAttribute<FixedLayoutAttr>() ||
3645+
nominal->getAttrs().hasAttribute<FrozenAttr>())) {
35973646
return nullptr;
3647+
}
35983648

3599-
auto conformance = ctx.getConformance(
3600-
nominal->getDeclaredInterfaceType(), proto, nominal->getLoc(),
3601-
nominal, ProtocolConformanceState::Complete);
3602-
conformance->setSourceKindAndImplyingConformance(
3603-
ConformanceEntryKind::Synthesized, nullptr);
3649+
// Check the instance storage for Sendable conformance.
3650+
if (checkSendableInstanceStorage(
3651+
nominal, nominal, SendableCheck::Implicit))
3652+
return nullptr;
36043653

3605-
return conformance;
3654+
return formConformance();
36063655
}
36073656

36083657
AnyFunctionType *swift::applyGlobalActorType(

test/Concurrency/actor_isolation.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -798,12 +798,12 @@ class SomeClassWithInits {
798798
func hasDetached() {
799799
Task.detached {
800800
// okay
801-
await self.isolated() // expected-warning{{cannot use parameter 'self' with a non-sendable type 'SomeClassWithInits' from concurrently-executed code}}
802-
self.isolated() // expected-warning{{cannot use parameter 'self' with a non-sendable type 'SomeClassWithInits' from concurrently-executed code}}
801+
await self.isolated()
802+
self.isolated()
803803
// expected-error@-1{{expression is 'async' but is not marked with 'await'}}{{7-7=await }}
804804
// expected-note@-2{{calls to instance method 'isolated()' from outside of its actor context are implicitly asynchronous}}
805805

806-
print(await self.mutableState) // expected-warning{{cannot use parameter 'self' with a non-sendable type 'SomeClassWithInits' from concurrently-executed code}}
806+
print(await self.mutableState)
807807
}
808808
}
809809
}

test/Concurrency/concurrent_value_inference.swift

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,14 +85,27 @@ struct HasFunctions {
8585
var cfp: @convention(c) () -> Void
8686
}
8787

88+
@globalActor
89+
actor MyGlobalActor {
90+
static let shared = MyGlobalActor()
91+
}
92+
93+
@MyGlobalActor
94+
class C3 { }
95+
96+
class C4: C3 { }
97+
8898
func testCV(
89-
c1: C1, c2: C2, s1: S1, e1: E1, e2: E2, gs1: GS1<Int>, gs2: GS2<Int>,
99+
c1: C1, c2: C2, c3: C3, c4: C4, s1: S1, e1: E1, e2: E2,
100+
gs1: GS1<Int>, gs2: GS2<Int>,
90101
bc: Bitcode, ps: PublicStruct, pe: PublicEnum,
91102
fps: FrozenPublicStruct, fpe: FrozenPublicEnum,
92103
hf: HasFunctions
93104
) {
94105
acceptCV(c1) // expected-error{{'C1' conform to 'Sendable'}}
95106
acceptCV(c2)
107+
acceptCV(c3)
108+
acceptCV(c4)
96109
acceptCV(s1)
97110
acceptCV(e1) // expected-error{{'E1' conform to 'Sendable'}}
98111
acceptCV(e2)

0 commit comments

Comments
 (0)