Skip to content

Commit 20a540e

Browse files
committed
[SE-0316] Global actor qualification on a type implies Sendable.
1 parent e595b4f commit 20a540e

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
@@ -3552,6 +3552,20 @@ bool swift::checkSendableConformance(
35523552
return true;
35533553
}
35543554

3555+
// Global-actor-isolated types can be Sendable. We do not check the
3556+
// instance properties.
3557+
switch (getActorIsolation(nominal)) {
3558+
case ActorIsolation::Unspecified:
3559+
case ActorIsolation::ActorInstance:
3560+
case ActorIsolation::DistributedActorInstance:
3561+
case ActorIsolation::Independent:
3562+
break;
3563+
3564+
case ActorIsolation::GlobalActor:
3565+
case ActorIsolation::GlobalActorUnsafe:
3566+
return false;
3567+
}
3568+
35553569
if (classDecl) {
35563570
// An non-final class cannot conform to `Sendable`.
35573571
if (!classDecl->isSemanticallyFinal()) {
@@ -3586,23 +3600,16 @@ bool swift::checkSendableConformance(
35863600

35873601
NormalProtocolConformance *GetImplicitSendableRequest::evaluate(
35883602
Evaluator &evaluator, NominalTypeDecl *nominal) const {
3589-
// Only structs and enums can get implicit Sendable conformances.
3590-
if (!isa<StructDecl>(nominal) && !isa<EnumDecl>(nominal))
3603+
// Protocols never get implicit Sendable conformances.
3604+
if (isa<ProtocolDecl>(nominal))
35913605
return nullptr;
35923606

3593-
// Public, non-frozen structs and enums defined in Swift don't get implicit
3594-
// Sendable conformances.
3595-
if (!nominal->getASTContext().LangOpts.EnableInferPublicSendable &&
3596-
nominal->getFormalAccessScope(
3597-
/*useDC=*/nullptr,
3598-
/*treatUsableFromInlineAsPublic=*/true).isPublic() &&
3599-
!(nominal->hasClangNode() ||
3600-
nominal->getAttrs().hasAttribute<FixedLayoutAttr>() ||
3601-
nominal->getAttrs().hasAttribute<FrozenAttr>())) {
3607+
// Actor types are always Sendable; they don't get it via this path.
3608+
auto classDecl = dyn_cast<ClassDecl>(nominal);
3609+
if (classDecl && classDecl->isActor())
36023610
return nullptr;
3603-
}
36043611

3605-
// Check the context in which the conformance occurs.
3612+
// Check whether we can infer conformance at all.
36063613
if (auto *file = dyn_cast<FileUnit>(nominal->getModuleScopeContext())) {
36073614
switch (file->getKind()) {
36083615
case FileUnitKind::Source:
@@ -3636,23 +3643,65 @@ NormalProtocolConformance *GetImplicitSendableRequest::evaluate(
36363643
return nullptr;
36373644
}
36383645

3639-
// Check the instance storage for Sendable conformance.
3640-
if (checkSendableInstanceStorage(
3641-
nominal, nominal, SendableCheck::Implicit))
3646+
// Local function to form the implicit conformance.
3647+
auto formConformance = [&]() -> NormalProtocolConformance * {
3648+
ASTContext &ctx = nominal->getASTContext();
3649+
auto proto = ctx.getProtocol(KnownProtocolKind::Sendable);
3650+
if (!proto)
3651+
return nullptr;
3652+
3653+
auto conformance = ctx.getConformance(
3654+
nominal->getDeclaredInterfaceType(), proto, nominal->getLoc(),
3655+
nominal, ProtocolConformanceState::Complete);
3656+
conformance->setSourceKindAndImplyingConformance(
3657+
ConformanceEntryKind::Synthesized, nullptr);
3658+
3659+
return conformance;
3660+
};
3661+
3662+
// A non-protocol type with a global actor is implicitly Sendable.
3663+
if (nominal->getGlobalActorAttr()) {
3664+
// If this is a class, check the superclass. We won't infer Sendable
3665+
// if the superclass is already Sendable, to avoid introducing redundant
3666+
// conformances.
3667+
if (classDecl) {
3668+
if (Type superclass = classDecl->getSuperclass()) {
3669+
auto classModule = classDecl->getParentModule();
3670+
if (TypeChecker::conformsToKnownProtocol(
3671+
classDecl->mapTypeIntoContext(superclass),
3672+
KnownProtocolKind::Sendable,
3673+
classModule))
3674+
return nullptr;
3675+
}
3676+
}
3677+
3678+
// Form the implicit conformance to Sendable.
3679+
return formConformance();
3680+
}
3681+
3682+
// Only structs and enums can get implicit Sendable conformances by
3683+
// considering their instance data.
3684+
if (!isa<StructDecl>(nominal) && !isa<EnumDecl>(nominal))
36423685
return nullptr;
36433686

3644-
ASTContext &ctx = nominal->getASTContext();
3645-
auto proto = ctx.getProtocol(KnownProtocolKind::Sendable);
3646-
if (!proto)
3687+
// Public, non-frozen structs and enums defined in Swift don't get implicit
3688+
// Sendable conformances.
3689+
if (!nominal->getASTContext().LangOpts.EnableInferPublicSendable &&
3690+
nominal->getFormalAccessScope(
3691+
/*useDC=*/nullptr,
3692+
/*treatUsableFromInlineAsPublic=*/true).isPublic() &&
3693+
!(nominal->hasClangNode() ||
3694+
nominal->getAttrs().hasAttribute<FixedLayoutAttr>() ||
3695+
nominal->getAttrs().hasAttribute<FrozenAttr>())) {
36473696
return nullptr;
3697+
}
36483698

3649-
auto conformance = ctx.getConformance(
3650-
nominal->getDeclaredInterfaceType(), proto, nominal->getLoc(),
3651-
nominal, ProtocolConformanceState::Complete);
3652-
conformance->setSourceKindAndImplyingConformance(
3653-
ConformanceEntryKind::Synthesized, nullptr);
3699+
// Check the instance storage for Sendable conformance.
3700+
if (checkSendableInstanceStorage(
3701+
nominal, nominal, SendableCheck::Implicit))
3702+
return nullptr;
36543703

3655-
return conformance;
3704+
return formConformance();
36563705
}
36573706

36583707
AnyFunctionType *swift::applyGlobalActorType(

test/Concurrency/actor_isolation.swift

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

795-
print(await self.mutableState) // expected-warning{{cannot use parameter 'self' with a non-sendable type 'SomeClassWithInits' from concurrently-executed code}}
795+
print(await self.mutableState)
796796
}
797797
}
798798
}

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)